参阅5.3源码
最近这段时间,各种喜羊羊,美羊羊出现,也确实许久未外出跑动了。就算外出也只是买菜,拿下快递。就算外出,也不敢跑,也怕羊羊,于是家里蹲。还望大家注意防护,增强抵抗力。废话有点多,文章如下:
根据Bean的生命周期
Bean生成有几个重要步骤
1.实例化得到对象
(扫描ClassPathBeanDefinitionScanner#doScan)
2.填充属性(AbstractAutowireCapableBeanFactory#populateBean)
3.初始化前(BeanPostProcessor#postProcessBeforeInitialization)
4.初始化
5.初始化后
(BeanPostProcessor#postProcessAfterInitialization)
6.放入单例池(singletonObjects)
简单描述过程
- 实例化TeacherService->放入三级缓存singletonFactories(原生对象或代理对象)
- 填充StudentService属性->从单例池->没有需要创建
- 实例化StudentService->放入三级缓存
- 填充TeacherService->单例池->TeacherService创建中出现循环->earlySingletonObjects->执行singletonFactories的Object,放入二级缓存earlySingletonObjects且移除三级缓存。
循环依赖
就是循环引用即两个或两个以上的Bean相互引用.
以及时序图:
sequenceDiagram
TeacherService->>StudentService: 上课啦
StudentService->>TeacherService: 听讲
源码关键点
- 添加三级缓存 ;有三个前提条件必须是单例,支持循环依赖且正在创建(在集合中@(singletonsCurrentlyInCreation)
故而:原型,构造方法是不能解决循环依赖。
//判断单例,支持循环依赖且正在创建(Set<String> )
// TODO 循环依赖-添加到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
- 根据Bean生命周期,AOP是在初始化后调用
出现循环依赖,会在初始化对象的时候,放入singletonFatory->lamdba getEarlyBeanReference,该方法中提前进行AOP(AbstractAutoProxyCreator)操作。
//TODO 提前代理引用
private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
解决循环依赖的三个缓存
主要源码(DefaultSingletonBeanRegistry)下:
//TODO 一级缓存,完整生命周期的Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//TODO 三级缓存,主要打破循环依赖的关键
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
//TODO 保证AOP或原始对象是单例(早期的Bean对象,未经过完整生命周期)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
开始打破关键代码块
选取AbstractAutowireCapableBeanFactory#doCreateBean关键部分
// TODO 为了解决循环依赖提前缓存单例创建工厂
// TODO 判断单例,支持循坏引用且当前正在创建
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// TODO 循环依赖-添加到三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//目的很清楚,主要就是为了往singletonFatories存放对象,lamdba表达式在初始化之后运行,获取原生对象或代理对象
/**
* TODO 关键代码,注释也使相当清楚
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
注意:循环依赖,使用AOP
如果在当前方法中增加Async注解方法,则会报错。
后置方法中执行AOP的逻辑,返回的是Bean而非代理对象;因为cacheKey在循环依赖,就在earlyProxyReference集合中存在
异常原因就在于bean在Bean初始化后置方法中(AOP)被修改了;则exposedObject!=bean导致错误。
解决方法
TeacherService @Lazy注解的方式,在Bean生命周期过程中提前进行AOP返回代理对象。 还有一种方案,就是根据Service创建过程根据排序规则(ASCII排序),StudentService会先执行。TeacherService后执行,将方法移入TeacherService中也可以解决。