问题场景
在上一篇文章中spring中autowired注入自己的代理后,最后容器中的对象是原对象还是代理对象,我们谈到了在spring中autowired注入自己的代理后,最后容器中实际只有一个根据事务切面生成的代理对象。
现在我们给文章中涉及的代码加@Async
注解
@Service
public class UserRightService {
@Autowired
private UserRightService userRightService;
@Transactional
@Async
public boolean checkUserFuncRight(String userId, String url) {
......
......
}
}
然而启动项目报错
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'userRightService': Bean with name 'userRightService' has been injected into other beans [userRightService] 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.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:624)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1304)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1224)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
... 42 common frames omitted
分析
我们结合spring源码一步步分析:为什么加了@Async
注解后,反而报错了?
1.populateBean方法注入成员
该方法给UserRightService
类注入成员变量UserRightService
后,spring也会往二级缓存中存放一份。
2.initializeBean方法
之后进入initializeBean
方法,
方法遍历里BeanPostProcessor
,如下
由于加了@Async
注解,此时会遍历到AsyncAnnotationBeanPostProcessor
的父类AbstractAdvisingBeanPostProcessor
的如下方法,
红框处会生成代理对象。
此时,
initializeBean
方法返回的exposedObject
是一个通过AsyncAnnotationBeanPostProcessor
生成的新代理对象。
3.获取二级缓存数据
由于二级缓存已经存放了数据,所以下面if (earlySingletonReference != null) {
为true,之后触发下图红框的判断逻辑
那到底进入哪一个条件分支呢?
因为
exposedObject
是一个通过AsyncAnnotationBeanPostProcessor
生成的新代理对象,而bean
是原对象,所以第一个红框if不成立;
所以,进入第二个红框的else if
,最终抛出文章开头的异常。
如此设计的原因
spring为什么要这么设计?假如spring最后不报错,出现的场景是:
假设我在A类也注入了
UserRightService
成员,那么最终A类的UserRightService
成员,和UserRightService
类自身注入的UserRightService
成员,就不是同一个代理对象了,违反了单例原则。
解决方式
通过分析可知,if (earlySingletonReference != null) {
为true的条件是二级缓存能获取到数据,所以我们可以避免从二级缓存获取到数据,来让条件为false。
而二级缓存能获取到数据的前提是该类触发了循环依赖,即通过@Autowired
注入自己,或者A->B->A模式。
所以,避免循环依赖,就可以同时使用 @Transactional
和@Async
。
但如果我们确实需要在UserRightService
类的方法里用到自身代理对象,可以在代码中通过AopContext.currentProxy()
来获取。