本节介绍 Spring Bean 生命周期,主要是 Spring Bean 的创建过程。
已知,Spring Bean 是由 Spring IoC Container 进行管理的对象。
因此 Spring Bean 的创建 / 销毁,以及各种状态,与 Spring 的实现有关。
Spring 从哪里获知需要管理的 Bean#
- XML 配置
- 注解,Spring 通过扫描指定的包,将其中带有 @Bean 注解的工厂方法当作 Bean
通过这些,将其视为 BeanDefinition 或者 Factory
Spring Bean 的创建和销毁时机#
Spring Bean 根据需要,可以自定义 Bean 的创建和销毁相关的时机。通常由 BeanDefinition 中的 Scope 进行指定。
- Singleton (默认,每个 Spring Application 应用一个)
- Prototype(多个)
- Request,每个 Http 请求一个
- Session,每个会话一个
- Application,(每个 SevletContext 一个,此处是指 SevletContext,对于像 Tomcat 这样的 Servlet 容器,可以存在多个 Spring Application,共用一个 ServletContext)
- Websocket,每个 websocket 连接一个
- 自定义
Spring 如何创建 Bean#
Spring 解析通过 XML / 注解方式声明的 Bean,生成对应的 BeanDefinition 对象。
在生成 BeanDefinition 对象之后,Spring 通过 BeanFactoryPostProcessor 对 BeanDefinition 进行微调。
再交给 BeanFactory 进行实例化,也就是通常的对象初始化(此处为了与 Spring 提供的 Hook 区分,使用别名实例化 表示 Java 对象的初始化),调用 Bean 的构造函数。如果此时构造函数依赖其他 Bean,则会取用对应 Bean 进行依赖注入。
随后对 Bean 的进行赋值各个属性进行赋值。如果存在依赖其他 Bean 的属性值,就获取对应的 Bean。这也就是依赖注入的过程
BeanPostProcessor 首次执行初始化前调整。
紧接着调用 Spring Bean 提供的 初始化 Hook,可以通过 InitializingBean 接口或是 JSR-250 的 @PostConstruct进行指定。
随后再次由 BeanPostProcessor 进行初始化后调整。以满足对 Bean 进行代理操作或是处理循环依赖的需要。
至此 Bean 即可作为正常的 Spring Bean 进行使用。
Spring Bean 的销毁#
当 Spring Context 销毁时,会将单例 Bean 进行销毁,此时会调用由 Spring 提供的销毁 Hook。可以通过 DisposableBean 接口或是 JSR-250 的 @PreDestory进行指定。
仅对单例 Bean 有效,因为对其他非单例 Bean 来说,Spring 只负责创建,后续的使用、控制、回收都是用户决定的。
![[attachments/spring bean lifecycle.png]]
Bean Full / Lite 模式#
对于包含在 @Configuration 中的 Bean。默认工作在 Full mode 下,在这种情况下, Spring 会自动处理 “Bean 间引用 / 依赖”
@Configuration
class AppConfig {
// 此处 fooServiceA() 内部对另外的 Bean fooRepository() 进行调用
@Bean
fun fooServiceA(): FooService {
return new FooService(fooRepository());
}
// 此处 fooServiceB() 内部对另外的 Bean fooRepository() 进行调用
@Bean
fun fooServiceB():FooService {
return new FooService(fooRepository());
}
@Bean
fun fooRepository():JdbcFooRepository {
return JdbcFooRepository();
}
}
在这种情况下,假设 fooRepository 在其他地方已被实例化,当 fooServiceA/fooServiceB 内部调用 fooRepository 时,Spring 会拦截对 fooRepository 的调用,直接返回已实例化的JdbcFooRepository,而非再次执行 fooRepository()。
相对的,还存在一个 Lite 模式。
对于不包含在 @Configuration 中的 Bean,默认使用 Lite 模式。
Lite 模式下的 Bean 方法将被容器视为普通工厂方法(类似于 XML 中的 factory-method 声明),并正确应用作用域和生命周期回调。在这种情况下,包含类保持不变,并且包含类或工厂方法没有异常约束。
与包含在 由@Configuration标注的类中的 Bean 方法的语义相反,Lite 模式下不支持 "bean 间引用"。因此,当一个 @Bean 方法在 Lite 模式下调用另一个 @Bean 方法时,该调用是标准的 Java 方法调用; Spring 不会通过 CGLIB 代理拦截调用。这类似于代理模式下 @Transactional 方法间的调用,Spring 不会拦截调用 ——Spring 仅在 AspectJ 模式下这样做。