3.0、Spring源码学习:认识 AbstractApplicationContext.refresh()

深入剖析Spring框架中refresh()方法的关键作用与内部机制,包括初始化上下文、beanFactory的准备与后置处理,以及单例bean的实例化过程。

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

前言

体能状态先于精神状态,习惯先于决心,聚焦先于喜好

所有初始化 Spring 容器的操作都会调用这个 refresh() 方法

上一篇文章提到类这一点 2、Spring源码学习:认识加载 xml 文件的 ClassPathXmlApplicationContext
从其定义(org.springframework.context.ConfigurableApplicationContext.refresh() )来看,用于加载或者刷新来自 XML 文件、properties 文件 或者关系数据库的配置
是一个在开始阶段调用的方法,简单说,其负责加载的(non-lazy-init)单例 bean 要么全部成功,要么全部失败
refresh()的实现方法位于 org.springframework.context.support.AbstractApplicationContext

//进去 debug 跟踪 refresh()方法即可
ApplicationContext application=new ClassPathXmlApplicationContext("classpath:applicationContext2.xml");

refresh() 方法 修改 active flag 为true

refresh() 内部调用方法 org.springframework.context.support.AbstractApplicationContext.prepareRefresh(
准备刷新上下文,设置开始时间和 active flag 未true
这个 active flag 是作为 是否需要刷新 applicatonContext 对象的标志

提供一个新入口

GenericApplicationContext

因为 在 Spring 容器加载的初期, refresh()方法都会被调用,本文开启了另一个入口
org.springframework.context.support.GenericApplicationContext
这个类可以为我们提供更灵活的配置,即手动去设置配置文件,以及手动调用 refresh() 方法

@Test
public void testRefresh() {
	//org.springframework.context.support.GenericApplicationContext
	GenericApplicationContext ctx = new GenericApplicationContext();
	XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
	//文件位于 src/main/resources 目录下
	xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
	PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
	propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
	//从这里进去源码
	ctx.refresh();
	//MyBean myBean = (MyBean) ctx.getBean("myBean");
}
refresh() 的 UML 关系图

用于表示 refresh() 方法与其他接口或类关系的UML 关系图

在这里插入图片描述

进入源码

refresh()

简单来说,Spring 创建一个webApplicationContext 之后,需要进行 refresh()动作,其中最主要的就是加载指定的 Spring 的配置文件。
并且将在这个方法中修改if (!cwac.isActive()) {判断是是否激活标志 传送门
加载或者刷新持久化对配置信息,该配置可能是一个XML 文件,properties 文件,或者关系型数据库
由于这是一个启动阶段对方法,如果启动失败对化,所有已经被创建对单例bean都需要被销毁,以避免不稳定对资源出现.换句话说,这个方法被调用完毕后,要么所有的单例bean被加载到(上下文),要么一个都没有被加载.

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		//刷新之前的准备工作,包括设置启动时间,是否激活标识位,初始化属性源(property source)配置
		//这里将beanFactory 是否激活修改为 true
		prepareRefresh();
		
		// 通知子类刷新自己的 BeanFactory (将 bean定义转化为 beandefination 对象,添加到 BeanFactory,但是未实例化)
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		// 准备 beanFactory 供上下文使用(context,看具体实现的子类,设置ClassLoader,Spel解析器,忽略部分接口的注入,实例化部分类,添加后置处理器-postprocess)
		prepareBeanFactory(beanFactory);
		try {
			// Allows post-processing of the bean factory in context subclasses.
			//模版方法:允许子类对 beanFactory 进行 后续处理(post-processing),webXmlApplicationContext 设置了scope等参数
			postProcessBeanFactory(beanFactory);
			// 实例化、注册 beanFactory 中 扩展了 BeanFactoryPostProcessor 接口的beanFactory 处理器,比如ConfigurationClassPostProcessor 用于  @Configuration、@Bean、@PropertySource 标签的处理,在普通普通 bean 实例化前调用
			invokeBeanFactoryPostProcessors(beanFactory);
			
			// 实例化、注册、调用 beanFactory中扩展了 BeanPostProcessor 的后置处理器bean,如 AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			// 初始化 消息国际化工具类 MessageSource
			initMessageSource();

			// Initialize event multicaster for this context.
			// 初始化 事件 组播处理器
			initApplicationEventMulticaster();

			// 模版方法:初始化子类上下文中被特殊定义的bean;Spring 提供的是一个空方法,供子类扩展
			onRefresh();

			// 检查并注册事件监听器bean,广播early application events
			registerListeners();

			// 实例化所有non-lazy-init(非懒加载的) 的单例bean,实例化的过程各种 BeanPostProcessor 开始起作用
			finishBeanFactoryInitialization(beanFactory);

			// 最后一步:发布相应的事件-上下文刷新工作完成了
			//清除上下文资源缓存(如扫描中的ASM元数据);初始化上下文的生命周期处理器,并刷新(找出Spring容器中实现了Lifecycle接口的bean并执行start()方法)。发布ContextRefreshedEvent事件告知对应的ApplicationListener进行响应的操作
			finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - "+"cancelling refresh attempt: " + ex);
			}
			//catch逻辑:销毁已经被创建的单例 bean,避免不稳定资源的出现
			destroyBeans();

			//catch逻辑:重置 ‘active’标志
			cancelRefresh(ex);

			// 继续抛出异常给调用者
			throw ex;
		}

		finally {
			// finally逻辑:重置 Spring's core 的缓存信息,因为我们可能不会再用到这些单例bean的元数据信息了
			resetCommonCaches();
		}
	}
}

对 refresh()中关键代码的解读

prepareRefresh();

org.springframework.context.support.AbstractApplicationContext.prepareRefresh(
准备刷新上下文,设置开始时间和 active flag 以便于开始任何配置文件的初始化动作。
这里的 active flag 默认为 false,一般作为 新创建 webApplicationContext 对象是否刷新的标志 如 传送门

	protected void prepareRefresh() {
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);

		if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}
		// 初始化任何通配符匹配的文件,你可以任务该方法之后,操作系统相关、JVM相关信息都被加载了,准备的参数可以通过 getEnvironment() 来获取
		initPropertySources();

		//检测必须参数的非空性,通过 ConfigurablePropertyResolver#setRequiredProperties 设置
		//debug发现,默认情况该方法没啥用-没有需要检测的从参数
		getEnvironment().validateRequiredProperties();

		//实例化LinkedHashSet,允许早期的 ApplicationEvents 集合在 multicaster 可用时被发布
		this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
	}
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

本方法用于将配置文件中的bean加载到 beanDefination 中,然后将beanDefination 放入到 beanFactory,但是需要注意到是,只是加载了 bean到定义信息,还没有进行注入、实例化等工作.
限于篇幅,单独起一篇文章
3.2ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

在这里插入图片描述

prepareBeanFactory(beanFactory);

本方法用于设置 beanFactory 的类加器、SpEL解析器、bean的属性编辑器、特殊接口的免自动注入和特殊类型的指定注入,环境对象设置为单例
限于篇幅,单独写一篇文章
3.3 prepareBeanFactory(beanFactory);

postProcessBeanFactory(beanFactory);

代码运行到这里,Spring 上下文已经将基本的 beanFactory 和 beanDefination 准备好了。
该方法允许子类对 beanFactory 进行进一步的后置处理;
该方法默认为空方法,允许子类根据需要选择是否覆盖重写,体现了模板方法模式;
具体来说,如果子类为 ClassPathApplicationContext ,那么这个方法依旧是一个空方法

后置处理器的概念

Spring 中的事件允许不同的bean之间的信息交换,体现了观察者模式。
事件可以是既定的事件,也可以是自定义的事件,事件发布后由多播处理器统一处理,对所有的监听器进行事件转发——该过程可以是多线程异步的。更详细的介绍可以看下文
Spring中的事件发布和处理

invokeBeanFactoryPostProcessors(beanFactory);:修饰 beanDefination

该方法与 beanFactory 相关的后置处理器的初始化、注册和调用
该方法主要用于处理 beanDefination,此外进一步对 configuration 修饰的类中标注的 beandefination 进行发掘

for (BeanDefinitionRegistryPostProcessor postProcessor : registryPostProcessorBeans) {
	postProcessor.postProcessBeanDefinitionRegistry(registry);
}

在 postProcessBeanDefinitionRegistry 方法内部是对配置类对具体处理——发掘使用注解注册的beanDefinition

processConfigBeanDefinitions(registry);

重点关注 ConfigurationClassPostProcessor 这个后置处理器,用于处理代码中的
@Component、@Configuration、@ComponentScan 、@Bean 注解的处理
可以理解为,Spring Bean 可以来自于 XML定义,也可以来自 Java 代码中的定义.

 @Configuration
 @PropertySource("classpath:/com/myco/app.properties")

可以自己实现 BeanFactoryPostProcessor 接口,该接口注解中有描述:允许修改 beanFactory中的 beanDefination 但是千万不要进行任何可能导致bean实例化的操作,一来破坏了Spring的组织结构,而来会导致注解注入无法生效——对于注解逻辑到处理还没到呢——除非你是用xml注入——所以千万不要在自定义到 BeanFactoryPostProcessor 到逻辑中进行任何实例化的操作.

自定义处理器和默认处理器可以有一个优先级设置——实现 PriorityOrdered 接口,在调用处理器时会按照排序进行

registerBeanPostProcessors(beanFactory);:修饰bean

该方法会实例化、注册、调用 beanFactory中扩展了 BeanPostProcessor 的后置处理器bean,如 AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
Must be called before any instantiation of application beans(必须在应用上下文实例化前调用这些处理器)
在这里插入图片描述
该方法仅仅用于与 普通 Spring bean 相关的后置处理器的处理
允许自己实现 BeanPostProcessor ,参与 bean 实例化过程的处理,允许实现 PriorityOrdered 可口以实现优先级处理

延伸阅读:Spring 中关于 多个BeanPostProcessor代理同个Bean的问题

initMessageSource();

配置 i18n 即国际化资源
i18n项目源码

initApplicationEventMulticaster();

初始化应用的事件多播处理器,事件发布后由该处理器统一进行处理

onRefresh();

默认是空实现,给子类扩展的空间,又是模版方法模式呦

registerListeners();

注册监听器,比如上下文完成刷新事件
也可以自定义监听器,由于多播处理器并不会区分监听器,所以需要监听器自己区分不同的事件进行针对性处理。

可以自定义监听器
Spring中的事件发布和处理

finishBeanFactoryInitialization(beanFactory);

实例化所有未实例化且非懒加载的单例对象

/**
* ConfigurableListableBeanFactory beanFactory
* Instantiate all remaining (non-lazy-init) singletons.
/* 
beanFactory.preInstantiateSingletons();

创建 bean时会先判断该bean是否已经创建或者正在创建

  • 已经创建 DefaultSingletonBeanRegistry.java
/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
  • 正在创建 DefaultSingletonBeanRegistry.java
/** Names of beans that are currently in creation (using a ConcurrentHashMap as a Set) */
	private final Map<String, Boolean> singletonsCurrentlyInCreation = new ConcurrentHashMap<String, Boolean>(16);

如果开始初始化一个 bean,会将这个name放入下面的map中,value=true

protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.containsKey(beanName) &&
				this.singletonsCurrentlyInCreation.put(beanName, Boolean.TRUE) != null) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

创建完毕后要移除

protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.containsKey(beanName) &&
				!this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}
  • 创建一个bean之后的处理
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

  • 判断循环依赖的逻辑

已在创建的bean和当前创建的名字一样 AbstractBeanFactory.java

/** Names of beans that are currently in creation */
	private final ThreadLocal<Object> prototypesCurrentlyInCreation =
			new NamedThreadLocal<Object>("Prototype beans currently in creation");

  • 已经至少被创建了一次 AbstractBeanFactory.java
/**
	 * Names of beans that have already been created at least once
	 * (using a ConcurrentHashMap as a Set)
	 */
	private final Map<String, Boolean> alreadyCreated = new ConcurrentHashMap<String, Boolean>(64);

finishRefresh();
protected void finishRefresh() {
		// 初始化上下文的生命周期
		initLifecycleProcessor();

		// Propagate refresh to lifecycle processor first.
		getLifecycleProcessor().onRefresh();

		// 发布事件 ContextRefreshedEvent ,监听器可以收到这个事件
		publishEvent(new ContextRefreshedEvent(this));

		// Participate in LiveBeansView MBean, if active.
		LiveBeansView.registerApplicationContext(this);
	}
初始化期间出现异常
destroyBeans();

catch逻辑:销毁所有已经创建的单利 bean

cancelRefresh(ex);

catch逻辑:重置 ‘active’标志

参考资料

[1]、https://www.jianshu.com/p/bede81fad45c

2025-03-14 14:37:19 [] [] [main] ERROR [org.springframework.boot.SpringApplication ] >>> Application run failed org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [smj.pc.PcRunApplication] at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:179) at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:416) at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:788) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:606) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) at smj.pc.PcRunApplication.main(PcRunApplication.java:41) Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name &#39;documentCenterYml&#39; for bean class [com.csair.office.oesic.base.
03-15
<think>嗯,用户遇到了Spring Boot启动失败的问题,具体是BeanDefinitionStoreException,特别是ConfigurationClassParser解析配置类时的冲突。我需要根据提供的引用内容来整理解决方案。首先,引用2和3都提到了默认包的问题,使用默认包可能导致组件扫描覆盖所有类,引发冲突。所以第一个步骤应该是检查所有类是否有明确的包声明,确保没有类在默认包中。 接下来,引用5提到版本兼容性问题,可能Spring Cloud和Spring Boot版本不匹配。用户可能需要检查依赖的版本是否一致,特别是Spring Cloud和Spring Boot的兼容性,参考官方文档的版本对应关系。 然后,引用4提到自动配置和@Import的处理流程,可能配置类重复定义或冲突。需要检查是否有多个配置类定义了相同的Bean,或者第三方库引入了冲突的配置。使用@ComponentScan时指定具体包路径,避免扫描到不必要的类。 另外,Bean的重复定义也是一个可能的原因,引用1中的报错可能与Bean定义冲突有关。检查是否有重复的@Component、@Service等注解,或者配置类中多次声明同一个Bean。使用@Bean(name="")指定不同名称,或者排除冲突的依赖。 最后,清理和重新编译项目,有时候旧的编译文件可能导致问题,执行mvn clean install或gradle clean build,确保IDE正确识别所有变更。如果问题依旧,查看完整堆栈跟踪,定位具体出错的配置类和行号,结合日志分析具体冲突原因。</think>### Spring Boot启动失败解决方案:BeanDefinitionStoreException与配置类冲突 当Spring Boot应用启动时出现`BeanDefinitionStoreException`异常,特别是涉及`ConfigurationClassParser`的配置类解析问题时,通常由以下原因导致: #### 1. 检查默认包问题(关键因素) - **现象**:未声明包路径的类会处于"default package",导致Spring Boot组件扫描异常 - **解决方案**: 1. 确认所有Java类都包含`package`声明 2. 确保启动类位于顶层包路径下 3. 显式指定组件扫描路径: ```java @SpringBootApplication(scanBasePackages = "com.yourpackage") ``` 引用Spring Boot官方文档:"当类没有包声明时,会导致组件扫描读取所有jar中的每个类,这应该避免"[^2][^3] #### 2. 版本兼容性检查 - **现象**:依赖库版本冲突导致配置解析失败 - **解决方案**: 1. 检查`pom.xml`/`build.gradle`中的依赖版本 2. 确保Spring Boot与Spring Cloud版本匹配 3. 使用官方版本对应表: ``` Spring Boot 2.7.x → Spring Cloud 2021.0.x Spring Boot 3.0.x → Spring Cloud 2022.0.x ``` 4. 执行`mvn dependency:tree`排查冲突 引用实际案例:"版本不匹配会导致Bean定义存储异常,需参考官网版本对应关系"[^5] #### 3. 配置类冲突检测 - **现象**:多个配置类定义相同Bean导致冲突 - **解决方案**: 1. 检查所有`@Configuration`类中的Bean定义 2. 使用唯一Bean名称: ```java @Bean(name = "uniqueBeanName") ``` 3. 排查第三方库中的自动配置类 4. 在`application.properties`中添加: ```properties spring.main.allow-bean-definition-overriding=true ``` #### 4. 注解使用规范 - **常见错误模式**: ```java // 错误示例:重复扫描导致冲突 @SpringBootApplication @ComponentScan({"com.packageA", "com.packageB"}) public class Application { ... } ``` - **修正方案**: ```java // 正确示例:限定扫描范围 @SpringBootApplication(scanBasePackages = "com.mainpackage") public class Application { ... } ``` #### 5. 构建清理与IDE缓存 执行以下命令清除构建残留: ```bash mvn clean install -U # 或 gradle clean build --refresh-dependencies ``` ### 典型错误日志分析 当出现`ConfigurationClassParser`相关异常时,应关注日志中的关键信息: ``` Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name &#39;xxxController&#39; for bean class [com.example.XxxController] conflicts with existing, non-compatible bean definition of same name and class [com.otherpackage.XxxController] ``` 表示存在同名Bean定义冲突,需要检查重复的类定义或包扫描范围过宽。 ### 调试建议 1. 启用调试模式: ```properties logging.level.org.springframework.context.annotation=DEBUG ``` 2. 分析`ConfigurationClassPostProcessor`的解析过程 3. 检查`BeanDefinitionRegistry`中的注册记录 通过以上步骤,可系统性地排查和解决配置类解析导致的启动失败问题。如果问题仍未解决,建议提供完整的异常堆栈信息进行进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值