什么是循环依赖?
就是字面意思,AService依赖BService,BService依赖AService。
如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情。
但是在spring中,循环依赖就是一个问题了,因为一个对象并不是简单 new出来的,而是会经过一系列bean的生命周期,所以会出现循环依赖。有些循环spring可以帮我们解决,有些依赖需要程序员自己解决。我们先说前者,循环依赖的从生命周期说起。
Bean的生命周期
这里只会描述一下生命周期的大概过程:
- Spring扫描class得到BeanDefinition,根据class推断构造方法,然后根据beanDefinition和构造方法,实例化得到原始bean;
- 填充原始对象中的属性(依赖注⼊)
- 如果原始对象中的某个⽅法被AOP了,那么则需要根据原始对象⽣成⼀个代理对象
- 把最终⽣成的代理对象放⼊单例池(源码中叫做singletonObjects)中,下次getBean时就直接从单例
池拿即可
属性填充,其实就是依赖注入,所以出现循环依赖,主要也是在第二步。
详解循环依赖
示例:
public class BService{
@Autowired
private AService aservice;
}
public class AService{
@Autowired
private BService bservice;
}
解决循环依赖,需要三级缓存
一级:singletonObject map<beanName,Object> 单例池 保证对象单例
二级:earlySingletonObject map<beanName,Object> 保证对象单例 也是对象 只不过是一个不完整的对象
三级:singletonFactories Map<String, ObjectFactory<?>> 原始对象池
step one:
AService的Bean的生命周期
1、class->实例化得到AService的原始对象
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean
BService的生命周期
2.1、class->实例化得到BService的原始对象
2.2、给AService属性赋值->从单例池找AService->找不到->又去创建AService对象=========这样就又返回去了,形成了死循环
2.3、给其他属性赋值
2.4、其余事情
2.5、将对象放入单例池
3、给其他属性赋值
4、其余事情(aop等)
5、将对象放入单例池
在2.2步的时候,AService创建时—>需要BService—>BService创建时—>需要A,这样就产生了死循环。
在1步实例化后得到的对象,下文都称为 原始对象。
step two:使用第三级缓存,解决初步问题。
AService的Bean的生命周期
1、class->实例化得到AService的原始对象->放入三级缓存map<beanName,AService原始对象>
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean
BService的生命周期
2.1、class->实例化得到BService的原始对象->放入三级缓存map<beanName,BService原始对象>
2.2、给AService属性赋值->从单例池找AService->找不到->从A三级缓存map->找到AService原始对象
2.3、给其他属性赋值
2.4、其余事情(aop等)
2.5、将对象放入单例池
3、给其他属性赋值
4、其余事情(aop等)
5、将对象放入单例池
通过一个 A原始对象缓存map 就可以解决问题 得以生命周期进行完;
但是依然还有问题,如果AService的方法配置了AOP,那么AOP会在第四步进行,aop后得到一个 代理对象 放入了单例池;但是在BService生命周期中,是赋值的AService的原始对象,那这样,就不一致了;
step three:解决对象一致问题–利用aop提前进行
如果AService配置了切面,那么在2.2步应该赋值一个代理对象。。。
但是在2.2步如何得到一个代理对象呢?是否可以提前进行aop?貌似可以。。。
1、class->实例化得到AService的原始对象-> 如何检测? -> 提前进行aop -> 得到AService的代理对象-> 放入三级缓存map<beanName,AService代理对象>
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean
BService的生命周期
2.1、class->实例化得到BService的原始对象->放入三级缓存map<beanName,BService原始对象>
2.2、给AService属性赋值->从单例池找AService->找不到->从A三级缓存map获取对象->得到AService代理对象
2.3、给其他属性赋值
2.4、其余事情
2.5、将对象放入单例池
但是 提前进行aop是有前提的,前提就是只有AService出现了循环依赖的情况下,才可以提前,正常都是第四步才aop,如何检测出现了循环依赖呢?
step four:如何检测 AService 出现了循环依赖呢?
如果在1处判断,会费老鼻子劲,其实在2.2步会很容易。
解决方法:在生命周期第一步之前 新加一步 加一个标志,既可以处理了;
0、creatingSet.add('aService')
1、class->实例化得到AService的原始对象
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean
BService的生命周期
2.1、class->实例化得到BService的原始对象
2.2、给AService属性赋值->从单例池找AService->找不到->creatingSet?->AService出现了循环依赖-> 提前AOP -> 生成AService代理对象
…………
3、给其他属性赋值
4、其余事情(aop等)
5、将对象放入单例池
6、creatingSet.remove('aService')
这样还是有问题的,如果aService还注入了cService,那么在第2步,还需要进行cService的生命周期,到2.2的时候,还会进行一次aService的aop,生成两个AService的代理对象,这样就出问题了,因为是单例bean,应该就一个。
step five:二级缓存–如果防止出现多个bean
这时候就引入了 第二级 缓存 earlySingletonObject map<beanName,Object>
0、creatingSet.add('aService')
1、class->实例化得到AService的原始对象
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean
BService的生命周期
2.1、class->实例化得到BService的原始对象
2.2、给AService属性赋值->从单例池找AService->找不到->creatingSet是否有值->AService出现了循环依赖-> 从earlySingletonObject找AService -> 找不到 -> 提前AOP -> 生成AService代理对象(不完整)->放入earlySingletonObject
……………………
CService的生命周期
2.1、class->实例化得到CService的原始对象
2.2、给AService属性赋值->从单例池找AService->找不到->creatingSet?->AService出现了循环依赖-> 从earlySingletonObject找AService -> 得到AService代理对象(不完整)
……………………
3、给其他属性赋值
4、其余事情
4.5、从earlySingletonObject获取代理对象
5、将对象放入单例池
这样就保证了 AService代理对象(不完整) 只有一个,解决了 会出现 多个代理对象的问题 。
而且第四步、第五步之间增加一步,直接从earlySingletonObject获取代理对象,这时候获取到的就是经历了整个生命周期的bean, 然后放入代理池
step six:三级缓存的正确使用
在进行aop之前,需要用到第三季缓存,需要从里边拿到原始对象,以及beanName和beanDefinition
我们知道代理对象中一个target属性,target属性就指向原始对象
三级缓存 Map<String, ObjectFactory<?>> singletonFactories value实际上 是一个lambda表达式 lambda(beanName,beanDefiniton,bean)
0、creatingSet.add('aService')
1、class->实例化得到AService的原始对象->放入三级缓存singletonFactories
2、给BService属性赋值->从单例池找BService->找不到->创建BService的bean
BService的生命周期
2.1、class->实例化得到BService的原始对象-> 第三级缓存map
2.2、给AService属性赋值->从单例池找AService->找不到->creatingSet?->AService出现了循环依赖-> earlySingletonObject -> 找不到 -> 提前AOP(aop需要拿到aService的原始对象,就需要从三级缓存singletonFactories 拿) -> 生成AService代理对象(不完整)--放入二级缓存earlySinletonObject
step seven :如何标记aop提前进行
提前进行了aop,第四步就不用进行aop了,那么如何判断 进行过aop了呢?
两个关键方法:
- getEarlyBeanReference(): 第二步aop 执行lambda表达式的时候 调用
- postProcessAfterInitialzation():第四步的时候调用 初始化的方法
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
//cacheKey 其实就是 bean的名字
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//将cacheKey存入earlyProxyReferences,也是一个ConcurrentHashMap 就用这个map来判断是否进行过aop
this.earlyProxyReferences.put(cacheKey, bean);
//然后进行aop,得到一个代理对象,方法里的createProxy,就是aop的入口
return wrapIfNecessary(bean, beanName, cacheKey);
}
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//earlyProxyReferences中存的是哪些提前进行了aop的bean,beanName aop之前的对象
//注意 earlyProxyReferences 中并没有存aop之后的代理对象
//就用这个map来判断是否进行过aop,如果没有提前进行aop,调remove方法得到一个null,null肯定不等object
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//没有提前进行aop,则进行aop
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
总结
到此,由于属性注入引起的循环依赖,spring解决原理就结束了,我们发现,spring在解决循环依赖的时候,不仅仅是靠三级缓存,还有两个东西。
creatingSet:标记是否产生循环
earlyProxyReferences:标记是否提前进行了aop
ConcurrentHashMap在step seven中已经出现,接下来,我们看看三级缓存和creatingSet的真实面貌,先看获取单例bean的方法:
//获取单例bean,singletonFactory是一个lambda表达式,
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
//先从单例池找
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
…………………………
//获取单例bean前,先做一个标记,正在创建
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
//创建单例bean,回调用createBean()
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
…………………………
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
//创建单例bean成功后,set移除bean
afterSingletonCreation(beanName);
}
if (newSingleton) {
//加入单例池
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
creatingSet
在获取单例bean时,有一个方法beforeSingletonCreation():
protected void beforeSingletonCreation(String beanName) {
//inCreationCheckExclusions中的bean,表示如果是这些bean正在创建中,重复创建也没关系
//singletonsCurrentlyInCreation中的bean,表示如果是这些bean正在创建中,在没创建完时不能重复创建
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
singletonsCurrentlyInCreation就是我们的creatingSet,表示这些bean正在创建中,在没创建完时不能重复创建;
我们点进去beforeSingletonCreation可以看到,是一个set。创建前加到set;
点进去afterSingletonCreation可以看到,创建成功后移除;
三级缓存
我们在看加入单例池的方法addSingleton():
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//一级缓存 存入bean
this.singletonObjects.put(beanName, singletonObject);
//三级缓存 移除
this.singletonFactories.remove(beanName);
//二级缓存 移除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
在addSingleton()方法,可以看到三级缓存,一级缓存是ConcurrentHashMap,二级、三级是HashMap。
这是为啥呢?留着后边解释。
在bean生命周期这篇博文里边,我们看到了doCreateBean()方法,这个方法里边还有一块代码,我们的细品下:
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
//如果 存在循环依赖,就存入一级缓存,如果不存循环依赖,就不存啦
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//添加 三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
//属性赋值和初始化
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
……………………
if (earlySingletonExposure) {
//从三个缓存池,挨个获取
Object earlySingletonReference = getSingleton(beanName, false);
………………………………
}
//添加 三级缓存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
//添加到三级缓存,从二级缓存移除
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
//从三个缓存池,挨个获取
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先从一级缓存取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//再从二级缓存取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//再从三级缓存取,如果有循环依赖,三级缓存应该是有的
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//从三级缓存,获取到作为value值得lambda,执行aop,得到代理对象
singletonObject = singletonFactory.getObject();
//添加到二级缓存,从三级缓存移除
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
这段代码,我们看到了:
原始对象添加到三级缓存,从二级缓存移除;
三级缓存中获取原始对象,添加到二级缓存,然后在移除三级中缓存;
我们看到,二级缓存和三级缓存,一个添加,另一个就要删除,他俩是同步进行的,是一个原子操作,即使是ConcurrentHashMap也无法保证两个map操作的原子行,所以添加和删除三级缓存都是在synchronized下运行的,这样的话,二级和三级缓存是hashMap就够了,不需要使用ConcurrentHashMap。
放到三级缓存后,立马移除二级缓存的该bean,这是为啥呢?
三级缓存中的lambda,只能使用一次,多次使用,会多次进行aop,那就产生了多个代理对象,这是不允许的。
什么样的循环依赖,spring解决不了
spring解决不了,构造函数的相互依赖, 也就是在对象的 原始对象 没有创建成功之前就依赖,解决不了,如下所示:
public class BService{
private AService aservice;
public BService(AService aService){
this.aservcie = aService;
}
}
public class AService{
private BService bservice;
public AService(BService bService){
this.bservcie = bService;
}
}
实例化AService,只有调用现在唯一的 构造方法AService(BService bService),但是必须得传一个参数BService的bean,才能调,那就得找到一个BService的bean,单例池没有。
那就的实例化BService,实例化BService,只有调用现在唯一的 构造方法BService(AService aService),但是必须得传一个参数AService的bean,才能调,但是AService的bean还没有创建出来,…又循环了……
类似这样的相互依赖 解决不了;
如果想解决的话,可以用@Lazy注解;
@Lazy注解的原理:实例化AService,先生成一个bService的代理对象,当调用bService方法的时候,会去单例池找,找不到再创建