@Async引起循环依赖报错

@Async引起循环依赖报错

@Async会通过代理,通过BeanPostProcessor扩展点增强类的功能,提供类方法异步的能力,新生成的代理类会改变原始类对象的引用。我们来看看具体的实现类AsyncAnnotationBeanPostProcessor。

好家伙,完全没有重写getEarlyBeanReference,结果报错,出处如下。

//earlySingletonReference 循环引用生成的代理类
		//exposedObject 本方法生成的代理类
		//bean  原始引用
		if (earlySingletonExposure) {
			Object earlySingletonReference = getSingleton(beanName, false);
			//如果走到这里,说明触发了循环依赖
			if (earlySingletonReference != null) {
				//如果相等,这里是指发生了aop根据cacheKey判定到幂等
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
			//走到这里说明有其他钩子(后置处理器)把bean替换了,所以要检查在此之前是否已经发生过该bean的依赖注入,如果发生,就导致一个bean的不同版本被注入,针对这种情况,会抛出异常
				} else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
										StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
										"] in its raw version as part of a circular reference, but has eventually been " +
										"wrapped. This means that said other beans do not use the final version of the " +
										"bean. This is often the result of over-eager type matching - consider using " +
										"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

在doCreateBean核心方法处以前看不懂的代码也可以解释了,代理对象的引用发生变化,导致spring认为有两个不同版本的bean加入到了容器内,对于这种情况spring也无能为力,只得抛出一个错。


@Component
public class A{
     @Autowired
    private B b;
 
    @Async
    @Override
    public void testA() {
 
    }
}

@Component
public class ClassB{
    @Autowired
    private A a;
 
    @Override
    public void testB() {
        a.testA();
    }
}

1.context.getBean(A)开始创建A,A实例化完成后给A的依赖属性b开始赋值
2.context.getBean(B)开始创建B,B实例化完成后给B的依赖属性a开始赋值
3.重点:此时因为A支持循环依赖,所以会执行A的getEarlyBeanReference方法得到它的早期引用。而执行getEarlyBeanReference()的时候因为@Async根本还没执行,所以最终返回的仍旧是原始对象的地址
4.B完成初始化、完成属性的赋值,此时属性field持有的是Bean A原始类型的引用
5.完成了A的属性的赋值(此时已持有B的实例的引用),继续执行初始化方法initializeBean(…),在此处会解析@Aysnc注解,从而生成一个代理对象,所以最终exposedObject是一个代理对象(而非原始对象)最终加入到容器里
6.尴尬场面出现了:B引用的属性A是个原始对象,而此处准备return的实例A竟然是个代理对象,也就是说B引用的并非是最终对象(不是最终放进容器里的对象)
7.执行自检程序:由于allowRawInjectionDespiteWrapping默认值是false,表示不允许上面不一致的情况发生,so最终就抛错了

如何解决

1.使用@Lazy或者 @ComponentScan(lazyInit = true)

@Component
public class ClassB{
 
    @Autowired
    @Lazy
    private A a;
 
    @Override
    public void testB() {
        a.testA();
    }
}

2.使用setter注入


@Component
public class ClassA{
 
    private B b;
 
    public void setB(B b) {
        this.b = b;
    }
 
   @Async
    @Override
    public void testA() {
        b.testb();
    }
}

@Component
public class ClassB{
 
    private A a;
 
    public void setA(A a) {
        this.a = a;
    }
 
    @Override
    public void testB() {
        a.testA();
    }
}

3.使用@Autowired注解

@Component
public class A{
     
    private B b;
 
    @Autowired
    public void SetB(B b) {
        this.b= b;
    }
 
 
    @Async
    @Override
    public void testA() {
        //TODO
    }
}

4.重构方法
新建class,把@Async的方法放在新的类中,从根本上消除循环依赖

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值