@Async注解的坑

@Async注解遇上循环依赖,Spring无法解决

场景复现

AService 和 BService 相互引用,AService的 save() 方法加了 @Async 注解。

@Component
public class AService {
    @Resource
    private BService bService;

    @Async
    public void save() {

    }
}

@Component
public class BService {

    @Resource
    private AService aService;

}

这段代码会报BeanCurrentlyInCreationException异常。

将@Async注解去掉之后,再次启动项目,项目成功起来。

问题结论

spring 已经默认解决了循环依赖,但是@Async导致的循环依赖无法解决。

分析

AsyncAnnotationBeanPostProcessor 没有实现 getEarlyBeanReference

它的代理逻辑只在:

org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization

//初始化完成后才创建代理
public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (this.advisor != null && !(bean instanceof AopInfrastructureBean)) {
            if (bean instanceof Advised) {
                Advised advised = (Advised)bean;
                if (!advised.isFrozen() && this.isEligible(AopUtils.getTargetClass(bean))) {
                    if (this.beforeExistingAdvisors) {
                        advised.addAdvisor(0, this.advisor);
                    } else {
                        advised.addAdvisor(this.advisor);
                    }

                    return bean;
                }
            }

            if (this.isEligible(bean, beanName)) {
                ProxyFactory proxyFactory = this.prepareProxyFactory(bean, beanName);
                if (!proxyFactory.isProxyTargetClass()) {
                    this.evaluateProxyInterfaces(bean.getClass(), proxyFactory);
                }

                proxyFactory.addAdvisor(this.advisor);
                this.customizeProxyFactory(proxyFactory);
                ClassLoader classLoader = this.getProxyClassLoader();
                if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
                    classLoader = ((SmartClassLoader)classLoader).getOriginalClassLoader();
                }

                return proxyFactory.getProxy(classLoader);
            } else {
                return bean;
            }
        } else {
            return bean;
        }
    }

而循环依赖发生在初始化之前(属性注入阶段),此时:

  • A 还没走到 postProcessAfterInitialization

  • B 拿到的是 A 的原始对象(raw bean)

  • 调用 a.asyncMethod()同步执行!代理未生效!

解决办法

1、调整对象间的依赖关系,从根本上杜绝循环依赖,没有循环依赖,就没有早期暴露这么一说,那么就不会出现问题。

2、不使用@Async注解,可以自己通过线程池实现异步,这样没有@Async注解,就不会在最后生成代理对象,也就不会导致跟早期暴露出去的对象不一样。

3、可以在循环依赖注入的字段上加@Lazy注解。

Last updated

Was this helpful?