Java Spring解决循环依赖的方法
一、Spring中的循环依赖
1.Spring中的循环依赖基础
1.什么是循环依赖?
循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如A引用B,B引用C,C引用A,则它们最终反映为一个环。
我们这里以两个类A和B为例进行讲解,如下是A和B的声明:
@Component
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
public void setA(A a) {
this.a = a;
}
}
结论先行:
1.构造器循环依赖—-初始化失败
2.field属性注入并且是单列模式循环依赖(setter方式)—-初始化成功
3.field属性注入循环依赖(prototype)—-初始化失败
现象总结:同样对于循环依赖的场景,构造器注入和prototype类型的属性注入都会初始化Bean失败。因为@Service默认是单例的,所以单例的属性注入是可以成功的。
2.Spring 如何解决循环依赖?
Spring bean注入流程
类实例化 -> 属性注入 -> 执行初始化方法 -> (如果有需要)生成代理对象 -> 使用
三级缓存解决循环依赖流程
A、B两个类相互依赖,初始化A的时候,第一步实例化A完成(生成对象工厂实例放入三级缓存),注入依赖属性B,一级缓存查询B没有,二级缓存查询B没有,
初始化B(生成对象工厂实例放入三级缓存),注入依赖属性A,一级缓存查询A没有,二级缓存查询A没有,三级缓存查询到A的对象工厂,需要AOP增强则生成A的代理对象,没有则直接创建A实例对象,并将A放入到二级缓存,注入A的代理对象完成,生成代理对象B,B移入一级缓存。
继续A属性注入(B的代理对象),然后可能还会依赖注入C对象流程和B一致,所有依赖注入完成后A初始化,生成A的代理对象,发现A的代理对象已存在,则跳过,放入一级缓存。此时A的代理对象也是提前生成的,但是仅针对循环依赖提前生成。
三级缓存不存在循环依赖的正常生成流程
第一步实例化A完成(生成对象工厂实例放入三级缓存,此时虽然放入三级缓存,但没有生成代理对象),注入属性,执行初始化方法,生成代理对象,移入一级缓存。
3.spring中出现循环依赖的场景
spring中出现循环依赖主要有以下场景:
2.循环依赖常见的解决方案
1.重新设计
当有一个循环依赖,很可能是有一个设计问题并且各责任没有得到很好的分离。应该尽量正确地重新设计组件,以便它们的层次是精心设计的,也没有必要循环依赖。
如果不能重新设计组件(可能有很多的原因:遗留代码,已经被测试并不能修改代码,没有足够的时间或资源来完全重新设计…),但有一些变通方法来解决这个问题,如下面几种方式
2.延迟加载@Lazy注解—需要了解一下它的实现原理???
解决Spring 循环依赖的一个简单方法就是对一个Bean使用延时加载。也就是说:这个Bean并没有完全的初始化完,实际上他注入的是一个代理,只有当他首次被使用的时候才会被完全的初始化。
3.Setter/Field 注入
简单地说,你对你需要注入的bean是使用setter注入(或字段注入),而不是构造函数注入。
@Service
public class ServiceAImpl implements ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
4.bean工厂延迟获取
FeederServiceImpl feederService = SpringUtils.getBean(FeederServiceImpl.class);
二.spring 三级缓存实现原理
1.一级缓存
已经完成了bean的所有流程,即实例化、注入、初始化完成的bean实例,并放进了单列池中了。
一级缓存:
/** 保存所有的singletonBean的实例,已经完成了所有流程可以用的bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
2.二级缓存
保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入,但已经提前完成了AOP增强。
/** 保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
为什么需要二级缓存?
如果出现循环依赖+aop时,多个地方注入这个动态代理对象需要保证都是同一个对象,而三级缓存中的取出来的动态代理对象每次都是新对象,地址值不一样。
3.三级缓存
singletonBean的生产工厂,即创建单列bean的工厂。
存的是一个函数接口, 函数接口实现 创建动态代理调用BeanPostProcessor 。 为了避免重复创建, 调用把返回的动态代理对象或者原实例存储在二级缓存中。
/** singletonBean的生产工厂,即创建单列bean的工厂*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
在取三级缓存并创建bean时,ObjectFactory调用getObject(),不仅会取到bean实例而且还会判断是否生成代理对象,并放到二级缓存中。
为什么需要第三级缓存?
解决如果出现循环依赖,判断是否需要提前进行aop等操作
一级缓存:
/** 保存所有的singletonBean的实例,已经完成了所有流程可以用的bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
二级缓存:
/** 保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
三级缓存:
/** singletonBean的生产工厂,即创建单列bean的工厂*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** 保存所有已经完成初始化的Bean的名字(name) */
private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);
/**
标识指定name的Bean对象是否处于创建状态,这个状态非常重要。
如果创建完毕之后,会将其从 singletonsCurrentlyInCreation 列表中移除,
并且会将创建好的 bean 放到另外一个单例列表中,这个列表叫做 singletonObjects即一级缓存中。
*/
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
7. 本站有不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别!
66源码网 » Java Spring解决循环依赖的方法