springioc解决循环依赖,spring循环依赖解决方案
目录
简介问题复现原因分析解决方案方案1:懒加载方案2:不让@Async的类有循环依赖方案3:allowrawinjectiondestewrapping设置为真实的为什么@事务性不会导致失败
简介
说明
本文介绍跳羚中的@Async导致循环依赖失败的原因及其解决方案。
概述
我们知道,春天解决了循环依赖问题,但弹簧的异步(@Async)会使得循环依赖失败。本文将用实例来介绍其原因和解决方案。
问题复现
启动类
启动类添加@启用异步以启用异步功能。
包com .刀导入org。spring框架。靴子。春季申请;导入org。spring框架。靴子。自动配置。弹簧启动应用程序;导入org。spring框架。日程安排。注释。启用异步;@启用异步@ spring启动应用程序公共类演示应用程序{ public static void main(String[]args){ spring应用程序。运行(演示应用程序。class,args);} }服务
英语字母表中第一个字母
包com。刀子。服务;导入org。spring框架。豆子。工厂。注释。自动连线;导入org。spring框架。日程安排。注释。异步;导入org。spring框架。刻板印象。组件;@组件公共类A { @自动有线私有B B@ Async public void print(){ system。出去。println(“Hello World”);}}B
包com。刀子。服务;导入org。spring框架。豆子。工厂。注释。自动连线;导入org。spring框架。刻板印象。组件;@组件公共类B { @自动有线私有A A}控制器
包com。刀子。控制器;导入com。刀子。服务。a;导入org。spring框架。豆子。工厂。注释。自动连线;导入org。spring框架。网络。绑定。注释。获取映射;导入org。spring框架。网络。绑定。注释。休息控制器;@ RestControllerpublic class hello controller { @ auto wired private A A;@ get mapping(/test )公共字符串test(){ a . print();返回"测试成功";}}启动:(报错)
由: org。spring框架。豆子。工厂。bean currentlyincreationexception :创建名为" a "的豆时出错:名为" a "的豆在其原始版本中作为循环引用的一部分被注入到其他豆[b]中,但最终被包装。这意味着所述其他豆类不
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) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1306) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE] ... 20 common frames omitted
原因分析
@EnableAsync开启时向容器内注入AsyncAnnotationBeanPostProcessor,它是一个BeanPostProcessor,实现了postProcessAfterInitialization方法。创建代理的动作在抽象父类AbstractAdvisingBeanPostProcessor上:
// 这个方法主要是为有@Async 注解的 bean 生成代理对象 @Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (this.advisor == null bean instanceof AopInfrastructureBean) { // Ignore AOP infrastructure such as scoped proxies. return bean; } // 如果此Bean已经被代理了(比如已经被事务那边给代理了~~) if (bean instanceof Advised) { Advised advised = (Advised) bean; if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) { // Add our local Advisor to the existing proxys Advisor chain... // beforeExistingAdvisors决定这该advisor最先执行还是最后执行 // 此处的advisor为:AsyncAnnotationAdvisor 它切入Class和Method标注有@Aysnc注解的地方~~~ if (this.beforeExistingAdvisors) { advised.addAdvisor(0, this.advisor); } else { advised.addAdvisor(this.advisor); } return bean; } } // 若不是代理对象,则进行处理 if (isEligible(bean, beanName)) { //copy属性 proxyFactory.copyFrom(this); 工厂模式生成一个新的 ProxyFactory ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName); // 如果没有采用CGLIB,就去探测它的接口 if (!proxyFactory.isProxyTargetClass()) { evaluateProxyInterfaces(bean.getClass(), proxyFactory); } // 切入切面并创建一个getProxy 代理对象 proxyFactory.addAdvisor(this.advisor); customizeProxyFactory(proxyFactory); return proxyFactory.getProxy(getProxyClassLoader()); } // No proxy needed. return bean; } protected boolean isEligible(Object bean, String beanName) { return isEligible(bean.getClass()); } protected boolean isEligible(Class<?> targetClass) { //首次从 eligibleBeans 这个 map 中获取值肯定为 null Boolean eligible = this.eligibleBeans.get(targetClass); if (eligible != null) { return eligible; } //如果没有配置 advisor(即:切面),返回 false if (this.advisor == null) { return false; } // 若类或方法有 @Aysnc 注解,AopUtils.canApply 会判断为 true eligible = AopUtils.canApply(this.advisor, targetClass); this.eligibleBeans.put(targetClass, eligible); return eligible; }
创建A,A实例化完成后将自己放入第三级缓存,然后给A的依赖属性b赋值创建B,B实例化后给B的依赖属性a赋值从第三级缓存中获得A(执行A的getEarlyBeanReference方法)。执行getEarlyBeanReference()时@Async根本还被扫描,所以返回的是原始类型地址(没被代理的对象地址)。B完成初始化、属性的赋值,此时持有A原始类型引用(没被代理)完成A的属性的赋值(此时持有B的引用),继续执行初始化方法initializeBean(...),解析@Aysnc注解,生成一个代理对象,exposedObject是一个代理对象(而非原始对象),加入到容器里。问题出现了:B的属性A是个原始对象,而此处的实例A却是个代理对象。(即:B里的A不是最终对象(不是最终放进容器的对象))执行自检程序:由于allowRawInjectionDespiteWrapping默认值是false,表示不允许上面不一致的情况发生,就报错了
解决方案
有三种方案:
懒加载:使用@Lazy或者@ComponentScan(lazyInit = true)
不要让@Async的Bean参与循环依赖
将allowRawInjectionDespiteWrapping设置为true
方案1:懒加载
说明
建议使用@Lazy。
不建议使用@ComponentScan(lazyInit = true),因为它是全局的,容易产生误伤。
实例
这两个方法都是可以的:
法1. 将@Lazy放到A类的b成员上边法2: 将@Lazy放到B类的a成员上边法1:将@Lazy放到A类的b成员上边
package com.knife.service; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Lazy;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Component; @Componentpublic class A { @Lazy @Autowired private B b; @Async public void print() { System.out.println("Hello World"); }}
法2:将@Lazy放到B类的a成员上边
package com.knife.service; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Lazy;import org.springframework.stereotype.Component; @Componentpublic class B { @Lazy @Autowired private A a;}
这样启动就能成功。
原理分析
以这种写法为例进行分析:@Lazy放到A类的b成员上边。
即:
package com.knife.service; import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Lazy;import org.springframework.scheduling.annotation.Async;import org.springframework.stereotype.Component; @Componentpublic class A { @Lazy @Autowired private B b; @Async public void print() { System.out.println("Hello World"); }}
假设 A 先加载,在创建 A 的实例时,会触发依赖属性 B 的加载,在加载 B 时发现它是一个被 @Lazy 标记过的属性。那么就不会去直接加载 B,而是产生一个代理对象注入到了 A 中,这样 A 就能正常的初始化完成放入一级缓存了。
B 加载时,将前边生成的B代理对象取出,再注入 A 就能直接从一级缓存中获取到 A,这样 B 也能正常初始化完成了。所以,循环依赖的问题就解决了。
方案2:不让@Async的类有循环依赖
略。
方案3:allowRawInjectionDespiteWrapping设置为true
说明
本方法不建议使用。
这样配置后,容器启动不报错了。但是:Bean A的@Aysnc方法不起作用了。因为Bean B里面依赖的a是个原始对象,所以它不能执行异步操作(即使容器内的a是个代理对象)。
方法
import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;import org.springframework.stereotype.Component; @Componentpublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowRawInjectionDespiteWrapping(true); }}
为什么@Transactional不会导致失败
概述
同为创建动态代理对象,同为一个注解标注在类/方法上,为何@Transactional就不会出现这种启动报错呢?
原因是,它们代理的创建的方式不同:
@Transactional创建代理的方式:使用自动代理创建器InfrastructureAdvisorAutoProxyCreator(AbstractAutoProxyCreator的子类),它实现了getEarlyBeanReference()方法从而很好的对循环依赖提供了支持
@Async创建代理的方式:使用AsyncAnnotationBeanPostProcessor单独的后置处理器。它只在一处postProcessAfterInitialization()实现了对代理对象的创建,因此若它被循环依赖了,就会报错
详解
处理@Transactional注解的是InfrastructureAdvisorAutoProxyCreator,它是SmartInstantiationAwareBeanPostProcessor的子类。AbstractAutoProxyCreator对SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法进行了覆写:
AbstractAutoProxyCreator# getEarlyBeanReference
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { // 其他代码 @Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); } }
AbstractAutoProxyCreator#postProcessAfterInitialization方法中,判断是否代理过,是的话,直接返回:
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { // 其他代码 @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } }
以上就是Spring处理@Async导致的循环依赖失败问题的方案详解的详细内容,更多关于Spring 循环依赖失败的资料请关注盛行IT其它相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。