环球快消息!Spring循环依赖分析与图解

阿里开发者   2023-05-18 15:04:01

点击链接阅读原文,获取更多技术内容:

本篇不仅仅是介绍Spring循环依赖的原理,而且给出Spring不能支持的循环依赖场景与案例,对其进行详细解析,同时给出解决建议与方案,以后出现此问题可以少走弯路。

作者 | 刘斌(蔆素)


(资料图片)

来源 | 阿里开发者公众号

背景

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通过推断构造方法后,通过反射调用构造函数创建的对象

1、什么情况下出现循环依赖

并非使用者手动去getBean才会加载并初始化,而是框架启动时进行加载

Spring创建Bean - #DefaultListableBeanFactory#preInstantiateSingletons@Overridepublic void preInstantiateSingletons() throws BeansException {        //......        ListbeanNames = 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 来处理

阿里云开发者社区,千万开发者的选择。百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,尽在:

相关资讯
最新资讯