背景
1 循环依赖异样信息
- 利用工夫工夫久
- 利用多人同时并行开发
- 利用保障迭代进度
经常出现启动时呈现循环依赖异样
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskPunchEvent': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'playContentService': Bean with name 'playContentService' has been injected into other beans [toVoConvertor] 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. at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:325) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1404) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1255) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:595) ... 40 moreCaused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'playContentService': Bean with name 'playContentService' has been injected into other beans [toVoConvertor] 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. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:622) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeanByName(AbstractAutowireCapableBeanFactory.java:452) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:527) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:497) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:637) at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:180) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:322) ... 51 more
2 依赖关系
先不关注其余不标准问题,看景象
3 波及基础知识
- Spring bean 创立流程
- Dynamic Proxy 动静代理
- Spring-AOP 原理
问题
1、什么是循环依赖?
2、为什么会产生循环依赖?
3、循环依赖有哪些场景?
4、Spring 如何解决循环依赖的?
5、Spring 为什么应用三级缓存?
6、Spring 反对 AOP 循环依赖,为何还存在循环依赖异样?
7、Spring 不反对的循环依赖场景及如何解决?
注:Spring 启动流程与 Bean 创立初始化流程如不相熟,自行补习,篇幅起因此处不做介绍
Spring 循环依赖
1 什么是循环依赖
2 外围概念
- BeanDefinition:spring 外围 bean 的配置信息
- Spring Bean:spring 治理的曾经初始化好当前的可应用的实例
- 首先,通过 spring 通过扫描各种注解 @Compoent、@Service、@Configuration 等等把须要交给 spring 治理的 bean 初始化成 BeanDefinition 的列表
- 而后,依据 BeanDefinition 创立 spring bean 的实例
- Java Bean:Java 简略通过构造函数创立的对象
- Spring 通过推断构造方法后,通过反射调用构造函数创立的对象
3 什么状况下呈现循环依赖
并非使用者手动去 getBean 才会加载并初始化,而是框架启动时进行加载
Spring 创立 Bean - #DefaultListableBeanFactory#preInstantiateSingletons
@Overridepublic void preInstantiateSingletons() throws BeansException { //...... List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) {//FactoryBean 接口解决 ......} else {// 失常 Bean 的加载入口 getBean(beanName); } } } //......}
4 循环依赖场景
- 结构器内的循环依赖
- 注入的益处很显著,如果容器中不存在或者存在多个实现时,能够从容解决。
- 强依赖,先有鸡还是先有蛋问题暂无解,此依赖形式 Spring 不反对,除非本身实现代理加提早注入,这种形式很难解决,除非实现相似于 lazy 生成代理形式进行解耦来实现注入,Spring 没有反对可能因为此种注入场景都能够用其余形式代替且场景极少。
- 弱依赖,spring 4.3 之后减少 ObjectProvider 来解决
// 结构器循环依赖示例
public class StudentA { private StudentB studentB ;
public StudentA(StudentB studentB) {this.studentB = studentB;}}
public class StudentB {private StudentA studentA ; public StudentB(StudentA studentA) {this.studentA = studentA;}}
- setter 形式单例,默认形式
- setter 形式原型,prototype
- 对于“prototype”作用域 Bean,Spring 容器不进行缓存,因而无奈提前裸露一个创立中的 Bean。
- field 属性循环依赖
- 最罕用,此场景是通过反射注入,以下为 @Autowire 注入代码,@Resource 省略
- AutowiredAnnotationBeanPostProcessor#postProcessProperties
@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try {// 属性注入 metadata.inject(bean, beanName, pvs); } catch (BeanCreationException ex) {throw ex;} catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs;}
5 三级缓存解决循环依赖
(1) 一级缓存
- DefaultSingletonBeanRegistry
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
- 最根底的单例缓存
- 限度 bean 在 beanFactory 中只存一份,即实现 singleton scope
(2) 二级缓存
- 二级缓存(未初始化未填充属性提前裸露的 Bean)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
- 看名字应该就能猜到,缓存 earlySingletonBean,与三级缓存配合应用的
- 须要留神:
- 在没有 AOP 场景时是能够的,每次 earlySingletonObjects.get()换成去三级缓存取就能够,存在问题
- 存在 AOP 场景时
- 因而,让使用者去做重复性判断是不可控的,很容易呈现问题,于是引入了第二级缓存,当调用三级缓存里的对象工厂的 getObject 办法之后,getEarlyBeanReference 就会把返回值放入二级缓存,删除三级缓存,后续其余依赖该对象的 Bean 获取的都是同一个 earlyBean,保障 singleton 准则。
- 每次都调用 getEarlyBeanReference,即便返回对象都统一,也节约不必要工夫
- 如果使用者在 getEarlyBeanReference 时间接 new XXX(),则对象又不统一,无奈保障 singleton,所以须要使用者相熟这块原理,并且本身保护,并且裸露外部实现细节
- 每次都调用 getEarlyBeanReference 返回代理对象都不统一,无奈保障 singleton
- 如果没有此缓存,可不可以解决循环依赖问题?
(3) 三级缓存
- 三级缓存(Bean 创立时提供代理机会的 Bean 工厂缓存)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
- 所以二级缓存和三级缓存是组合,不要拆成两个独立的货色去了解
- 基于这种设计,没有产生循环依赖的 bean 就是失常的创立流程
- 互相援用的 bean 会触发链路中最后结点放入三级缓存内容,调用 getEarlyBeanReference 返回相应对象
6 Spring 为何不应用一级、二级缓存解决循环依赖
- 循环依赖产生在 Bean 创立时
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {// 创立 Bean instanceWrapper = createBeanInstance(beanName, mbd, args); } ..... boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean'" + beanName + "'to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
// 填充 Bean 依赖与 Bean 的初始化 Object exposedObject = bean; try {// 填充依赖的 bean 实例 populateBean(beanName, mbd, instanceWrapper); // 初始化 --- 留神!留神!留神!此办法中可能调用 BeanPostProcessor // 的 applyBeanPostProcessorsAfterInitialization 时可能会返回代理对象,如果代理路径与创立时代理形式不同则也会产生不同代理对象 // 从而产生循环依赖中对象不统一状况 exposedObject = initializeBean(beanName, exposedObject, mbd); }
// 如果存在循环依赖,则保障最开始创立的 Bean 须要是循环依赖 getEarlyBeanReference 触发生成的 bean // 因为 getEarlyBeanReference 可能返回的是代理类,因为 singleton 必须全局惟一 if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false); // 只有真正存在循环依赖时,才会触发 getEarlyBeanReference 调用产生 EarlyBean // 未存在循环依赖,则 getEarlyBeanReference 不触发,earlySingletonReference 为 null,返回 exposedObject 即可 if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;} else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {...... if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName, "Bean with name'" + beanName + "'has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] 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."); } } } } return exposedObject;}
- 三级缓存获取 Bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 一级缓存 (单例池) 获取 Bean Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {// 二级缓存获取(提前裸露不齐全)Bean singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) {// 三级缓存 Bean 的创立工厂获取 bean(可提前被代理) singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject;}
- SmartInstantiationAwareBeanPostProcessor 重点 -> APC 之父
// 提供提前创立并返回代理的工厂 singletonFactory.getObject()执行的是个回调 //addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {//getEarlyBeanReference 是 SmartInstantiationAwareBeanPostProcessor 接口定义办法,// 此办法很要害(构造函数推断也在此定义) if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject;}
7 Spring 反对动静代理循环依赖,为何还会出循环依赖异样?
(1) 相互依赖的 Bean 只有须要 AOP 或者动静代理时才有可能呈现循环依赖异样
- 失常状况原始 Spring Bean 无论怎样相互依赖都没有问题,Spring 齐全能够解决这种场景
- 绝大多数存在 AOP 场景也都是反对的,Spring 反对的
- 只有相互依赖场景下某些 Bean 须要被动静代理时偶然会呈现循环依赖异样问题,以下解释异样场景:
艰深解释(省略很多细节):A -> B -> C -> A
- Spring 启动开始创立 A,doCreateBean()中对 A 进行属性填充 populateBean()时须要发现依赖 B 对象,此时 A 还没有进行初始化,把 A 原始对象包装成 SingletonFactory 放入三级缓存。
- A 依赖 B,因而 doCreateBean()会创立 B,并对 B 进行属性填空 populateBean()时须要发现依赖 C 对象。
- C 依赖 A,因而 doCreateBean()会创立 C,并对 C 进行属性填空 populateBean()时须要发现依赖 A 对象。
- 3.1. 此时去一级缓存获取 A,因为 A 前边并没有填充与初始化实现,因而在一级缓存中不存在;
- 3.2. 去二级缓存取 A,因为 A 前边并没有填充与初始化实现,因而在二级缓存中不存在;
- 3.3. 去三级缓存取 A,第一步中把 A 封装成 SingletonFactory 放入三级缓存的,因而三级缓存中能够获取到 A 的对象
3.3.1. 此时获取的 A 如果有必要会对 A 进行动静代理,返回代理对象;
3.3.2. 否则不须要代理则返回未填充、未初始化的原始对象 A;
- 3.4. 获取到 A 对象,注入到 C 中,接着初始化 C,返回 C 对象;
- C 对象返回,注入到 B 中,接着初始化 B,返回 B 对象;
- B 对象返回,注入到 A 中,接着初始化 A,问题就在这儿:
- 5.1. 如接下来初始化 A 无需被代理
5.1.1. exposedObject 返回是 A 原始对象,此时与 C 中被注入 A 都是原始 Bean,完满;
- 5.2. 如接下来初始化 A 须要被代理:
5.2.1. APC 依据缓存查看之前创立 A 时是否被代理过,如已被代理,间接返回原始对象,与 A 原始统一,完满;
5.2.2. 然而,如此时 A 初始化过程中有独特的其余 BeanPostProcessor,对 A 的代理形式有独自解决,则被代理后的 proxy2 与原始 Bean、被注入到 C 中的 A 的 Proxy 均不再统一,抛出异样;
- 总结重点:
- 6.1. 最终起因就是提前裸露的曾经注入到 C 中的 A(无论是否被代理)与起初通过初始化后被代理的 A(proxy2)不再是同一个 Bean;
- 6.2. 因为 Spring 治理 Bean 默认是 Singleton 的,当初呈现了两个 bean,默认状况下无奈决断,因而就抛出了异样。
(2) 各别注解使用不当
- @Respository
- 处理器 PersistenceExceptionTranslationPostProcessor#postProcessAfterInitialization
- 被 @Respository 注解的类在 Spring 启动初始化时存在循环依赖链路中,如果此时 Spring 中开启了 AOP,则必抛出循环依赖异样
- 所以 DAO 层应用时,最好不要引入内部业务逻辑,业务逻辑能够提取到 Manager、Service 层等中,放弃 DAO 污浊
- 案例剖析:见第四节
- @Asyn
- 处理器 AsyncAnnotationBeanPostProcessor#postProcessAfterInitialization
- 被 @Asyn 注解的类在 Spring 启动初始化时存在循环依赖链路中,如果此时 Spring 中开启了 AOP,则必抛出循环依赖异样
- 以上等注解的类使用不当都比拟容易呈现循环依赖,这两个注解同一个父类,造成循环依赖原理一样
- AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization
(3) 存在多个 AutoProxyCreator(APC),呈现多层代理
spring 默认保障一个容器中只能有一个 Aop 的 APC,如过手动增加或者自定义会呈现多个 APC 状况
- InfrastructureAdvisorAutoProxyCreator
- AspectJAwareAdvisorAutoProxyCreator
- AnnotationAwareAspectJAutoProxyCreator
- 三者有就依照优先级笼罩,否则就注册一个,因而始终就只会有一个 APCAopConfigUtils
static {APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class); APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);}
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) {// 因为三个 APC 存在能力父子关系,依照指定注册的 APC 主动调整优先级,从而保障只存在一个 APC // 如未指定 APC,则默认为 InfrastructureAdvisorAutoProxyCreator int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) {apcDefinition.setBeanClassName(cls.getName()); } } return null; }
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition;}
- 存在多个 APC 时,如存在循环依赖,此时触发之前放入三级缓存逻辑
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
- 从而触发多个 APC 的 getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {// 此时如存在多个 APC, 则顺次执行 getEarlyBeanReference 返回多层代理对象 for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject;}
- 最终 proxy2 会被注入到依赖的 Bean 中,即例如:A-proxy2 注入到 B 中存在多个多层代理状况,getEarlyBeanReference 没有问题,然而执行到初始化时
@Overridepublic Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {// 留神这个 Bean 可是原始对象,每个 APC 都缓存本身代理过的类,然而存在多个 APC 时,后续的 APC 缓存确实是代理类的代理 // 即如第二个 APC 是 BeanNameAutoProxyCreator,其缓存的可是 proxy1 的 class,原始类在此 APC 是没被代理过的,// 因而此时会对原始类进行二次代理,产生 Proxy3 Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) {return wrapIfNecessary(bean, beanName, cacheKey); } } return bean;}
// 眼帘返回本次循环依赖最后实例化的结点:A->B->C->A,则此处为 A 的创立流程 // 此时 A 通过 getEarlyBeanReference 生成 A ->proxy2 注入到 C 中,// C 间接实例创立不会触发 getEarlyBeanReference,注入到 B 中 // B 间接实例创立不会触发 getEarlyBeanReference,注入到 A 中 // A 依赖处理完毕,持续初始化 initializeBean 流程 -> postProcessAfterInitialization,返回 proxy3if (earlySingletonExposure) {// 此时获取到的代理类是 proxy2,即曾经注入到依赖类 C 中的代理,因而不为 null Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) {// 多 APC 时,exposedObject 在之前 initializeBean -> postProcessAfterInitialization 作用下返回 proxy3 //proxy3 != bean 不统一,违反了 singletion 准则,因而会抛出循环依赖异样 if (exposedObject == bean) {exposedObject = earlySingletonReference;} else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {...... if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName, "Bean with name'" + beanName + "'has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] 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."); } } }}
8 失常 AOP 代理为何没问
- SmartInstantiationAwareBeanPostProcessor
@Overridepublic Object getEarlyBeanReference(Object bean, String beanName) {Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey);}
// 提前通过 singletonFactory.getObject()创立的代理缓存起来当前,这里如果再次判断须要代理,// 缓存中存在已被代理则间接返回原始 bean,无需再次代理,后续间接获取 earlySingletonReference,// 因而前后代理进去的对象是统一的 @Overridepublic 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;}
解决方案
1 无需代理场景应用原始对象
- 原始对象互相注入没有问题,查看不许要生成代理的类
2 @lazy 解耦
- 原理是发现有 @lazy 注解的依赖为其生成代理类,依赖代理类,从而实现理解耦
- @Lazy 用来标识类是否须要提早加载;
- @Lazy 能够作用在类上、办法上、结构器上、办法参数上、成员变量中;
- @Lazy 作用于类上时,通常与 @Component 及其衍生注解配合应用;
- @Lazy 注解作用于办法上时,通常与 @Bean 注解配合应用;
- DefaultListableBeanFactory#resolveDependency
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); if (Optional.class == descriptor.getDependencyType()) {return createOptionalDependency(descriptor, requestingBeanName); } ...... else {// 解决 @lazy Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName); if (result == null) {result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; }}
ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessarypublic Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);}
ContextAnnotationAutowireCandidateResolver#isLazy// 是否为 @lazy,如果为 @lazy 则创立依赖代理 protected boolean isLazy(DependencyDescriptor descriptor) {for (Annotation ann : descriptor.getAnnotations()) {Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class); if (lazy != null && lazy.value()) {return true;} } .......}
3 抽取公共逻辑
- 业务层面重构,不再相互依赖而是依赖公共模块,并且各个对外业务与外部接口拆分
案例(可间接运行)
1 @Repository 案例剖析
import org.junit.Test;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;import org.springframework.core.env.Environment;import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;import org.springframework.stereotype.Component;import org.springframework.stereotype.Repository;import javax.annotation.Resource;/** * @author: Superizer */@Componentpublic class MainSpringCircularDependencyTester{@Test public void springCircularDependencyTest() {AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringCircularDependencyConfig.class); X x = ac.getBean(X.class); System.out.println("Spring bean X =" + x.getClass().getName()); x.display(); Y y = ac.getBean(Y.class); System.out.println("Spring bean Y =" + y.getClass().getName()); y.display(); Z z = ac.getBean(Z.class); System.out.println("Spring bean Z =" + z.getClass().getName()); z.display(); System.out.println("******************Main********************"); } @Configuration @ComponentScan("com.myself.demo.spring.v5.circular.dependency")// @EnableAspectJAutoProxy @ConditionalOnClass(PersistenceExceptionTranslationPostProcessor.class) static class SpringCircularDependencyConfig{@Bean @ConditionalOnMissingBean @ConditionalOnProperty(prefix = "spring.dao.exceptiontranslation", name = "enabled", matchIfMissing = true) public static PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(Environment environment) {PersistenceExceptionTranslationPostProcessor postProcessor = new PersistenceExceptionTranslationPostProcessor(); boolean proxyTargetClass = environment.getProperty("spring.aop.proxy-target-class", Boolean.class, Boolean.TRUE); postProcessor.setProxyTargetClass(proxyTargetClass); return postProcessor; } } abstract static class A {public abstract A injectSources(); public abstract A self(); public void display(){System.out.println("injectSources:" + injectSources().getClass().getName()); System.out.println("*******************************************************"); } } //X、Y、Z 只有循环依赖中第一个类 X 有注解 @Repository,就会呈现循环依赖异样 // 执行 X 的 singletonFactory.getObject()返回的原对象,然而后边初始化时 // 执行到 PersistenceExceptionTranslationPostProcessor 时独自创立代理逻辑返回的是代理类 //exposedObject = initializeBean(beanName, exposedObject, mbd); @Repository// @Component static class X extends A{@Resource private Y y; @Override public Y injectSources() {return y;} @Override public X self() { return this;} } @Component// @Repository static class Y extends A{@Resource private Z z; @Override public Z injectSources() {return z;} @Override public Y self() { return this;} } @Component// @Repository static class Z extends A{@Resource private X x; @Override public X injectSources() {return x;} @Override public Z self() { return this;} }}
2 多 AutoProxyCreator 场景
import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;import org.junit.Test;import org.springframework.aop.ClassFilter;import org.springframework.aop.MethodMatcher;import org.springframework.aop.Pointcut;import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;import org.springframework.aop.support.AbstractExpressionPointcut;import org.springframework.aop.support.DefaultPointcutAdvisor;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;import org.springframework.stereotype.Component;import javax.annotation.Resource;import java.util.Arrays;/** * @author: Superizer * Copyright (C) 2021 * All rights reserved */@Componentpublic class MainSpringCircularDependencyV2Tester{@Test public void circularDependencyV2Tester() {AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringCircularDependencyConfig.class); A a = ac.getBean(A.class); System.out.println("Spring bean A =" + a.getClass().getName()); a.display(); B y = ac.getBean(B.class); System.out.println("Spring bean B =" + y.getClass().getName()); y.display(); C z = ac.getBean(C.class); System.out.println("Spring bean C =" + z.getClass().getName()); z.display(); System.out.println("******************Main********************"); } @Configuration @ComponentScan("com.myself.demo.spring.v5.circular.dependency.v2") @EnableAspectJAutoProxy static class SpringCircularDependencyConfig {@Bean public DefaultPointcutAdvisor defaultPointcutAdvisor() {DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(); Pointcut pointcut = new AbstractExpressionPointcut() { @Override public ClassFilter getClassFilter() {return (tmp) -> {String name = tmp.getName(); if(name.equals(A.class.getName())) {return true;} return false; }; } @Override public MethodMatcher getMethodMatcher() { return MethodMatcher.TRUE;} }; advisor.setPointcut(pointcut); advisor.setAdvice(new SpringAopAroundMethod()); advisor.setOrder(0); return advisor; } @Bean public BeanNameAutoProxyCreator beanNameAutoProxyCreator() { BeanNameAutoProxyCreator apc = new BeanNameAutoProxyCreator(); apc.setBeanNames("a"); apc.setOrder(-1); apc.setProxyTargetClass(true); return apc; } } abstract static class G {public abstract G injectSources(); public abstract G self(); public void display(){System.out.println("injectSources:" + injectSources().getClass().getName()); System.out.println("*******************************************************"); } } @Component(value = "a") static class A extends G {@Resource private B b; @Override public B injectSources() {return b;} @Override public A self() { return this;} } @Component static class B extends G {@Resource private C c; @Override public C injectSources() {return c;} @Override public B self() { return this;} } @Component static class C extends G {@Resource private A a; @Override public A injectSources() {return a;} @Override public C self() { return this;} } static class SpringAopAroundMethod implements MethodInterceptor {@Override public Object invoke(MethodInvocation methodInvocation) throws Throwable {System.out.println("Aop Before method!"); try {Object result = methodInvocation.proceed(); System.out.println("Aop after method!"); return result; } catch (IllegalArgumentException e) {System.out.println("Aop throw exception!"); throw e; } } }}
总结
呈现循环依赖其实反映代码结构设计上的问题,实践上该当将循环依赖进行分层,抽取公共局部,而后由各个性能类再去依赖公共局部。然而在简单代码中,各个 service、manager 类相互调用太多,总会一不小心呈现一些类之间的循环依赖的问题。可有时候咱们又发现在用 Spring 进行依赖注入时,尽管 Bean 之间有循环依赖,然而代码自身却大概率能很失常的 work,仿佛也没有任何 bug。很多敏感的同学心里必定有些犯嘀咕,循环依赖这种触犯因果律的事件怎么能产生呢?没错,这所有其实都并不是那么天经地义。Spring 曾经为咱们背负了太多,但绝不是偷懒的借口,还是应该标准设计,标准代码,尽量做到从根本上防止这种循环依赖的产生。
$$
Spring 流程图
$$
$$
Spring 流程图
$$
点击立刻收费试用云产品 开启云上实际之旅!
原文链接
本文为阿里云原创内容,未经容许不得转载。