什么是循环依赖
很简单就是A对象依赖了B对象,B对象依赖了A对象
//A依赖了B
class A{
public B b;
}
//B依赖了A
class B{
public A a;
}
如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情
A a=new A();
B b=new B();
a.b=b;
b.a=a;
这样A,B就依赖上了
spring的循环依赖
@Component("aService")
public class AService {
@Autowired
private BService bService;
public void test(){
System.out.println(bService);
}
}
@Component("bService")
public class BService {
@Autowired
private AService aService;
}
AService的生命周期
1. 实例化(new AService()),原始对象
2.填充bService属性----》从单例池中找---》找不到---》创建BService的Bean
BService的生命周期
2.1 实例化(new BService()),原始对象
2.2 .填充bService属性----》从单例池中找---》找不到---》创建AService的Bean
2.3 填充其他属性
2.4 做其他重要的事情
2.5 放入单例池 Bean对象
3.填充其他属性
4.做其他重要的事情
5. 放入单例池 Bean对象
这就造成了循环依赖
解决方法:我们可以定义一个map,来存储实例化对象,在1的时候存储实例化对象<"aService",AService原始对象>
在2.2的时候,一级缓存单例池找不到,就去找这个map里的对象,进行属性的填充!
AService的生命周期
1. 实例化(new AService()),原始对象 map存储对象<"aService",AService原始对象>
2.填充bService属性----》从单例池中找---》找不到---》创建BService的Bean
BService的生命周期
2.1 实例化(new BService()),原始对象 map存储对象<"bService",BService原始对象>
2.2 .填充bService属性----》从单例池中找---》找不到---》map---》AService原始对象
2.3 填充其他属性
2.4 做其他重要的事情
2.5 放入单例池 Bean对象
3.填充其他属性
4.做其他重要的事情(Aop)
5. 放入单例池 Bean对象
大致流程
若第四步需要做Aop,那么就会产生一个Aservice的代理对象,我们放到单例池中去应该是代理对象,我们赋值给BService中的属性AService也应该是代理对象!!!那么问题也随之而来了,在执行流程中,我们属性赋值赋值的是原始对象!!!如何解决?那也就是说我们要提前去做AOP,我们可以在第一步实例化就去创建一个AService代理对象,map里面也存放代理对象。当然这样的话第四步就要去判断AService有没有进行过AOP,若进行过AOP,则第四步就直接跳过。
为什么要提前新建Aop?出现循环依赖
那么我们在2.2的时候,就需要去判断此对象是否存在循环依赖(也就是AService是否正在创建中),若存在,且满足切面,那么就需要提前进行Aop!!!
如何判断aService正在创建中?
我们可以提前建立一个集合,存放aService,然后在2.2中判断。
若是的话,直接就生成代理对象填充属性
这个aService等创建Bean再释放。
这样问题看似得到了解决!!!
新建CService
@Component("cService")
public class CService {
@Autowired
private AService aService;
public void xxx(){
aService.test();
}
}
修改AService
@Component("aService")
public class AService {
@Autowired
private BService bService;
@Autowired
private CService cService;
public void test(){
System.out.println(bService);
}
}
这样的话,我们填充完BService属性就要填充CService,spring中找不到CService,就要去实例化CService,
这样就会产生两个AService,但是实际上我们想要B、CService中产生的同一个AService!!!
要解决这个问题很简单,我们只需要将第一个产生的Aservice代理对象放入二级缓存earlySingleObjects里面,填充cService的时候,先从单例池从找,找不到就去二级缓存中找,这样就有效的解决了单例Bean的问题。这就为为什么需要二级缓存的原因,保证单例
二级缓存还有一个作用就是可以在4.5的时候从二级缓存中拿出aService放入单例池中
缓存介绍:
二级缓存 earlySingleObjects Map<beanName,对象>
一级缓存 单例池 singletonObjects Map<beanName,bean对象>
三级缓存 singletonFactories Map<String,ObjectFactory<?>>
AService原始对象和AService代理对象的联系
修改下代码
AService
BService
现在A、BService就没有出现循环依赖了
主启动类(打断点,像下图那样)
发现得到的代理对象aService的属性是空的
下图是原始对象(里面的bService是有值的)
在打个断点
启动spring
此时的原始对象bean aService是空的
然后会去执行三级缓存的操作(后面有讲)
exposedObject对象是我们当前要返回的对象
这个方法是来填充Bean属性的,上图是还没执行此方法的情况
执行完此方法 情况:
目前为止还未进行AOP
下面此方法就进行aop
exposedObject就是代理对象,对bService没有进行任何操作
返回的话就是代理对象
从上面可以看出,进行aop产生代理对象,用到了我们的原始对象
实例化产生的原始对象其实就是放到三级缓存中去的,并且将BeanName和BeanDefinition也一起放进去,大概就是map<'aService',lamda(()->getEarlyBeanReference(beanName,mbd,bean))>
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
大致流程图:
2.2填充属性aService 调用的就是这个方法
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先从单例池中找
Object singletonObject = this.singletonObjects.get(beanName);
//判断是否循环依赖
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//从二级缓存中找
singletonObject = this.earlySingletonObjects.get(beanName);
//二级缓存中也没有
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
//从三级缓存中拿
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); //执行lambla ,也就是执行AOP
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject); //放到二级缓存
this.singletonFactories.remove(beanName);
//从三级缓存中移除
}
}
}
}
}
}
return singletonObject;
}
执行了aop,就从三级缓存中移除,是因为要防止再次生产对象(防止二次代理)!!!
类似的再添加表达式去三级缓存,也有类似现象
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);
}
}
}
2.2中去三级缓存找后,不一定会生产代理对象(不一定走Aop),也许还是原始对象,放入二级缓存中
判断是否出现循环依赖对应饿源码
//AService出现了循环依赖
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName); //beanName aService
//判断
this.earlyProxyReferences.put(cacheKey, bean);
return this.wrapIfNecessary(bean, beanName, cacheKey); //代理对象
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
} else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
} else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
//获取当前beanClass所匹配的advisors,判断有误切面
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
//如果匹配的advisors不等于null,name则进行代理,并返回代理对象
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//基于bean对象和Advisor创建代理对象
Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
//存一个代理对象的类型
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean; //返回原始对象
}
} else {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean; //返回原始对象
}
}
正常情况进行AOP的地方(也就是第四步)
//正常情况进行AOP的情况
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
//earlyproxyReferences中存取的是哪些提前进行过AOP的bean,beanName:AOP之前的对象
//注意earlyproxyReferences中并没有存AOP的代理对象,BeanPostProcessor
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//没有提前过Aop,则进行Aop
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
因此解决循环依赖,除了一二三级缓存,还有creatingSet和earlyProxyReferencs
简述就是:
a–>放进三级缓存–>注入b–>从单例池中找–>找不到就二级缓存–>再找不到就三级缓存中找,创建a后(可能是代理对象),将a从三级缓存中移除,放入二级中,将b放入单例池中,进行a中b的属性注入,判断若提前进行aop则,则跳过aop步骤,将a放入单例池,从二级缓存中把a拿掉。
修改一下ABservice的循环依赖模式
启动
报循环依赖的错误
此时加个@Lazy注解即可解决