@lazy注解处理循环注入问题 和彻底理解循环依赖底层原理
1.先看如下demo: B和A相互循环依赖
@Component public class B { @Autowired private A a; } @Component public class A { @Autowired private B b; }
启动项目:结果没有报错:
2. 加入异步逻辑修改
@Component public class A { @Autowired private B b; @Async public void test(){ } } @Component public class B { @Autowired private A a; } @EnableAsync public class App { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(App.class); } }
启动后:
1 Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Bean with name 'a' has been injected into other beans [b] 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 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example. 2 Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Bean with name 'a' has been injected into other beans [b] 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 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
解决方案:加入lazy注解:
@Component public class B { @Autowired @Lazy private A a; } @Component public class A { @Autowired private B b; @Async public void test(){ } }
启动后:没有异常
上面发现使用@Async异步注解,循环依赖就会报错,有可能是因为有了@Async注解修饰的方法,其对应的类被代理了,那代理了就会报错么?我们继续尝试事务注解看看:
@Component public class A { @Autowired private B b; @Transactional public void test(){ } } @Component public class B { @Autowired private A a; } @EnableTransactionManagement public class App { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(App.class); } }
启动后:正常,没有报错
于是我们不经要问:1.循环依赖本来不会报错,为何添加@Async异步注解后就会导致报错 2. 为何添加@Transactional注解就不会报错 3.使用了@Async异步注解的循环依赖,为何可以使用@lazy注解解决
我们要想清楚上面的问题,就需要了解Bean的生命周期:
一个简单的Bean生命周期如下:
问题出现就属性赋值这里:
由图:我们知道,当B也依赖A时,需要去容器中找到A,A已经实例化了,只是还没属性赋值,所以,不应该再实例化,解决方案:在A创建的实例化后,用一个map存起来A来不就行了么?于是有了
似乎上面已经可以解决循环依赖了,但细想一下我们会发现问题:
通过上面的逻辑,我们发现了问题所在,B赋值属性A时,如果从Map中直接获取,那么得到的是原生对象,如果后续A没有被代理,一切没问题,如果A被代理了,那么B得到的对象就不对了,怎么解决,如果我们将aop提前是不是解决了问题
由于A对象的Aop方式提前了,那么B依赖的A就是代理对象了,A对象执行赋值后,后续到Aop这一步,会判断是否已经AOP过了,是的话就不会再Aop了,问题来了:如果C也跟A相互依赖,难道C
去依赖A时,也要通过ObjectFactory获取A的代理对象么?如果是这样,A就存在2个代理对象了,A是单例的,因此这样不行,于是产生了一个新的缓存,我们称之为二级缓存
于是,spring似乎完美解决了循环依赖问题?但为何使用@Async进行异步代理,会报错?
我们看看报错的原因就知道:
那为何@Transactional修饰就没问题呢?原因是因为:ObjectFactory.getObject()方法可以产生代理对象
为何使用@lazy注解修饰就能解决问题呢?
我们看看源码:
从源码来看,为何@Aync注解修饰,不能在ObjectFactory.getObject()方法实现代理对象:
而@Tranctional注解相关的处理器
那么问题?如果A已经在getObject()方法后产生了代理类,后续init()方法后,还会执行代理么?答案是不会了,因为: