Spring--Bean的生命周期和循环依赖
Bean的生命周期和循环依赖
- Bean 的生命周期了解么?
- Spring中的循环引用
- 什么是循环引用?
- 三级缓存解决循环依赖
- 总结
- 构造方法出现了循环依赖怎么解决?
Bean 的生命周期了解么?
- 整体上可以简单分为四步:实例化 —> 属性赋值 —> 初始化 —> 销毁。
- 初始化这一步涉及到的步骤比较多,包含
Aware
接口的依赖注入、BeanPostProcessor
在初始化前后的处理以及InitializingBean
和init-method
的初始化操作。 - 销毁这一步会注册相关销毁回调接口,最后通过
DisposableBean
和destory-method
进行销毁。
详细如下:
- 创建 Bean 的实例:Bean 容器首先会找到配置文件中的 Bean 定义,然后使用 Java 反射 API 来创建 Bean 的实例。
- Bean 属性赋值/填充:为 Bean 设置相关属性和依赖,例如
@Autowired
等注解注入的对象、@Value
注入的值、setter
方法或构造函数注入依赖和值、@Resource
注入的各种资源。 - Bean 初始化:
- 如果 Bean 实现了
BeanNameAware
接口,调用setBeanName()
方法,传入 Bean 的名字。 - 如果 Bean 实现了
BeanClassLoaderAware
接口,调用setBeanClassLoader()
方法,传入ClassLoader
对象的实例。 - 如果 Bean 实现了
BeanFactoryAware
接口,调用setBeanFactory()
方法,传入BeanFactory
对象的实例。 - 与上面的类似,如果实现了其他
*.Aware
接口,就调用相应的方法。 - 如果有和加载这个 Bean 的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessBeforeInitialization()
方法 - 如果 Bean 实现了
InitializingBean
接口,执行afterPropertiesSet()
方法。 - 如果 Bean 在配置文件中的定义包含
init-method
属性,执行指定的方法。 - 如果有和加载这个 Bean 的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessAfterInitialization()
方法。
- 销毁 Bean:销毁并不是说要立马把 Bean 给销毁掉,而是把 Bean 的销毁方法先记录下来,将来需要销毁 Bean 或者销毁容器的时候,就调用这些方法去释放 Bean 所持有的资源。
- 如果 Bean 实现了
DisposableBean
接口,执行destroy()
方法。 - 如果 Bean 在配置文件中的定义包含
destroy-method
属性,执行指定的 Bean 销毁方法。或者,也可以直接通过@PreDestroy
注解标记 Bean 销毁之前执行的方法。
AbstractAutowireCapableBeanFactory
的 doCreateBean()
方法中能看到依次执行了这 4 个阶段:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 1. 创建 Bean 的实例
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object exposedObject = bean;
try {
// 2. Bean 属性赋值/填充
populateBean(beanName, mbd, instanceWrapper);
// 3. Bean 初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
// 4. 销毁 Bean-注册回调接口
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
return exposedObject;
}
Spring中的循环引用
什么是循环引用?
在创建A对象的同时需要使用的B对象,在创建B对象的同时需要使用到A对象‘
三级缓存解决循环依赖
Spring解决循环依赖是通过三级缓存,对应的三级缓存如下所示:
缓存名称 | 源码名称 | 作用 |
---|---|---|
一级缓存 | singletonObjects | 单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象 |
二级缓存 | earlySingletonObjects | 缓存早期的bean对象(生命周期还没走完) |
三级缓存 | singletonFactories | 缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的 |
一级缓存作用:限制bean在beanFactory中只存一份,即实现singleton scope,解决不了循环依赖
如果要想打破循环依赖, 就需要一个中间人的参与, 这个中间人就是二级缓存。
但是如果是代理对象,则需要三级缓存。
总结
循环依赖:循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A
循环依赖在spring中是允许存在,spring框架依据三级缓存已经解决了大部分的循环依赖
①一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象
②二级缓存:缓存早期的bean对象(生命周期还没走完)
③三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的
构造方法出现了循环依赖怎么解决?
A依赖于B,B依赖于A,注入的方式是构造函数
原因:由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构造函数的的依赖注入
解决方案:使用@Lazy进行懒加载,什么时候需要对象再进行bean对象的创建