spring使用@Autowired @Lazy 注解 解决循环依赖

本文讲述了在Spring项目中遇到的启动错误,源于循环依赖问题。通过分析,Spring解决循环依赖的方式被详细解释,以及如何使用`@Autowired`和`@Lazy`注解避免这种问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在启动项目时报错:org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘colorController’: Unsatisfied dependency expressed through field ‘projectService’; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘projectService’: Bean with name ‘jobService’ has been injected into other beans [ProcessService,ProcessManualService] 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的循环依赖问题。
业务代码是在spring的事务中引入了自身的依赖,导致自己调用自己的时候通过@Autowired是进行引入,但是查询发现这个service被其他的controller层调用,而其他的controller层调用的这个代码又引起了循环依赖,所以导致项目启动报错。
那么,什么是循环依赖呢?
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:
在这里插入图片描述
Spring是怎么解决循环依赖的呢?
1.Spring首先从一级缓存singletonObjects中获取。

2.如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。

3.如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取.

4.如果从三级缓存中获取到就从singletonFactories中移除,并放入earlySingletonObjects中。其实也就是从三级缓存移动到了二级缓存。

认识@Autowired 注解
@Autowired 是 Spring 框架中最常用的注解之一,作用是自动注入依赖。在 Spring 容器中,当我们需要使用某个类的时候,如果这个类需要依赖于其他类,在使用 @Autowired 注解的情况下,Spring 就会自动查找并注入所需的依赖。

具体来说,当一个 Spring Bean 类中有 @Autowired 注解标注的属性时,Spring 容器会扫描整个应用程序上下文以找到匹配的 bean 并注入到这个属性中。@Autowired 注解也可以标注在 setter 方法、构造方法和方法参数上。

用法
当使用 @Autowired 注解时,Spring 会自动扫描项目中被 @Component 或其衍生注解标注的类,并将其注册为 Bean。然后,当需要使用某个 Bean 时,Spring 会检查该 Bean 的字段、Setter 方法或者构造方法中是否有被 @Autowired 注解标注的依赖需求,如果有,Spring 会自动按照类型进行注入。

但是,在实际编程中,除开两个单纯的bean有可能产生循环依赖外,任何交给spring管理的bean都可能产生循环依赖,有的时候由于业务逻辑的处理,导致service层需要自己注入,因此,产生了循环依赖问题,正确的解决方式,是在加载的时候配合@Lazy 注解来使用,让这个注入的类延迟加载,等真正在调用他的时候再去加载他,类似代码如下:

@Lazy
private ClassA classA;
@Autowired
@Lazy
private ClassB classB;

在这里插入代码片
<think>好的,我现在需要解决用户提出的关于Spring@Autowired注解导致的循环依赖问题。首先,我得回忆一下Spring框架处理依赖注入的机制,尤其是循环依赖的情况。用户已经提供了一些引用内容,我需要结合这些信息来组织回答。 首先,循环依赖是什么?当两个或多个Bean相互依赖,比如A依赖B,B又依赖A,这时候Spring在初始化的时候可能会遇到问题。用户提到的@Autowired注解在这种情况下可能导致循环依赖,因为Spring默认是通过构造函数注入还是setter注入?我记得Spring有三种循环依赖解决方案,但只支持单例作用域的Bean,并且如果是构造函数注入的话可能无法解决。 接下来,我需要分析原因。根据引用[1],@Autowired本身不解决循环依赖,而是依靠Spring容器的机制。所以问题产生的原因可能是因为Bean的创建顺序或者作用域的问题。比如,如果两个Bean都使用构造函数注入,那么Spring无法提前暴露一个尚未完全初始化的Bean,导致失败。 解决方案方面,引用[2]提到使用@Lazy延迟加载,或者调整注入方式,比如用setter注入代替构造函数注入。另外,可能还可以使用ApplicationContext.getBean()手动获取,但这可能不太推荐。Spring的三级缓存机制是如何处理这个问题的?需要解释一下三级缓存:一级缓存存放完整Bean,二级缓存存放早期暴露的对象,三级缓存存放Bean工厂。当创建A的时候,它会被提前放入三级缓存,然后在注入B时,B开始创建,同样需要A,这时Spring会发现A正在创建中,于是从三级缓存中获取早期的A对象,从而完成B的创建,接着A也能完成初始化。 可能还需要提到,Spring只能解决单例模式下通过setter或字段注入的循环依赖,而原型作用域或者构造函数注入无法解决。用户的问题中,示例代码是字段注入,所以可能属于可解决的情况,但如果配置不当,比如混合使用构造函数注入,还是会有问题。 另外,异常情况如引用[3]提到的NoSuchBeanDefinitionException,可能和循环依赖无关,而是其他配置问题,但需要区分。同时,引用[4]提到设计应用程序时应注意避免循环依赖,提高系统可用性,所以除了技术解决方案,还应考虑代码结构优化,比如使用接口、拆分业务逻辑等。 在组织回答时,需要分点说明原因和解决方案,结构清晰。可能还需要提醒用户使用Spring Boot 2.6之后默认禁止循环依赖,需要通过配置开启。同时,结合用户提供的引用,适当添加引用标识,比如在提到三级缓存时引用[1],提到@Lazy时引用[2]。 最后,生成相关问题,比如如何检测循环依赖、不同作用域下的处理差异、Spring Boot的默认配置等。这样用户可以有进一步的学习方向。</think>### Java@Autowired循环依赖解决方案与原因分析 #### 原因分析 1. **循环依赖的本质** 当两个或多个Bean互相依赖时,例如$BeanA$依赖$BeanB$,而$BeanB$又依赖$BeanA$,Spring容器在初始化时会因无法确定创建顺序而陷入死锁[^2]。例如: ```java @Service public class A { @Autowired B b; } @Service public class B { @Autowired A a; } ``` 2. **Spring的依赖注入机制限制** - **构造函数注入**:若循环依赖的Bean均通过构造函数注入,Spring无法解决(容器启动时直接报错`BeanCurrentlyInCreationException`)[^1]。 - **Setter/字段注入**:Spring通过**三级缓存机制**支持单例作用域(Singleton)的循环依赖,但原型作用域(Prototype)不支持。 3. **作用域与代理模式的影响** 若Bean涉及AOP代理(如使用`@Async`或`@Transactional`),需通过`@Lazy`延迟加载或调整代理生成方式(如`@Scope(proxyMode = ...)`)避免代理对象导致的依赖问题[^2]。 --- #### 解决方案 1. **调整注入方式** - **优先使用Setter/字段注入**替代构造函数注入,允许Spring在Bean未完全初始化时提前暴露引用。 - 示例: ```java @Service public class A { private B b; @Autowired public void setB(B b) { this.b = b; } // Setter注入 } ``` 2. **使用@Lazy注解延迟加载** 对其中一个依赖添加`@Lazy`,延迟实际代理对象的生成: ```java @Service public class A { @Autowired @Lazy // 延迟初始化B的代理对象 private B b; } ``` 3. **重构代码设计** - **提取公共逻辑**到第三方Bean中,打破直接循环依赖。 - 使用**接口隔离**,通过中间接口解耦具体实现。 4. **配置Spring容器** - 若使用Spring Boot 2.6+,默认禁止循环依赖,需在配置中添加: ```properties spring.main.allow-circular-references=true ``` --- #### Spring三级缓存机制简析 1. **一级缓存(Singleton Objects)**:存储完全初始化后的Bean。 2. **二级缓存(Early Singleton Objects)**:存储提前暴露的Bean(已实例化但未填充属性)。 3. **三级缓存(Singleton Factories)**:存储Bean的工厂对象,用于生成早期引用。 当$BeanA$依赖$BeanB$时,Spring会: - 创建$BeanA$实例 → 放入三级缓存。 - 填充$BeanA$属性时发现需要$BeanB$ → 创建$BeanB$实例。 - $BeanB$填充属性时从三级缓存获取$BeanA$的早期引用 → 完成$BeanB$初始化。 - 最终完成$BeanA$的初始化[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值