报错信息如下
2024-10-15 16:05:30 admin [main] ERROR org.springframework.boot.SpringApplication - Application run failed
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'adminUserServiceImpl':
Bean with name 'adminUserServiceImpl' has been injected into other beans [adminConfigServiceImpl,adminRoleServiceImpl] 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.
spring默认是支持循环依赖,allowCircularRefrence默认为true。
也可以手动设置applicationContext.setAllowCircularReferences(false);
产生这个问题原因:in its raw version as part of a circular reference, but has eventually been wrapped.
应该是项目中对某个类进行了wrapped, 可能是开启异步导致
最后发现是处于循环依赖中一环的xxxService中添加了@Async
的注解导致的。
结论:当A、B对象之间相互依赖,A早于B实例化,并且A对象使用了@Async
注解或由BeanPostProcessor后置处理器返回了代理对象时,会产生如上报错
原因:我们知道,spring三级缓存一定程度上解决了循环依赖问题。
A对象在实例化之后,属性赋值opulateBean(beanName, mbd, instanceWrapper)
执行之前,将ObjectFactory添加至三级缓存中,
从而使得在B对象实例化后的属性赋值过程中,能从三级缓存拿到ObjectFactory,调用getObject()方法拿到A的引用,
B由此能顺利完成初始化并加入到IOC容器。
此时A对象完成属性赋值opulateBean
方法之后,将会执行初始化initializeBean(beanName, exposedObject, mbd)
方法,
重点是@Async
注解的处理正是在这地方完成的,其对应的后置处理器AsyncAnnotationBeanPostProcessor
接收的是在postProcessAfterInitialization
方法中返回的代理对象,
此代理对象与B中持有的A对象引用不同,导致了以上报错。
这里的问题其实是因为代理对象被延迟创建导致在后续检查依赖关系时不通过。
假设A、B两个循环,A类中有@Async
标记的方法,因为在处理循环依赖的时候从三级缓存当中取出来的工厂类当中的BeanPostProcessor并没有能够处理@Async
注解的,所以此时并没有获取代理对象返回仍然是原始对象Bean A,这也就意味着Bean B此时被注入的是原始Bean A而不是Bean A的代理对象。
而当Bean B完成创建返回Bean A创建流程时,在initializeBean
方法中的afterIntialization
当中会存在一个AsyncAnnotationBeanPostProcessor
处理器,这个处理器会识别@Async
注解并生成Bean A代理对象。
这个时候就会出现问题,我们预期的是Bean B应该依赖的是Bean A的代理对象,但是在Bean B创建过程中Spring已经提前注入了Bean A对象,这就导致了在后续检查循环是否正确的时候报错。
这也是为什么我们使用在Bean A当中对Bean B使用@Lazy
就可以解决这个问题,因为延迟了Bean B的创建就不会将没有生成代理对象的原始Bean A注入导致依赖错误。
有些博客里面说因为循环依赖无法创建代理对象,这个说法是完全错误的,跟循环依赖没有任何关系啊,只是Spring往三级缓存添加的工厂对象里面的BeanPostProcessor
没有能够处理@Async
注解的,所以才返回的是原始的Bean,千万不要被忽悠了
有其他也会报错的注解吗?
- 报错是因为在检查Bean依赖关系时发现注入的对象和预期不符(应该是代理对象而不是原始对象);
- 由1可得出现该错的前置条件是存在代理对象的循环依赖场景。基于这个前置条件,还需要是Spring设置在三级缓存中的工厂对象里面的BeanPostProcessor无法处理,但在initializeBean方法中BeanPostProcessor的afterInitialization可以处理的代理对象生成的场景,在这样的条件下才会出现上面的报错;