Python 官方文档:入门教程 => 点击学习
目录循环依赖产生情景spring如何解决循环依赖循环依赖产生情景 探讨如何解决循环依赖之前,更应该思考清楚什么情况下会发生这种问题? 1、模拟Prototype Bean的循环依赖
探讨如何解决循环依赖之前,更应该思考清楚什么情况下会发生这种问题?
1、模拟Prototype Bean的循环依赖
static class BeanA {
// 1. 属性循环依赖
BeanB beanB = new BeanB();
// 2. 构造器循环依赖
public BeanA(BeanB beanB) {
this.beanB = beanB;
}
}
static class BeanB {
// 1. 属性循环依赖
BeanA beanA = new BeanA();
// 2. 构造器循环依赖
public BeanB(BeanA beanA) {
this.beanA = beanA;
}
}
public static void main(String[] args) {
// 1. 属性循环依赖
BeanA beanA = new BeanA(); // StackOverflowError
// 2. 构造器循环依赖
new BeanA(new BeanB(new BeanA(new BeanB( ))));
}
Prototype bean,构造器注入Bean时,强调注入的是成品Bean,无法解决循环依赖问题。
Prototype bean,属性上注入Bean时,由于原型模式是单个bean可以被多次创建的,一旦发生循环依赖,就会产生上面这种不断在创建新的BeanA与BeanB导致的栈溢出。源码中的解决方式:记录并检测,如下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// ...
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// ...
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// ...
}
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal == null) {
this.prototypesCurrentlyInCreation.set(beanName);
}
else if (curVal instanceof String) {
Set<String> beanNameSet = new HashSet<>(2);
beanNameSet.add((String) curVal);
beanNameSet.add(beanName);
this.prototypesCurrentlyInCreation.set(beanNameSet);
}
else {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.add(beanName);
}
}
获取Bean前先判断是否当前Bean是否为Prototype并且在创建中。如果不是,且当前Bean为Prototype,那么会记录当前Bean正在创建中(通过beforePrototypeCreation方法)。
以模拟循环依赖代码为例:
2、模拟Singleton Bean的循环依赖
static class BeanA {
BeanB beanB;
public BeanA(BeanB beanB) {
this.beanB = beanB;
}
}
static class BeanB {
BeanA beanA;
public BeanB(BeanA beanA) {
this.beanA = beanA;
}
}
public static void main(String[] args) {
// 1. 属性循环依赖
BeanA beanA = new BeanA();
BeanB beanB = new BeanB();
beanA.beanB = beanB;
beanB.beanA = beanA;
// 2. 构造器循环依赖
new BeanA(new BeanB(new BeanA(new BeanB( ))));
}
Singleton bean,构造器注入Bean时,强调注入的是成品Bean,无法解决循环依赖问题。
Singleton bean,属性上注入Bean时,允许注入提前暴露的半成品的Bean,即没有填充属性&初始化的Bean,只要引用关系能关联上,属性填充与初始化随着程序的执行自然就会处理完成,可以解决循环依赖。
为什么构造器中不能先传递一个半成品Bean,然后赋值给成员Bean属性呢?而一定要传递一个成品Bean?很好理解,spring并不能确定用户在构造器中的操作仅为赋值给Bean属性。且很多时候,我们需要通过bean的一些注入的Bean属性来执行一些业务操作的。如果为空,那么就可能会发生空指针异常。而且这样也不是特别合理。若用户选择属性注入的方式,用户不会涉及这些操作,那么自然就不需要考虑这些问题。
3、总结成一句话来说:spring仅能解决单例Bean下发生在属性上注入Bean产生的循环依赖。
通过三级缓存解决循环依赖,分别是:
1、singletonObjects: 一级缓存,存储成品Bean
2、earlySingletonObjects: 二级缓存,存储半成品Bean
3、singletonFactories: 三级缓存,存储生成半成品Bean的ObjectFactory的lambda
还是以BeanA注入BeanB,BeanB注入BeanA为例,描述一下大致的执行流程:
思考一下,是否能通过二级缓存来解决循环依赖?
首先spring对于三级缓存的应用,就是在生成需要提前暴露的半成品Bean,要么返回的是通过反射实例化后的对象,要么是被一组SmartInstantiationAwareBeanPostProcessor处理后的对象(比如生成代理Bean),然后在放到二级缓存中。那么我们在放入二级缓存时,不通过三级缓存获取这个过程,直接在方法中复现这个过程,在放入二级缓存中,效果想必也是相同的。
到此这篇关于Spring循环依赖产生与解决的文章就介绍到这了,更多相关Spring循环依赖内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
--结束END--
本文标题: Spring循环依赖产生与解决
本文链接: https://www.lsjlt.com/news/175316.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0