springboot ApplicationContext之finishBeanFactoryInitialization(getBean(),循环依赖)上

本文深入探讨Spring框架如何处理循环依赖的问题,通过详细分析TestA与TestB两个Bean的实例化和初始化过程,揭示Spring是如何利用多级缓存机制来解决这一难题。

定义了以下两个bean(单例),看看spring是怎么生产出来提供给我们用的。

@Service
public class TestA {

    @Autowired
    private TestB testB;
}

@Service
public class TestB {

    @Autowired
    private TestA testA;
}

这个大家一看就知道怎么回事,我想要讲的是什么。循环依赖么,看上去是怎么也绕不开的话题,平常开发中会在不经意之间就使用上了,我们就从这里出发看看,spring是怎么解决的,但是重点是要了解bean的实例化、初始化流程。

还是比较喜欢图,因为比文字短,且看的清楚。
![bean的获取流程](https://img-blog.csdnimg.cn/d578acbd94e24d2a82ab6233c8810e20.png
getSignleton方法的流程
图虽然清晰但是也要加文字的,不然也不能说的很清楚。

图一中绿色加数字的是执行的方法,并按照数字从小到大顺序执行,箭头对应的方块是重要的参数,注意本文撇去了Aop相关的步骤。

TestA,获取流程:

  1. getBean,获取bean入口
  2. doGetBean
  3. getSingleton,重要方法(解决循环依赖),这个时候会执行到①,会去一级缓存(singletonObjects)获取为null,但是isSingletonCurrentlyInCreation(beanName)的方法为false,没有在创建中,返回是null.
  4. markBeanAsCreated,标记当前bean在创建中
  5. getSingleton,注意这个方法跟3不是同一个,参数不一样,重要参数singletonFactory
	getSingleton(beanName, () -> {
		try {
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			destroySingleton(beanName);
			throw ex;
		}
	});
	5.1.1 beforeSingletonCreationation,标记为TestA创建中
	5.1.2 singletonFactory.getObject(),或执行传入的代码
  1. createBean,开始创建bean

  2. resolveBeforeInstantiation,实例化前的操作,先忽略

  3. doCreateBean

  4. createBeanInstance,实例化一个TestA。《springboot启动bean加载处理器ConfigurationClassPostProcessor 四(@bean注解)

       a:  instantiateUsingFactoryMethod 工厂方式创建bean(之前在@bean注解中介绍过)
       b:  instantiateBean
    
  5. addSingletonFactory, 向三级缓存对象singletonFactories,放入一个ObjectFactory<?> singletonFactory,对应的key=TestA

       addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))
    

11.populateBean, 这个时候如果不存在循环依赖,用不到三级缓存(三级缓存是存在TestA,但是用不到),会直接继续完成初始化

	TestA                                           TestB
	--------------------------------------------------------------
	populateBean(A)       
	-------------------------------------------------------------
	                                                getBean(B)
	                                                 ...
	                                                populateBean(B)     
	--------------------------------------------------------------
	getBean(A)
	getSingleton(A)
	-------------------------------------------------------------
		                                             initializeBean(B)
		                                                 B完成实例化
    -------------------------------------------------------------
	initializeBean(A)
	
	赋值TestA的属性TestB,这个时候会重新到(方法:1)开始执行,一直执行到TestB的 populateBean,
	需要赋值TestA,又回到(方法:1),执行到(方法:3),这个时候会执行到getSingleton ③,
	获取到TestA的singletonFactory返回的bean,放入到二级缓存earlySingletonObjects,
	并从三级缓存中移除,返回对象TestA(未初始化)。这个时候 TestB会对A进行赋值,并完成初始化。
  1. initializeBean,实例化TestA
  2. invokeAwareMethods
  3. invokeInitMethods
  4. addSingleton, 把TestA放入到一级缓存singletonObjects中,并冲二级缓存中移除

看看整个过程中,一、二、三级缓存的动态吧。
在这里插入图片描述
总结来说,spring解决循环依赖的处理是,把bean的实例化、初始化分开所以如果bean是构造注入的,那怎么也玩不转了

接下来就是,搜索一大把问题,

spring可以不要三级缓存吗?,看上去是可以的吧,只要两个缓存就可以了,把TestA创建好后直接放入到二级缓存,TestB要的时候直接拿。
看看我后续的解释吧。。。《springboot ApplicationContext之finishBeanFactoryInitialization(getBean())下

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 2025-06-10T23:02:17.513+08:00 ERROR 31450 --- [ main] o.s.boot.SpringApplication : Application run failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bookController': Injection of resource dependencies failed at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:323) ~[spring-context-6.0.13.jar:6.0.13] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1416) ~[spring-beans-6.0.13.jar:6.0.13] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:597) ~[spring-beans-6.0.13.jar:6.0.13] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520) ~[spring-beans-6.0.13.jar:6.0.13] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:325) ~[spring-beans-6.0.13.jar:6.0.13] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.13.jar:6.0.13] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:323) ~[spring-beans-6.0.13.jar:6.0.13] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-6.0.13.jar:6.0.13] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973) ~[spring-beans-6.0.13.jar:6.0.13] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:950) ~[spring
最新发布
06-11
<think>嗯,用户的问题是关于SpringBoot应用启动时出现的"BeanCreationException:Injectionofresourcedependenciesfailed"报错,具体是在BookController组件中。这个问题看起来是依赖注入失败导致的。用户之前已经提供了几个引用内容,这些引用非常有价值。引用[1]提到了缺少@Repository注解的问题,引用[2]指出可能是Bean定义缺失,引用[3]提供了详细的异常栈信息,引用[4]则涉及了SpringBoot的自动配置机制。这些都可以帮助找到解决方案。用户遇到的这个错误在Spring框架中很常见,通常有以下几种情况:-Bean没有被Spring正确扫描到,比如漏了@Repository或@Service注解-包扫描配置有问题,导致需要注入的Bean没有被加载-接口和实现类名称混淆,在注入接口时找不到实例-复杂的循环依赖问题-bean覆盖或条件配置等导致的配置冲突针对用户的具体情况,应该从基础检查开始:-检查BookController是否在组件扫描范围内,是否有@Controller或@RestController-确保所有被注入的依赖都有对应的Spring注解(@Service/@Repository)-确保接口的实现类存在且正确注解-检查是否有多余的@ComponentScan配置或手动配置冲突-查看完整的错误堆栈,确定哪个字段注入失败从错误信息来看,"bookService"字段注入失败,所以应重点检查:-BookService接口是否有实现类?-实现类是否有@Service注解?-如果BookService接口有多个实现,应该使用@Qualifier另外值得注意的是引用[4]中提到的自动配置机制。虽然用户的问题看起来不需要深入事务管理器的配置,但还是需要确认是否添加了必要的starter依赖,比如spring-boot-starter-data-jpa或spring-boot-starter-jdbc等。在提供解决方案时,需要分步骤说明,从最简单的可能性开始排查:1.检查基础注解(@Repository/@Service)是否缺失2.检查扫描路径是否正确3.考虑是否可能存在大小写或命名问题4.如果使用自动配置和手动配置并存,需要确保没有冲突5.在特殊情况下可能需要检查具体字段名和bean名称匹配关系最后在建议中强调查阅完整堆栈的重要性,因为错误文本可能有更关键的上下文信息。在生成相关问题部分,可以围绕依赖注入机制、自动配置原理等问题来设计。</think> 根据错误 `BeanCreationException: Injection of resource dependencies failed`,以下是针对 `BookController` 的完整解决方案,结合日志中的具体线索和 Spring Boot 机制逐步排查: --- ### **错误原因分析** 关键报错信息: ``` Injection of resource dependencies failed No qualifying bean of type [...] expected at least 1 bean which qualifies ``` **核心问题**:Spring 容器无法找到 `@Resource` 或 `@Autowired` 注解标记的依赖项对应的 Bean。对于 `BookController`,可能是注入的 `BookService` 或关联组件未被 Spring 管理。 --- ### **解决方案步骤** #### **1. 检查依赖项的注解声明** - **缺失注解是常见原因**: 确保被注入的字段(如 `BookService`)的实现类有 **`@Service`** 注解,DAO 层有 **`@Repository`** 注解。 ```java // BookServiceImpl.java @Service // 必须添加 public class BookServiceImpl implements BookService { ... } ``` - **接口注入需明确实现**: 若 `BookController` 中注入了接口(如 `BookService`),必须有对应的实现类并添加 `@Service`。 #### **2. 验证组件扫描路径** - **主启动类位置**: 确保 `@SpringBootApplication` 主类位于**根包**(如 `com.example.app`),且 `BookController`、`BookService` 在其子包中。 - **手动指定扫描路径**(如分包不规范): ```java @SpringBootApplication @ComponentScan(basePackages = {"com.example.controller", "com.example.service"}) public class Application { ... } ``` #### **3. 检查依赖字段命名冲突** - 若使用 `@Resource(name = "beanName")` 指定 Bean 名称,需确认目标 Bean 的命名一致: ```java // BookController.java @Resource // 默认按字段名bookService匹配 private BookService bookService; ``` #### **4. 排除循环依赖** - **症状**:Bean A 依赖 B,同时 B 依赖 A。 - **解决**: - 使用 `@Lazy` 延迟加载其中一个 Bean: ```java @Controller public class BookController { @Lazy @Resource private BookService bookService; } ``` - 重构代码,提取公共逻辑到第三方组件。 #### **5. 检查依赖库配置** - **MyBatis 等整合问题**: 若使用 MyBatis,确认 Mapper 接口是否被扫描: ```java @MapperScan("com.example.mapper") // 在启动类添加 ``` - **JPA 配置**: 检查 `Repository` 接口是否继承 `JpaRepository` 并位于扫描路径。 --- ### **快速验证流程** 1. 在 `BookServiceImpl` 类上添加 `@Service`。 2. 确认主启动类包路径覆盖所有组件。 3. 检查日志中完整堆栈,定位具体无法注入的字段名(如 `bookService`)。 4. 重启应用,观察是否解决。 > **📌 关键提示**:Spring Boot 的自动配置依赖于注解扫描。若手动定义 Bean(如 `@Bean` 方法),需避免与注解扫描冲突[^4]。 --- ### **相关问题** 1. **如何诊断 `NoSuchBeanDefinitionException` 的具体原因?** 2. **Spring Boot 中 `@ComponentScan` 与 `@SpringBootApplication` 的扫描优先级是什么?** 3. **循环依赖解决方案中,`@Lazy` 注解的工作原理是什么?** 4. **如何整合 MyBatis 时避免 Mapper 接口注入失败?** 5. **Spring Boot 启动时 Bean 加载顺序如何影响依赖注入?** [^1]: 组件缺失 `@Repository` 导致注入失败 [^2]: Bean 未定义的根本原因分析 [^3]: `@Resource` 注入的底层处理流程 [^4]: Spring Boot 自动配置与事务管理器机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值