1. 依赖注入(DI)和控制反转(IoC)
1. 控制反转(IoC)
控制反转是一种设计原则,它通过反转传统的控制流程,使得对象的创建和依赖关系的管理从应用程序代码中抽象出来。传统的编程方式中,对象通常会直接创建它们所依赖的其他对象,而 IoC 则将这种责任转移给一个外部容器(如 Spring 容器)。
在 IoC 中,应用程序的控制权被“反转”到容器中,容器负责创建对象并管理它们的生命周期。这种设计方式使得应用程序更加模块化,易于扩展和测试。
2. 依赖注入(DI)
依赖注入是实现控制反转的一种具体方式。通过依赖注入,依赖的对象(称为依赖项)不再由使用它的对象(称为客户端)创建,而是由 IoC 容器自动提供。
在 Spring 中,依赖注入可以通过以下几种方式实现:
1. 构造函数注入:
- 通过构造函数传入依赖项。
public class UserService {private final UserRepository userRepository;// 通过构造函数注入public UserService(UserRepository userRepository) {this.userRepository = userRepository;}
}
2. Setter 注入:
- 通过 setter 方法注入依赖项。
public class UserService {private UserRepository userRepository;// 通过 setter 方法注入public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}
}
3. 字段注入(推荐使用,适合小型应用):
- 使用注解直接在字段上注入依赖项。
public class UserService {@Autowiredprivate UserRepository userRepository; // 通过字段注入
}
3. 总结
- 控制反转(IoC) 是一种设计模式,将对象的创建和管理交给容器。
- 依赖注入(DI) 是 IoC 的一种实现方式,通过注入的方式提供对象之间的依赖关系。
这两种模式使得 Spring 应用程序更加模块化、可测试性更强,并降低了代码之间的耦合度。
4. 容器
在 Spring 中,容器(Container)通常是指 Spring IoC 容器(Inversion of Control 容器),它是整个 Spring 框架的核心部分。Spring IoC 容器负责创建、管理和注入应用程序中的对象(即 Bean),并处理对象之间的依赖关系。
Spring IoC 容器的主要职责是通过配置文件(如 XML、注解、Java 配置类)来管理 Bean 的生命周期、依赖注入、AOP 和事务等功能。
1. Spring 容器的定义
Spring 容器主要是基于 ApplicationContext 接口的实现类,如 ClassPathXmlApplicationContext、AnnotationConfigApplicationContext 等,它们是 Spring IoC 容器的实现。容器本质上是一个持有 Bean 定义和 Bean 实例的核心组件。
核心接口:
- BeanFactory: 是 Spring 容器最基础的接口,提供对 Bean 的懒加载支持。BeanFactory 提供了一种低级容器,用于管理 Bean 实例的创建和依赖注入。
- ApplicationContext: ApplicationContext 扩展了 BeanFactory,提供更高级的特性,如事件发布、国际化和自动 Bean 的配置。
- 它们都是 Spring 容器,BeanFactory 只能管理单例 Bean 的生命周期。它不能管理非单例 Bean 的生命周期。这是因为原型 Bean 实例被创建之后便被传给了客户端,容器失去了对它们的引用。
- BeanFactorty 无法支持 spring 的 aop 功能和 web 应用。而 ApplicationContext 接口作为 BeanFactory 的派生,因而提供 BeanFactory 所有的功能。而且 ApplicationContext 还在功能上做了扩展。
- BeanFactroy 采用的是延迟加载形式来注入 Bean 的,即只有在使用到某个 Bean 时(调用 getBean()),才对该 Bean 进行加载实例化,
- ApplicationContext 则相反,它是在容器启动时,一次性创建了所有的 Bean。
- beanFactory 主要是面对与 spring 框架的基础设施,面对 spring 自己。而 Applicationcontext 主要面对与 spring 使用的开发者。基本都会使用 Applicationcontext 并非 beanFactory 。
常见的 ApplicationContext 实现类:
- ClassPathXmlApplicationContext: 从类路径中的 XML 文件加载 Bean 定义。
- FileSystemXmlApplicationContext: 从文件系统中的 XML 文件加载 Bean 定义。
- AnnotationConfigApplicationContext: 使用注解配置的 Java 类来加载 Bean 定义。
2. Spring 容器中的数据结构
Spring 容器内部使用了一些数据结构来管理 Bean 实例和它们之间的依赖关系。因为 Spring 是用 Java 实现的,所以底层的数据结构依赖于 Java 的集合框架,如 Map、Set、List 等,Spring 使用这些数据结构来存储和管理 Bean 的定义和实例。以下是 Spring 容器中使用的数据结构的主要组成部分:
Bean 定义的存储结构:
- Map<String, BeanDefinition>: Spring 使用 Map 来存储 Bean 定义,其中 String 是 Bean 的名称,BeanDefinition 包含了有关 Bean 的所有元数据,如类名、作用域(singleton、prototype 等)、初始化和销毁方法、依赖关系等。
// Bean 名称与 Bean 定义的映射
Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
单例 Bean 实例的存储结构:
- Map<String, Object>: Spring 容器会缓存所有的 singleton 作用域的 Bean 实例。在单例作用域中,每个 Bean 只会实例化一次,并存储在一个 Map 中。Map 的键是 Bean 的名称,值是对应的 Bean 实例。
// Bean 名称与单例 Bean 实例的映射
Map<String, Object> singletonObjects = new HashMap<>();
早期 Bean 引用的存储结构(三级缓存):
为了防止循环依赖,Spring 使用了三级缓存(三级 Map 结构)来管理 Bean 实例的初始化过程。
- 一级缓存:存储完全初始化好的单例 Bean 实例。
- 二级缓存:存储提前暴露的 Bean 实例(已实例化,但尚未完成依赖注入和初始化)。
- 三级缓存:存储 Bean 工厂,用于解决循环依赖时的提前暴露。
// 一级缓存:完全初始化好的单例 Bean 实例
Map<String, Object> singletonObjects = new HashMap<>();// 二级缓存:提前暴露的 Bean 实例
Map<String, Object> earlySingletonObjects = new HashMap<>();// 三级缓存:Bean 工厂,用于创建 Bean 实例的 FactoryBean
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
依赖关系存储结构:
- Map<String, Set>: Spring 使用 Map 来存储 Bean 的依赖关系,其中 Map 的键是 Bean 的名称,值是它依赖的其他 Bean 的名称集合。这有助于管理 Bean 的依赖关系,尤其是在处理依赖注入时。
// Bean 名称与其依赖的 Bean 名称集合映射
Map<String, Set<String>> dependentBeanMap = new HashMap<>();
3. Spring 容器的工作原理
当 Spring 容器启动时,它会执行以下操作:
- 解析配置:容器根据 XML 文件、Java 配置类或注解扫描来加载 Bean 定义(BeanDefinition)。
- 存储 Bean 定义:将解析的 Bean 定义存储到一个 Map(如 beanDefinitionMap)中,以便后续使用。
- 实例化 Bean:当第一次请求某个 Bean 时,容器会从 beanDefinitionMap 中查找其定义,实例化该 Bean 并注入依赖。
- 管理 Bean 生命周期:容器管理 Bean 的创建、初始化、依赖注入和销毁。
4. 总结
- Spring 容器是负责管理对象生命周期、依赖注入和横切关注点的核心组件。
- Spring 容器内部使用诸如 Map、Set、List 等数据结构来存储 Bean 定义、实例和依赖关系。
- 三级缓存是容器中用于解决循环依赖的重要机制。
Spring 容器是通过这些核心数据结构来组织和管理应用程序中的 Bean 以及它们之间的关系的。
2. AOP
Spring AOP(面向切面编程)是一种用于处理横切关注点的编程范式。横切关注点是指在应用程序多个地方都需要处理的功能,例如日志记录、安全性、事务管理等。通过 AOP,开发者可以将这些关注点与业务逻辑分离,从而提高代码的可重用性和可维护性。
1. AOP 的核心概念
- 切面(Aspect):切面是横切关注点的模块化实现,它包含了具体的功能逻辑。
- 连接点(Join Point):程序执行的某个点,通常是方法的调用。例如,方法调用、异常抛出等。每当程序执行到某个特定点时,Spring AOP 就会考虑在该点上应用相关的通知。例如:类 UserService 的 getUserById 方法的调用就是一个连接点。
public class UserService {public User getUserById(Long id) {// 方法实现}
}
- 通知(Advice):在连接点上执行的操作,有多种类型:
- 前置通知(Before):在目标方法执行之前执行。
- 后置通知(After):在目标方法执行之后执行(无论是正常结束还是异常结束)。
- 返回通知(After Returning):在目标方法正常返回之后执行。
- 异常通知(After Throwing):在目标方法抛出异常时执行。
- 环绕通知(Around):包裹目标方法的调用,可以在方法调用前后添加逻辑。
- 切入点(Pointcut):用于定义在哪些连接点上应用通知。可以基于方法名称、参数、注解等进行匹配。切入点是连接点的集合,表示在哪些连接点上执行通知。例如:假设想要在 UserService 类的所有公共方法上添加日志记录,可以定义一个切入点来匹配这个条件。
@Aspect
public class LoggingAspect {// 切入点:匹配 UserService 类中所有公共方法@Pointcut("execution(public * com.example.service.UserService.*(..))")public void userServiceMethods() {}// 使用前置通知@Before("userServiceMethods()")public void logBefore() {System.out.println("A method in UserService is about to be called.");}
}
在这个示例中,userServiceMethods 切入点定义了一个规则,匹配 com.example.service.UserService 类中的所有公共方法。@Before 通知则在这个切入点匹配的连接点上执行。
2. AOP 的实现方式
Spring AOP 主要有两种实现方式:
- 基于代理的 AOP:
- JDK 动态代理:适用于实现了接口的类,使用 Java 的反射机制在运行时创建代理对象。
- CGLIB 代理:适用于没有实现接口的类,通过生成子类来实现代理。
- AspectJ:
- 是一个功能更强大的 AOP 框架,支持编译时和加载时织入。Spring 可以与 AspectJ 集成,但通常使用 Spring 的内置 AOP 进行快速开发。
3. AOP 的使用
1. 创建切面
@Aspect
@Component
public class LoggingAspect {// 前置通知,拦截所有 public 方法@Before("execution(public * com.example.service.*.*(..))")public void logBefore() {System.out.println("A method is about to be called.");}
}
2. 启用 AOP
@SpringBootApplication
@EnableAspectJAutoProxy
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}
3. AOP 的优点
- 分离关注点:将横切关注点与业务逻辑分离,提高了代码的可维护性。
- 可重用性:切面可以在多个地方重用,减少重复代码。
- 灵活性:可以在不修改业务逻辑的情况下添加或修改横切逻辑。
4. AOP 失效问题
在 Spring 中,尽管 AOP 非常强大,但在某些情况下,它可能会失效。以下是一些常见的情形:
1. 直接调用自身的方法
当在同一个类中直接调用带有 AOP 注解(如 @Transactional 或 @Log)的方法时,这种调用不会通过代理拦截,因此 AOP 相关的逻辑不会执行。
示例:
@Service
public class MyService {@Transactionalpublic void outerMethod() {innerMethod(); // 直接调用,不会触发 AOP}public void innerMethod() {// 这里的事务不会生效}
}
2. 方法的访问修饰符
AOP 仅适用于 public 方法。对于 protected 或 private 方法,Spring 的 AOP 代理无法拦截这些方法,因此 AOP 不会生效。
示例:
@Service
public class MyService {@Transactionalprotected void transactionalMethod() {// 事务管理不会生效}public void publicMethod() {transactionalMethod(); // 直接调用,事务传播失效}
}
3. 代理类型问题
Spring AOP 使用 JDK 动态代理和 CGLIB 代理。JDK 动态代理只能代理实现了接口的类,如果目标类没有实现接口,Spring 将使用 CGLIB 代理。这可能导致预期的 AOP 行为失效。
- 解决办法:确保你的类实现接口,或者手动配置 CGLIB 代理。
4. 没有配置 AOP 相关依赖
如果未在项目中引入 AOP 相关的依赖,AOP 功能将无法使用。在使用 Spring Boot 时,确保引入了 spring-boot-starter-aop 依赖。
5. Bean 不是 Spring 管理的
AOP 仅适用于 Spring 管理的 Bean。如果在非 Spring 管理的类中使用 AOP 注解,AOP 将失效。
示例:
@Aspect
@Component
public class LoggingAspect {// 定义一个切入点,拦截所有名为 'doSomething' 的方法@Before("execution(* com.example.service.MyService.doSomething(..))")public void logBeforeMethod() {System.out.println("AOP: Method doSomething() is called.");}
}
@Service
public class MyService {public void doSomething() {System.out.println("Executing doSomething() in MyService.");}
}
当通过 手动实例化 MyService 类,而不是让 Spring 管理时,AOP 将不会生效:
@SpringBootApplication
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);// 手动实例化 MyService,非 Spring 管理的 BeanMyService myService = new MyService();myService.doSomething(); // AOP 不会生效}
}
输出:
Executing doSomething() in MyService.
在这个例子中,虽然 MyService 类的方法 doSomething() 被调用了,但由于手动实例化了 MyService,而不是通过 Spring 容器获取它的实例,AOP 切面(LoggingAspect)没有生效,拦截器也没有触发。
6. AOP 配置不正确
AOP 的切点表达式配置错误或切入点未正确匹配,可能导致 AOP 失效。确保正确配置了切点表达式。
7. 多线程环境中的问题
如果在多线程环境中,AOP 可能会因线程上下文的切换而失效,特别是当使用 ThreadLocal 变量时。
3. 设计模式
Spring 框架中运用了多种设计模式,以增强其灵活性、可维护性和可扩展性。以下是一些常见的设计模式及其在 Spring 中的应用:
1. 单例模式(Singleton Pattern)
- 描述:确保一个类只有一个实例,并提供一个全局访问点。
- 应用:Spring 默认情况下会以单例模式创建 Bean,确保每个 Bean 只存在一个实例,避免资源浪费。
2. 工厂模式(Factory Pattern)
- 描述:定义一个接口用于创建对象,但由子类决定实例化哪个类。
- 应用:Spring 的 BeanFactory 和 ApplicationContext 实现了工厂模式,负责创建和管理 Bean 的实例。
3. 观察者模式(Observer Pattern)
- 描述:定义对象之间的一种一对多的依赖关系,当一个对象改变状态时,所有依赖它的对象都会收到通知并自动更新。
- 应用:Spring 事件机制使用观察者模式,允许组件之间通过事件进行松耦合通信。
4. 装饰者模式(Decorator Pattern)
- 描述:动态地给一个对象添加一些额外的职责。
- 应用:在 Spring AOP 中,代理对象(Decorator)在运行时动态地增强目标对象的功能。
5. 适配器模式(Adapter Pattern)
- 描述:将一个类的接口转换成客户端所期望的另一个接口。
- 应用:Spring 的 HandlerAdapter 允许不同类型的请求处理器兼容处理请求。
6. 策略模式(Strategy Pattern)
- 描述:定义一系列算法,并将每一个算法封装起来,使它们可以互相替换。
- 应用:Spring 的 TransactionManager 可以通过策略模式实现不同的事务管理。
7. 责任链模式(Chain of Responsibility Pattern)
- 描述:通过将请求沿着处理链传递,直到找到一个合适的处理者。
- 应用:Spring 的 Filter 和 Interceptor 可以实现责任链模式,允许多个过滤器处理请求。
8. 模板方法模式(Template Method Pattern)
- 描述:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。
- 应用:Spring 的 JdbcTemplate 和 HibernateTemplate 提供了模板方法,简化数据库操作的步骤。
9. 代理模式(Proxy Pattern)
- 描述:为其他对象提供一种代理以控制对这个对象的访问。
- 应用:在 Spring AOP 中,代理用于实现横切关注点(如事务管理和安全性)。
总结
Spring 框架结合了多种设计模式,通过灵活的架构和设计,使得开发者能够构建出高效、可维护和可扩展的应用程序。这些设计模式为开发者提供了良好的开发实践和解决方案。
4. bean生命周期
在 Spring 框架中,Bean 的生命周期包括从创建到销毁的各个阶段。理解 Bean 生命周期有助于更好地管理 Spring 应用程序中的资源。以下是 Spring Bean 生命周期的主要步骤:
1. 实例化(Instantiation)
- Spring 容器通过反射创建 Bean 的实例。此时,Bean 的构造函数被调用。
2. 属性填充(Populate Properties)
- Spring 容器使用依赖注入(DI)将配置文件中定义的属性注入到 Bean 中。这些属性可以是基本数据类型或其他 Bean。
3. Bean 的初始回调(Bean Post-Processing)
- 在这个阶段,Spring 允许你在 Bean 实例化和初始化之后修改 Bean。实现 BeanPostProcessor 接口的类会在这个阶段的两个方法中执行:
- postProcessBeforeInitialization(Object bean, String beanName)
- postProcessAfterInitialization(Object bean, String beanName)
4. 初始化(Initialization)
- 如果 Bean 实现了 InitializingBean 接口,Spring 会调用其 afterPropertiesSet() 方法。
- 另外,如果在 Bean 的定义中配置了 init-method,Spring 也会调用指定的方法。
5. 使用 Bean(Using the Bean)
- Bean 现在可以被应用程序中的其他组件使用。
6. 销毁(Destruction)
- 当 Spring 容器关闭或不再需要 Bean 时,会调用 Bean 的销毁方法。
- 如果 Bean 实现了 DisposableBean 接口,Spring 会调用其 destroy() 方法。
- 同时,如果在 Bean 的定义中配置了 destroy-method,Spring 也会调用指定的方法。
7. 总结
Bean 的生命周期过程如下:
- 实例化
- 属性填充
- Bean 后处理(BeanPostProcessor)
- 初始化
- 使用 Bean
- 销毁
图示化 Bean 生命周期
+---------------------+| Instantiate Bean |+---------------------+|v+---------------------+| Populate Properties |+---------------------+|v+---------------------+| Post-Process (Before Initialization) |+---------------------+|v+---------------------+| Initialize Bean |+---------------------+|v+---------------------+| Post-Process (After Initialization) |+---------------------+|v+---------------------+| Use Bean |+---------------------+|v+---------------------+| Destroy Bean |+---------------------+
通过理解 Bean 的生命周期,开发者可以更有效地控制资源管理、实现初始化逻辑以及进行清理工作。
8. 重要的生命周期方法
在 Spring 中,有几种方法可以重载 Bean 的生命周期钩子(Hook):
- @PostConstruct 和 @PreDestroy 注解
- @PostConstruct:在 Bean 完成依赖注入后(即 Bean 已经创建并完成所有属性的设置),Spring 会调用这个方法。这是初始化 Bean 时非常常用的注解。
- @PreDestroy:在容器销毁 Bean 之前调用。可以用来释放资源、关闭连接等。
示例:
@Component
public class MyBean {@PostConstructpublic void init() {System.out.println("Bean is going through init.");}@PreDestroypublic void destroy() {System.out.println("Bean will destroy now.");}
}
在这个例子中,当 Bean 完成初始化时,init() 方法会被调用。当 Bean 被销毁时,destroy() 方法会被调用。
- 实现 InitializingBean 和 DisposableBean 接口
- afterPropertiesSet():这个方法来自 InitializingBean 接口,在 Bean 的所有属性设置好之后调用。相当于初始化方法。
- destroy():这个方法来自 DisposableBean 接口,在容器销毁该 Bean 时调用。
示例:
@Component
public class MyLifecycleBean implements InitializingBean, DisposableBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Bean has been initialized.");}@Overridepublic void destroy() throws Exception {System.out.println("Bean is about to be destroyed.");}
}
这两种方法为开发者提供了在初始化和销毁阶段自定义逻辑的机会。
- 通过 @Bean 注解配置 initMethod 和 destroyMethod
- 如果使用 Java 配置来定义 Bean,可以通过 @Bean 注解来指定初始化和销毁方法。
示例:
@Configuration
public class AppConfig {@Bean(initMethod = "customInit", destroyMethod = "customDestroy")public MyBean myBean() {return new MyBean();}
}class MyBean {public void customInit() {System.out.println("Custom init method.");}public void customDestroy() {System.out.println("Custom destroy method.");}
}
在这个例子中,customInit() 会在 Bean 初始化时被调用,而 customDestroy() 会在 Bean 销毁时被调用。
- 使用 XML 中的 init-method 和 destroy-method 配置
- 如果使用的是 XML 配置方式,可以通过 init-method 和 destroy-method 属性来指定初始化和销毁的方法。
示例:
<bean id="myBean" class="com.example.MyBean" init-method="customInit" destroy-method="customDestroy" />
MyBean 类:
public class MyBean {public void customInit() {System.out.println("Custom init method.");}public void customDestroy() {System.out.println("Custom destroy method.");}
}
- BeanPostProcessor 接口
- BeanPostProcessor 是 Spring 提供的接口,用于在 Bean 初始化前后执行一些自定义逻辑。它主要有两个方法:
- postProcessBeforeInitialization():在初始化之前执行。
- postProcessAfterInitialization():在初始化之后执行。
- BeanPostProcessor 是 Spring 提供的接口,用于在 Bean 初始化前后执行一些自定义逻辑。它主要有两个方法:
示例:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("Before Initialization: " + beanName);return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("After Initialization: " + beanName);return bean;}
}
1. Bean 生命周期中的方法触发顺序
在 Spring 中,Bean 生命周期通常包括以下几个关键步骤:
- 实例化:Spring 容器创建 Bean 实例。
- 依赖注入:Spring 注入所需的依赖。
- BeanPostProcessor 的 postProcessBeforeInitialization() 方法被调用。
- 初始化:
- 执行 @PostConstruct 方法。
- 调用 InitializingBean.afterPropertiesSet() 方法。
- 调用自定义的 init-method。
- BeanPostProcessor 的 postProcessAfterInitialization() 方法被调用。
- Bean 可用:此时 Bean 可以被应用程序使用。
- 销毁:
- 容器关闭时,执行 @PreDestroy 方法。
- 调用 DisposableBean.destroy() 方法。
- 调用自定义的 destroy-method。
5. springboot
Spring Boot 是构建在 Spring 框架之上的项目,它通过简化配置和内置的开发工具,帮助开发者快速创建基于 Spring 的应用。Spring Boot 的主要目的是减少样板代码和配置,使得开发者能够专注于业务逻辑。
它的主要启动逻辑都源自于它的主启动类注解上:@SpringBootApplication,其中包含了三个注解来实现以下功能:
- @Configuration 注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。
- @ComponentScan 的功能其实就是自动扫描并加载符合条件的组件,最终将这些bean定义加载到IoC容器中。
- @EnableAutoConfiguration 将所有符合自动配置条件的bean定义加载到IoC容器,自动配置好tomcat、springmvc、web常见功能,初始化容器、应用监听器。
6. @Autowired 和 @Resource
在 Spring 中,@Autowired 和 @Resource 是两个常用于依赖注入的注解,它们都可以用于将 Bean 注入到类中,但有一些显著的区别。主要区别体现在注解的来源、依赖注入的方式以及自动装配的优先级等方面。
1. 注解的来源
- @Autowired:
- 来源于 Spring 框架,位于 org.springframework.beans.factory.annotation 包下。
- 是 Spring 专有的注解,主要用于自动注入依赖。
- @Resource:
- 来源于 JDK 中的 JSR-250 规范,位于 javax.annotation 包下。
- 是 Java EE 标准中的一部分,因此它具有更广泛的兼容性,可以在非 Spring 的依赖注入容器中使用(如 Java EE 容器)。
2. 注入方式
- @Autowired:
- 默认情况下,@Autowired 是按类型(by type)进行自动装配的。如果容器中有多个同类型的 Bean,Spring 会尝试根据字段名称来选择一个合适的 Bean(按名称匹配)。
- 如果找到了多个匹配的 Bean,可以使用 @Qualifier 注解来明确指定注入的 Bean。
- @Autowired 可以用于字段、构造器和 setter 方法。
@Autowired
private UserService userService; // 按类型注入
如果有多个 UserService 实例,可以通过 @Qualifier 指定具体的 Bean:
@Autowired
@Qualifier("specificUserService")
private UserService userService;
- @Resource:
- @Resource 默认是按名称(by name)进行装配。如果没有找到匹配的名称,它会再尝试按类型匹配。
- 可以通过 name 属性指定要注入的 Bean 名称。如果不指定 name,则默认使用字段名作为 Bean 名称进行匹配。
- @Resource 仅支持用于字段和 setter 方法,不支持构造器注入。
@Resource
private UserService userService; // 按名称注入
指定名称注入:
@Resource(name = "specificUserService")
private UserService userService;
3. 自动装配的优先级
- @Autowired:
- 如果容器中找不到匹配的 Bean,会抛出 NoSuchBeanDefinitionException,除非将 required 属性设置为 false,这样 Spring 就会允许该 Bean 为 null。
@Autowired(required = false)
private UserService userService; // 如果找不到 Bean,userService 会是 null
- @Resource:
- 如果按名称或按类型找不到匹配的 Bean,容器会抛出 NoSuchBeanDefinitionException。
- @Resource 没有类似 required 的属性,所以不支持将其设置为可选的依赖注入。
4. 多重匹配时的行为
- @Autowired:
- 如果存在多个类型相同的 Bean 而没有使用 @Qualifier 指定具体的 Bean,Spring 会抛出异常。
- @Resource:
- @Resource 首先按照名称查找,如果找到名称匹配的 Bean 就注入该 Bean;如果没有找到名称匹配的 Bean,它会按照类型查找。如果按类型查找到了多个 Bean,会抛出异常。
5. 使用场景
- @Autowired:
- 常用于 Spring 应用中,更加灵活,尤其是在需要按类型注入或使用 @Qualifier 注解指定特定 Bean 时。
- 它是 Spring 框架的核心注解之一,特别适合需要依赖注入的 Spring 项目。
- @Resource:
- 适用于需要遵循 Java EE 规范的项目,尤其是在与其他依赖注入容器(如 Java EE 容器)兼容时。
- 适合需要按名称注入 Bean 的场景,尤其是项目中使用明确命名的 Bean 时。
6. 注解的属性
- @Autowired:
- 默认按类型注入。
- 可选属性:
- required: 是否必须注入(默认为 true)。
- @Resource:
- 默认按名称注入。
- 可选属性:
- name: 指定注入的 Bean 名称。
- type: 指定注入的 Bean 类型(很少用)。
实例对比
假设有两个 UserService 实现类:
@Service("userService1")
public class UserServiceImpl1 implements UserService {}@Service("userService2")
public class UserServiceImpl2 implements UserService {}
- 使用 @Autowired(按类型匹配,需要 @Qualifier 来区分):
@Autowired
@Qualifier("userService1")
private UserService userService; // 按类型注入并指定具体 Bean
- 使用 @Resource(按名称匹配):
@Resource(name = "userService1")
private UserService userService; // 按名称注入
在 Spring 项目中,@Autowired 更加常用且灵活,而 @Resource 在需要兼容其他标准时更合适。
7. 事务
Spring 框架提供了丰富的事务管理功能,其中事务的传播行为(Transaction Propagation Behavior)定义了当一个事务方法调用另一个事务方法时,如何处理这些方法之间的事务关系。Spring 通过 @Transactional 注解可以很方便地管理事务,且支持多种传播行为。
1. Spring 的事务传播行为
Spring 事务管理有以下几种常见的传播行为:
- REQUIRED(默认):如果当前没有事务,就新建一个事务;如果已经存在一个事务,则加入当前事务。这是默认的传播行为。
- REQUIRES_NEW:无论当前是否有事务,都新建一个事务。当前事务如果存在,则挂起当前事务。
- SUPPORTS:支持当前事务,如果存在事务就加入当前事务;如果当前没有事务,就以非事务方式执行。
- NOT_SUPPORTED:不支持事务,如果存在事务,则将其挂起,以非事务方式运行。
- MANDATORY:必须在事务中运行,如果当前没有事务则抛出异常。
- NEVER:不能在事务中运行,如果当前存在事务,则抛出异常。
- NESTED:如果当前有事务,则嵌套事务运行。如果没有事务,就新建一个事务。
通过这些传播行为,Spring 提供了灵活的事务管理策略,开发者可以根据实际需求选择合适的传播方式。
2. 代码示例
假设我们有两个服务类 OrderService 和 PaymentService,它们分别处理订单和支付逻辑。下面通过代码举例说明每种事务传播行为。
@Service
public class OrderService {@Autowiredprivate PaymentService paymentService;// 1. REQUIRED 传播行为示例@Transactional(propagation = Propagation.REQUIRED)public void createOrder() {System.out.println("Creating order...");// 调用支付服务方法,这里会加入当前事务paymentService.processPayment();// 抛出异常,事务回滚throw new RuntimeException("Order creation failed!");}// 2. REQUIRES_NEW 传播行为示例@Transactional(propagation = Propagation.REQUIRES_NEW)public void createOrderNewTransaction() {System.out.println("Creating order in new transaction...");paymentService.processPaymentNewTransaction();throw new RuntimeException("Order creation failed in new transaction!");}// 3. SUPPORTS 传播行为示例@Transactional(propagation = Propagation.SUPPORTS)public void createOrderSupports() {System.out.println("Creating order with SUPPORTS propagation...");paymentService.processPaymentSupports();}
}
@Service
public class PaymentService {// 1. REQUIRED:默认传播行为,加入到现有事务中@Transactional(propagation = Propagation.REQUIRED)public void processPayment() {System.out.println("Processing payment with REQUIRED propagation...");}// 2. REQUIRES_NEW:新建事务@Transactional(propagation = Propagation.REQUIRES_NEW)public void processPaymentNewTransaction() {System.out.println("Processing payment in a new transaction with REQUIRES_NEW...");}// 3. SUPPORTS:支持现有事务,但没有事务则以非事务方式执行@Transactional(propagation = Propagation.SUPPORTS)public void processPaymentSupports() {System.out.println("Processing payment with SUPPORTS propagation...");}
}
3. 各种传播行为的解释
1. REQUIRED(默认行为)
如果 OrderService.createOrder() 方法调用了 PaymentService.processPayment() 方法,而 createOrder() 开启了事务,则 processPayment() 会加入到同一个事务中。如果 createOrder() 发生异常并回滚事务,那么 processPayment() 的事务也会被回滚。
orderService.createOrder();
输出:
Creating order...
Processing payment with REQUIRED propagation...
// 抛出异常,回滚事务
在这种情况下,由于 OrderService.createOrder() 方法抛出了异常,整个事务(包括 processPayment())都会回滚。
2. REQUIRES_NEW
如果 OrderService.createOrderNewTransaction() 调用了 PaymentService.processPaymentNewTransaction(),Spring 会为 processPaymentNewTransaction() 新建一个事务,当前事务(createOrderNewTransaction())则会被挂起。即使 createOrderNewTransaction() 抛出异常并回滚,processPaymentNewTransaction() 的事务也不会受到影响。
orderService.createOrderNewTransaction();
输出:
Creating order in new transaction...
Processing payment in a new transaction with REQUIRES_NEW...
// 抛出异常,但 processPaymentNewTransaction() 的事务不会回滚
在这种情况下,processPaymentNewTransaction() 会独立提交,即使 createOrderNewTransaction() 回滚了事务。
3. SUPPORTS
OrderService.createOrderSupports() 方法使用 SUPPORTS 传播行为,意味着它会根据调用上下文来决定是否使用事务。如果 createOrderSupports() 是在事务中被调用的,它就会加入事务;如果没有事务,它就会以非事务方式执行。
orderService.createOrderSupports();
输出:
Creating order with SUPPORTS propagation...
Processing payment with SUPPORTS propagation...
在这个例子中,如果 createOrderSupports() 是在没有事务的上下文中调用的,processPaymentSupports() 也会以非事务方式执行。否则,它将加入现有事务。
4. 其他传播行为
4. NOT_SUPPORTED
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void someMethod() {// 无事务运行
}
NOT_SUPPORTED 表示该方法不需要事务。如果当前有事务存在,Spring 会将其挂起。
5. MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void someMethod() {// 必须在事务内运行,否则抛出异常
}
MANDATORY 表示方法必须在事务中执行。如果没有事务,会抛出 TransactionRequiredException。
6. NEVER
@Transactional(propagation = Propagation.NEVER)
public void someMethod() {// 必须在没有事务的情况下运行
}
NEVER 表示该方法不能在事务中运行。如果存在事务,会抛出异常。
7. NESTED
@Transactional(propagation = Propagation.NESTED)
public void someMethod() {// 嵌套事务
}
NESTED 传播行为允许当前事务内部创建一个子事务。如果外部事务回滚,嵌套事务也会回滚;但嵌套事务失败不会导致外部事务回滚。
5. 事务失效问题
在 Spring 中,事务传播机制用于控制一个事务如何在不同方法之间传播或继承。当事务管理得当时,Spring 的声明式事务管理非常强大,但在某些特定情况下,事务传播可能会失效。这通常是由于使用不当或对 Spring 事务管理机制的误解所导致。
1. 同类内部方法调用(Self-Invocation)
Spring 的事务是基于 AOP 代理实现的,AOP 代理只能拦截外部调用。如果一个类中的一个方法调用了同类中的另一个标记了事务注解的方法,Spring 无法通过代理拦截内部调用,这样事务不会生效。
示例代码:
@Service
public class MyService {@Transactionalpublic void outerMethod() {// 调用内部方法,这个内部调用不会触发事务innerMethod();}@Transactionalpublic void innerMethod() {// 期望事务生效,但不会生效// some transactional operation}
}
- 原因:outerMethod() 和 innerMethod() 在同一个类中,当 outerMethod() 调用 innerMethod() 时,它是类内部调用,而不是通过 Spring AOP 代理触发的,因此 innerMethod() 的事务管理不会生效。
- 解决方法:
- 可以将 innerMethod() 移到另一个类中,确保通过外部调用触发 AOP 代理。
- 使用 AopContext.currentProxy() 进行自调用代理,例如:((MyService) AopContext.currentProxy()).innerMethod();
2. @Transactional 注解标记在私有方法或静态方法上
Spring 的 AOP 代理无法拦截私有方法或静态方法,因此如果你在这些方法上使用 @Transactional,事务不会生效。
示例代码:
@Service
public class MyService {@Transactionalprivate void privateMethod() {// 事务不会生效}@Transactionalpublic static void staticMethod() {// 事务不会生效}
}
- 原因:Spring AOP 只能拦截公共的方法,因此 @Transactional 注解标记的私有方法、受保护的方法或静态方法无法被代理,事务不会生效。
- 解决方法:
- 需要将 @Transactional 注解应用于公共的方法。
3. 方法未被 Spring 管理(没有代理)
如果某个类的方法没有被 Spring 容器管理(即没有通过 Spring 的 AOP 代理),事务管理将失效。这通常发生在以下情况:
- 类没有使用 Spring 的注解(如 @Component, @Service 等)注册为 Bean。
- 使用 new 关键字创建对象,而不是通过 Spring 容器获取 Bean。
public class MyService {@Transactional(propagation = Propagation.REQUIRED)public void transactionalMethod() {// 事务不会生效,因为类没有被 Spring 管理}
}public class Main {public static void main(String[] args) {MyService myService = new MyService(); // 手动创建对象,事务不会生效myService.transactionalMethod(); // 事务失效}
}
4. 事务方法抛出非 RuntimeException 或自定义异常
默认情况下,Spring 的事务管理机制只会在方法抛出 RuntimeException(非受检异常) 或 Error 时回滚。如果事务方法抛出了一个 Checked Exception(受检异常),默认情况下事务不会回滚。
示例:
@Service
public class MyService {@Transactionalpublic void transactionalMethod() throws IOException {throw new IOException("Checked Exception"); // 不会触发事务回滚}
}
在这个例子中,IOException 是一个受检异常,Spring 不会自动回滚事务。可以通过设置 rollbackFor 属性来指定哪些异常应该导致事务回滚:
@Transactional(rollbackFor = IOException.class)
public void transactionalMethod() throws IOException {throw new IOException("Checked Exception"); // 现在会回滚
}
5. 事务管理器配置错误或者是没有开启事务管理器
如果没有在 Spring 配置中启用事务管理,即使标注了 @Transactional 注解,事务管理也不会生效。常见的启用事务管理的方式有以下两种:
- 注解配置:在配置类中加上 @EnableTransactionManagement。
@Configuration
@EnableTransactionManagement
public class AppConfig {// 配置内容
}
在 Spring Boot 中,默认情况下,Spring 会自动配置事务管理器。为了确保开启事务管理,可以使用 @EnableTransactionManagement 注解。不过,通常这一步是可选的,因为 Spring Boot 会在检测到 spring-boot-starter-data-jpa 或其他相关依赖时自动配置事务管理器。
- XML 配置:在 Spring XML 配置文件中启用事务管理:
<tx:annotation-driven />
6. 事务方法在同一线程中嵌套调用
即使事务传播机制配置正确,如果多个事务方法在同一个线程中相互调用,也可能会出现问题。通常这种情况发生在嵌套事务或异步调用场景中。
7. 事务传播策略不正确
某些事务传播行为可能不会启动新的事务。例如:
- Propagation.SUPPORTS:如果调用方有事务,则加入该事务;如果没有,则不使用事务。
- Propagation.NOT_SUPPORTED:以非事务方式执行。
如果使用这些传播行为,可能导致方法执行时没有事务。
示例:
@Transactional(propagation = Propagation.SUPPORTS)
public void noTransactionIfNoneExists() {// 如果调用此方法的上层没有事务,则此方法不在事务中执行
}
8. 嵌套事务与非事务方法组合调用
如果一个非事务方法调用了一个事务方法,事务注解可能失效。因为非事务方法不会开启事务,事务传播不会应用到被调用的方法。
示例:
@Service
public class MyService {public void nonTransactionalMethod() {transactionalMethod(); // 事务注解可能会失效}@Transactionalpublic void transactionalMethod() {// 事务应该生效的地方}
}
由于 nonTransactionalMethod() 是非事务的,调用 transactionalMethod() 时,事务不会生效。
9. 不同的线程
当不同线程之间进行事务操作时,事务传播会失效。Spring 的事务管理通常依赖于当前线程的上下文,如果事务在一个线程中开始,而在另一个线程中执行,则会失去事务上下文。
示例:
@Service
public class MyService {@Transactionalpublic void outerMethod() {// 开始事务new Thread(() -> innerMethod()).start();}@Transactionalpublic void innerMethod() {// 这里的事务将不会与 outerMethod 的事务关联}
}
10. 数据库事务支持不够
某些数据源或数据库可能不完全支持事务,或者对于特定的操作(例如某些查询)不支持事务。这种情况下,事务传播可能失效。例如:mysql 中的 myisam 是不支持事务的。
11. AOP 代理问题
如果在 AOP 代理的上下文中调用了自己(例如通过 this 调用),则事务不会被应用,因为 Spring AOP 是基于代理的,内部方法调用将跳过事务管理。
示例:
@Service
public class MyService {@Transactionalpublic void outerMethod() {innerMethod(); // 直接调用将不会被事务管理}public void innerMethod() {// 这里的事务不会与 outerMethod 的事务关联}
}
8. 循环依赖问题
在 Spring 中,循环依赖(circular dependency)是指两个或多个 Bean 之间互相依赖,形成依赖循环。例如,BeanA 依赖 BeanB,而 BeanB 又依赖 BeanA。这种情况如果处理不当,会导致 Bean 初始化失败。Spring 提供了多种机制来解决循环依赖问题,特别是在单例(Singleton)Bean 情况下。
1. Spring 如何检测和处理循环依赖
在 Spring 中,Bean 的创建和依赖注入分为两个阶段:
- 实例化:创建 Bean 的实例,分配内存,但不进行依赖注入。
- 依赖注入:完成 Bean 的属性注入。
为了防止循环依赖导致的无限递归,Spring 会在 Bean 实例化后将其放入一个 “一级缓存”(singletonObjects,用于存放完全初始化的 Bean)和 “二级缓存”(earlySingletonObjects,用于存放部分初始化的 Bean)。通过这种机制,Spring 可以在某些情况下提前暴露 Bean,从而解决循环依赖问题。
2. Spring 解决单例循环依赖的机制
对于单例 Bean,Spring 通过缓存机制来解决循环依赖问题。具体的机制如下:
- 一级缓存(singletonObjects):存放完全初始化完成的单例 Bean。
- 二级缓存(earlySingletonObjects):存放刚刚实例化但尚未完成依赖注入的 Bean。Spring 在遇到循环依赖时,允许其他 Bean 引用这个部分初始化的 Bean。
- 三级缓存(singletonFactories):存放提前暴露的 Bean 工厂(ObjectFactory)。这是用来支持 AOP 代理或其他增强的场景。
当 Spring 发现一个 Bean 的依赖 Bean 还没有完全初始化时,它会将该 Bean 提前暴露到二级缓存中,这样另一个 Bean 在注入依赖时就可以引用它,避免了循环依赖的问题。
代码示例
@Component
public class BeanA {@Autowiredprivate BeanB beanB;public BeanA() {System.out.println("BeanA is created");}
}@Component
public class BeanB {@Autowiredprivate BeanA beanA;public BeanB() {System.out.println("BeanB is created");}
}
- BeanA 依赖 BeanB,BeanB 依赖 BeanA,形成了循环依赖。
- Spring 在创建 BeanA 时会发现它依赖 BeanB,于是会去创建 BeanB。
- 但是在创建 BeanB 时发现它依赖 BeanA,而此时 BeanA 尚未完全初始化。
- Spring 会将部分完成的 BeanA 实例放到二级缓存中,让 BeanB 可以引用它,避免无限递归。
3. 在不同 Bean 作用域中的处理
- 单例(Singleton)作用域:Spring 能够通过上述缓存机制解决单例作用域下的循环依赖。
- 原型(Prototype)作用域:Spring 不会为原型作用域的 Bean 提供缓存,也不管理它们的生命周期。因此,原型作用域的 Bean 无法通过上述机制解决循环依赖问题。如果存在原型作用域的循环依赖,Spring 会抛出 BeanCurrentlyInCreationException。
4. 常见解决方案
对于更复杂的循环依赖场景,有时需要使用一些手动干预的方式来解决。
1. 构造函数注入避免循环依赖
如果你使用的是 构造函数注入 而不是 字段或 setter 注入,Spring 会在创建时检查循环依赖。如果发现循环依赖,Spring 会立即抛出异常,因此构造函数注入不能像字段注入那样自动解决循环依赖。
解决方法之一是拆分逻辑,避免构造函数注入时的循环依赖。
2. 使用 @Lazy 延迟加载
示例代码
// OrderService 类
@Component
public class OrderService {private final UserService userService;// 构造函数注入,通过 @Lazy 延迟加载 UserService@Autowiredpublic OrderService(@Lazy UserService userService) {this.userService = userService;System.out.println("OrderService is created");}public void processOrder() {System.out.println("Processing order...");userService.getUserDetails();}
}// UserService 类
@Component
public class UserService {private final OrderService orderService;// 构造函数注入 OrderService@Autowiredpublic UserService(OrderService orderService) {this.orderService = orderService;System.out.println("UserService is created");}public void getUserDetails() {System.out.println("Fetching user details from UserService...");}
}
测试代码
@SpringBootApplication
public class SpringLazyConstructorInjectionExample {public static void main(String[] args) {SpringApplication.run(SpringLazyConstructorInjectionExample.class, args);}// CommandLineRunner 用于应用启动后执行代码@Beanpublic CommandLineRunner run(ApplicationContext context) {return args -> {// 获取 OrderService Bean 并调用其方法OrderService orderService = context.getBean(OrderService.class);orderService.processOrder();};}
}
运行结果:
OrderService is created
Processing order...
UserService is created
Fetching user details from UserService...
解释:
- OrderService is created:首先,OrderService 被创建,因为在测试中我们主动调用了 OrderService 的方法。
- 延迟加载 UserService:由于我们在 OrderService 的构造函数中使用了 @Lazy 注解,UserService 并没有立即被实例化,而是在 orderService.processOrder() 调用时才被加载。
- UserService is created:在 OrderService 调用 userService.getUserDetails() 方法时,UserService 才被初始化。
- 最后,UserService 执行了它的逻辑,输出用户信息。
通过 @Lazy 延迟加载,Spring 可以解决构造函数注入中的循环依赖问题。它确保了在依赖被真正需要时才进行初始化,避免了 Bean 在创建时的循环依赖导致的递归问题。
3. 重构代码
有时候,循环依赖是设计问题的征兆。可以通过重构代码来拆分相互依赖的逻辑。例如,将共同的依赖移入第三个类,从而避免直接的循环依赖。
4. 手动注入(ApplicationContextAware)
可以实现 ApplicationContextAware 接口来手动获取 Bean。这种方法较为繁琐,但在复杂场景下可能有用。
@Component
public class BeanA implements ApplicationContextAware {private ApplicationContext applicationContext;private BeanB beanB;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;this.beanB = applicationContext.getBean(BeanB.class);}public BeanA() {System.out.println("BeanA is created");}
}
这种方法可以手动管理 Bean 的获取,避免循环依赖。
5. 通过 Setter 注入
在字段注入或构造函数注入中,循环依赖比较难以处理,但通过 Setter 注入可以推迟依赖的实际赋值时间,从而避免问题。
@Component
public class BeanA {private BeanB beanB;@Autowiredpublic void setBeanB(BeanB beanB) {this.beanB = beanB;}public BeanA() {System.out.println("BeanA is created");}
}@Component
public class BeanB {private BeanA beanA;@Autowiredpublic void setBeanA(BeanA beanA) {this.beanA = beanA;}public BeanB() {System.out.println("BeanB is created");}
}
通过 Setter 方法注入,可以让 BeanA 和 BeanB 在彼此初始化完成后再设置依赖。
5. 总结
- 单例(Singleton)Bean 的循环依赖:Spring 通过三级缓存机制(一级缓存、二级缓存、三级缓存)解决单例 Bean 的循环依赖。
- 原型(Prototype)Bean 的循环依赖:Spring 无法自动解决,需手动处理或重构代码。
- 解决循环依赖的方案:
- 使用 @Lazy 延迟加载。
- 使用 Setter 注入推迟依赖初始化。
- 手动管理依赖(如 ApplicationContextAware)。
- 重构代码设计,减少循环依赖。
9. bean 的作用域
singleton:单例,整个Spring容器中只有一个Bean实例。
prototype:原型,每次获取Bean都会创建一个新的实例。
request:每个HTTP请求都会创建一个Bean实例。
session:每个HTTP会话都会创建一个Bean实例。
global-session:每个全局HTTP会话都会创建一个Bean实例。
在 Spring 中,Bean 的作用域(Scope)定义了容器如何创建和返回 Bean 实例。Spring 提供了多种作用域,允许根据应用程序的需求来配置 Bean 的生命周期和可见性。常见的作用域有 singleton、prototype,以及一些与 Web 环境相关的作用域,如 request、session 和 application。
1. singleton(单例,默认)
- 描述:在 Spring 容器中,singleton 作用域的 Bean 只会被创建一次。每次请求该 Bean 时,Spring 都返回同一个实例。无论有多少次依赖注入或调用,它都返回该单例实例。
- 适用场景:对于无状态的 Bean 或大多数共享的服务类,singleton 作用域通常是默认选择。
示例:
@Component
@Scope("singleton") // 默认是 singleton,可以省略
public class MySingletonBean {// 单例 Bean
}
2. prototype(原型)
- 描述:每次请求 Bean 时,Spring 都会创建一个新的实例。在 prototype 作用域下,Bean 不共享,相互独立。
- 适用场景:适用于有状态的 Bean 或者需要频繁创建和销毁实例的场景。
示例:
@Component
@Scope("prototype")
public class MyPrototypeBean {// 每次请求都会生成一个新的实例
}
3. request(仅适用于 Web 应用)
- 描述:在 Web 应用程序中,request 作用域的 Bean 在每个 HTTP 请求中创建一个新的实例,并在请求结束后销毁。
- 适用场景:用于处理与单个 HTTP 请求生命周期相关的数据或操作。
示例:
@Component
@Scope("request")
public class MyRequestBean {// 该 Bean 每个请求生成一个新实例
}
4. session(仅适用于 Web 应用)
- 描述:session 作用域的 Bean 在每个 HTTP 会话(Session)中创建一个实例,并在会话结束时销毁。它与用户会话相关。
- 适用场景:用于与用户会话相关联的 Bean,通常用于保存用户数据。
示例:
@Component
@Scope("session")
public class MySessionBean {// 该 Bean 每个 HTTP Session 生成一个新实例
}
5. application(仅适用于 Web 应用)
- 描述:application 作用域的 Bean 在整个 ServletContext(即整个 Web 应用)中只创建一个实例,生命周期与 Web 应用的生命周期相同。
- 适用场景:用于跨整个 Web 应用共享的 Bean。
示例:
@Component
@Scope("application")
public class MyApplicationBean {// 整个 Web 应用中只创建一个实例
}
6. 自定义作用域
除了上述内置的作用域,Spring 还允许自定义作用域。你可以通过实现 org.springframework.beans.factory.config.Scope 接口来创建自己的作用域,并将其注册到 Spring 容器中。
7. 配置方式
可以通过两种方式为 Bean 配置作用域:
- 注解方式:使用 @Scope 注解在 Java 类上。
- XML 配置:在 Spring XML 配置文件中指定作用域。
示例:
<bean id="myBean" class="com.example.MyBean" scope="prototype"/>
8. 总结
- Spring 提供了多种作用域,可以根据需求选择合适的作用域来管理 Bean 的生命周期。
- 默认作用域是 singleton,这意味着 Bean 是共享的。对于特定需求,如需要多个实例,可以使用 prototype 或其他 Web 相关的作用域(如 request、session 等)。