SpringFactoriesLoader在Springboot中的作用

SpringFactoriesLoader在SpringBoot的作用
本文深入探讨SpringFactoriesLoader在SpringBoot启动过程中的关键角色,包括初始化ApplicationContextInitializer、ApplicationListener等组件,以及自动化配置的实现机制。

SpringFactoriesLoader类的主要作用是通过类路径下的META-INF/spring.factories文件获取工厂类接口的实现类,初始化并保存在缓存中,以供Springboot启动过程中各个阶段的调用。Spring的自动化配置功能,也与此息息相关。本文有一部分会以spring整合redis为示例,讲述SpringFactoriesLoader在Spring自动化配置中的作用。

版本: 2.0.6.RELEASE


SpringBoot启动简要流程图

SpringBoot启动流程

原始大图链接

应用初始化ApplicationContextInitializer

Springboot启动过程中第一次调用SpringFactoriesLoader类是为了初始化SpringApplication的initializers成员变量(ApplicationContextInitializer类型)。

//构造SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	//通过SpringFactoriesLoader获取ApplicationContextInitializer实现类并设置为成员变量
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

SpringApplication构造函数中通过getSpringFactoriesInstances(ApplicationContextInitializer.class)) 来获取ApplicationContextInitializer实现类并设置为成员变量initializers。方法实现如下:

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
	//type为ApplicationContextInitializer.class
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		//获取ApplicationContextInitializer的实现类,springboot最简配置下应该是6个
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//通过默认构造函数创建实例
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		//通过指定排序器进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

这6个实现类,4个在spring-boot-2.0.6.RELEASE.jar包下spring.factories文件中

#Application Context Initializers
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
org.springframework.boot.context.ContextIdApplicationContextInitializer,
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

2个在spring-boot-autoconfigure-2.0.6.RELEASE.jar包下的spring.factories文件中

Initializers
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

应用初始化ApplicationListener

SpringApplication构造函数中,初始化完ApplicationContextInitializer后,立马就会初始化ApplicationListener。方式与上面介绍的一模一样,这里不再说明。springboot最小配置模式下,共10个实现类。

org.springframework.context.ApplicationListener=
org.springframework.boot.ClearCachesApplicationListener,
org.springframework.boot.builder.ParentContextCloserApplicationListener,
org.springframework.boot.context.FileEncodingApplicationListener,
org.springframework.boot.context.config.AnsiOutputApplicationListener,
org.springframework.boot.context.config.ConfigFileApplicationListener,
org.springframework.boot.context.config.DelegatingApplicationListener,
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,
org.springframework.boot.context.logging.LoggingApplicationListener,
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer

获取SpringApplicationRunListener

SpringApplication的run()方法中获取SpringApplicationRunListener是通过SpringFactoriesLoader来获取的,方式与上面类似。

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		//通过SpringFactoriesLoader来获取META-INF/spring.factories的实现类
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

正如在第一篇文章中所说,springboot最简配置下,只有一个实现类

#Run Listeners
org.springframework.boot.SpringApplicationRunListener=
org.springframework.boot.context.event.EventPublishingRunListener

该类在springboot启动的不同阶段发布不同的事件,使ApplicationListener作出响应,就像一个事件响应驱动器一样。

准备ConfigurableEnvironment

	public ConfigurableApplicationContext run(String... args) {
		...
		//通过SpringFactoriesLoader来获取META-INF/spring.factories的实现类
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			//准备环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
		...

准备环境过程中,会去寻找org.springframework.boot.env.EnvironmentPostProcessor接口的实现类,
META-INF/spring.factories文件中有三个。

#Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor

代码执行过程中,还会多一个org.springframework.boot.context.config.ConfigFileApplicationListener
它既是一个ApplicationListener,又是一个EnvironmentPostProcessor。它包含一个内部类Loader,这个类在构造过程中也需要获取 org.springframework.boot.env.PropertySourceLoader接口的实现类。在META-INF/spring.factories文件中是

#PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=
org.springframework.boot.env.PropertiesPropertySourceLoader,
org.springframework.boot.env.YamlPropertySourceLoader

顺带说一下,ConfigFileApplicationListener跟应用的配置文件息息相关,后面的文章会讲述。

准备SpringBootExceptionReporter

	public ConfigurableApplicationContext run(String... args) {
		...
		try {
		...
			context = createApplicationContext();
			//准备启动异常报告器
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
		...
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}
		...
		return context;
	}

涉及到org.springframework.boot.SpringBootExceptionReporter接口的子类获取及初始化,以及org.springframework.boot.diagnostics.FailureAnalyzer接口的子类及初始化。

刷新上下文AnnotationConfigApplicationContext

SpringApplication类的refresh方法,是创建上下文context的关键步骤

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

在这个过程中,SpringFactoriesLoader获取了如下接口的实现类

BeanInfoFactory
EnableAutoConfiguration
AutoConfigurationImportFilter
AutoConfigurationImportListener
RepositoryFactorySupport

->invokeBeanFactoryPostProcessors(beanFactory)
->ConfigurationClassPostProcessor.processConfigBeanDefinitions
->SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class)
->AutoConfigurationImportSelector.selectImports()
->SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class)

这个基本过程是ConfigurationClassPostProcessor这个Configuration Bean工厂后置器去扫描要注册的bean。AutoConfigurationImportSelector的selectImports方法返回的一组类应该被当做bean注册到容器中,这个方法其实最终是到META-INF/spring.factories文件中找EnableAutoConfiguration为键的值(一组类),RedisAutoConfiguration只是其中的一个值。

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

}

RedisAutoConfiguration将被标记且注册为bean,其上的@Import注解中的
LettuceConnectionConfiguration也会被标记为bean进行注册,而
JedisConnectionConfiguration不会被标记为bean,因为默认情况下类路径下没有其依赖的Jedis.class

@EnableConfigurationProperties(RedisProperties.class)会使RedisProperties类也被标记为bean。从RedisProperties类的定义可以看出它仅仅是一个配置映射类,配置项都是以spring.redis开头。配置文件中,一定要配置redis数据源

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
...
}

总结

SpringFactoriesLoader在SpringBoot启动流程中的作用与META-INF/spring.factories文件关系密切,该文件中的工厂实现类,几乎都要被SpringFactoriesLoader标记注册为bean并实例化。比如与SpringBoot启动周期阶段相关的ApplicationContextInitializer、ApplicationListener、SpringApplicationRunListener、SpringApplicationRunListener、EnableAutoConfiguration等类。
SpringBoot的自动化配置重度依赖@EnableAutoConfiguration注解、SpringFactoriesLoader、META-INF/spring.factories等重要要素。

相关博客

springboot启动流程简析
Springboot内置ApplicationListener–ConfigFileApplicationListener
SpringBoot实战之SpringBoot自动配置原理
Spring ConfigurationClassPostProcessor Bean解析及自注册过程
@EnableAutoConfiguration注解原理

<think>我们正在讨论SpringBoot中的ConfigurationClassPostProcessor。根据用户的问题,他想了解这个类的具体作用和工作原理。根据引用[1][2][3][4],我们可以知道SpringBoot的启动过程涉及多个核心组件,其中ConfigurationClassPostProcessor是Spring框架中处理配置类的关键后置处理器。作用:ConfigurationClassPostProcessor是BeanDefinitionRegistryPostProcessor的实现类,它在Spring容器启动过程中负责解析配置类(比如带有@Configuration注解的类)并处理相关的注解(如@ComponentScan,@Import,@Bean等),从而将更多的bean定义注册到容器中。工作原理:1.在Spring容器刷新(refresh)阶段,会调用所有BeanFactoryPostProcessor的postProcessBeanFactory方法,以及BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法(因为ConfigurationClassPostProcessor实现了这个接口)。2.ConfigurationClassPostProcessor首先会查找当前容器中所有的BeanDefinition,然后筛选出配置类(配置类通常带有@Configuration注解,但也可以是@Component、@ComponentScan、@Import、@ImportResource注解修饰的类,或者是任何有@Bean方法的类)。3.然后使用ConfigurationClassParser解析这些配置类,解析过程包括:-解析@ComponentScan注解,扫描指定包下的组件并注册为BeanDefinition。-解析@Import注解,导入其他配置类或ImportSelector、ImportBeanDefinitionRegistrar的实现类。-解析@ImportResource注解,导入XML配置文件。-解析@Bean注解的方法,将这些方法注册为BeanDefinition。-处理配置类中嵌套的配置类(内部类)。4.解析完成后,ConfigurationClassPostProcessor会使用ConfigurationClassBeanDefinitionReader将解析得到的配置类中定义的bean(例如通过@Bean方法、@Import导入的组件等)注册到容器中。值得注意的是,ConfigurationClassPostProcessor是Spring框架的一部分,并不是SpringBoot特有的,但SpringBoot大量使用配置类(如自动配置)所以它非常重要。引用[2]中提到SpringBoot自动配置也是通过@EnableAutoConfiguration注解(通过@Import导入AutoConfigurationImportSelector)来实现的,而这个处理过程就是由ConfigurationClassPostProcessor来完成的。另外,引用[4]中提到,SpringBoot在启动时读取spring.factories文件中的配置类并不是直接通过@SpringBootApplication注解,而是通过SpringFactoriesLoader加载,然后由ConfigurationClassPostProcessor来处理这些配置类。因此,ConfigurationClassPostProcessor在SpringBoot启动过程中扮演了核心角色,它负责处理所有配置类,包括自动配置类的导入和解析。下面我们来详细说明:步骤1:在SpringApplication.run()方法启动后,会创建AnnotationConfigApplicationContext(或者其它类型的上下文),然后会调用refresh()方法。步骤2:在refresh()方法中,会调用invokeBeanFactoryPostProcessors(beanFactory)方法,这个方法会执行所有BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor。步骤3:ConfigurationClassPostProcessor作为BeanDefinitionRegistryPostProcessor,它的postProcessBeanDefinitionRegistry方法会被调用。在这个方法中,它会开始处理配置类。具体源码层面(简要):-ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry->processConfigBeanDefinitions(registry)->创建ConfigurationClassParser,解析配置类(parse()方法)->创建ConfigurationClassBeanDefinitionReader,读取配置类并注册bean定义(loadBeanDefinitions()方法)在解析过程中,如果遇到@EnableAutoConfiguration,那么AutoConfigurationImportSelector会被调用,它会从spring.factories中加载自动配置类。总结:ConfigurationClassPostProcessor的作用是解析配置类并注册相关的bean定义。它是Spring容器中处理Java配置的核心处理器,也是SpringBoot自动配置机制能够生效的关键。相关问题:1.ConfigurationClassPostProcessor在Spring容器启动过程中的哪个阶段被调用?2.如何自定义一个配置类?配置类中常用的注解有哪些?3.SpringBoot自动配置是如何通过ConfigurationClassPostProcessor实现的?4.除了@Configuration,还有哪些注解可以标记一个类为配置类?注意:在回答中,我们引用了[1][2][3][4],但用户的问题主要集中在ConfigurationClassPostProcessor,所以我们结合这些引用内容进行了解释。</think>### Spring Boot 中 `ConfigurationClassPostProcessor` 的作用和工作原理 `ConfigurationClassPostProcessor` 是 Spring 框架中处理**配置类(`@Configuration`)** 的核心后置处理器,在 Spring Boot 启动过程中扮演关键角色。其作用和工作原理如下: --- #### **一、核心作用** 1. **解析配置类** 扫描并处理所有被 `@Configuration` 注解标记的类(包括 `@SpringBootApplication` 隐含的配置类),解析其中的: - `@Bean` 方法 → 注册 Bean 定义 - `@ComponentScan` → 扫描指定包下的组件 - `@Import` → 导入其他配置类或 `ImportSelector`/`ImportBeanDefinitionRegistrar` - `@PropertySource` → 加载外部属性文件 2. **实现自动配置的关键** 通过解析 `@EnableAutoConfiguration`(内置于 `@SpringBootApplication`)触发的 `AutoConfigurationImportSelector`,加载 `META-INF/spring.factories` 中的自动配置类[^2][^4]。 3. **注册 Bean 定义** 将解析结果转换为 Spring 容器的 `BeanDefinition`,为后续 Bean 实例化做准备。 --- #### **二、工作原理(源码级流程)** ##### **阶段 1:配置类发现** - Spring Boot 启动时,`SpringApplication.run()` 初始化 `AnnotationConfigApplicationContext`。 - 容器刷新阶段(`refresh()` 方法)调用 `BeanFactoryPostProcessor`,其中 `ConfigurationClassPostProcessor` 作为 `BeanDefinitionRegistryPostProcessor` 优先执行[^1][^2]。 ##### **阶段 2:配置类解析** ```java // 简化的源码流程 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { // 1. 筛选候选配置类(含 @Configuration 的类) List<BeanDefinition> configCandidates = findConfigCandidates(registry); // 2. 使用 ConfigurationClassParser 解析配置类 ConfigurationClassParser parser = new ConfigurationClassParser(); parser.parse(configCandidates); // 解析内部注解 // 3. 处理 @Import(AutoConfigurationImportSelector.class) if (注解含 @EnableAutoConfiguration) { loadAutoConfigurations(parser); // 加载 spring.factories 中的配置类 } } ``` ##### **阶段 3:Bean 定义注册** - 通过 `ConfigurationClassBeanDefinitionReader` 将解析结果注册到容器: ```java // 注册 @Bean 方法、@Imported 资源等 reader.loadBeanDefinitions(configClasses); ``` - 处理嵌套配置类(内部类)和 `@Profile` 条件注解。 ##### **阶段 4:代理增强(CGLIB)** - 为 `@Configuration` 类生成 CGLIB 代理,确保 `@Bean` 方法调用时返回单例 Bean(避免重复创建)[^3]。 --- #### **三、与 Spring Boot 自动配置的关系** 1. `@SpringBootApplication` 组合了 `@EnableAutoConfiguration`。 2. `ConfigurationClassPostProcessor` 解析该注解时,触发 `AutoConfigurationImportSelector`: ```java // AutoConfigurationImportSelector 关键逻辑 List<String> loadFactoryNames(Class<?> factoryType) { return SpringFactoriesLoader.loadFactoryNames(factoryType, classLoader); } ``` 从 `META-INF/spring.factories` 加载自动配置类(如 `DataSourceAutoConfiguration`)[^2][^4]。 3. 最终将这些配置类解析为 `BeanDefinition` 并注册到容器。 --- #### **四、总结流程** $$ \text{启动类} \xrightarrow{\text{@SpringBootApplication}} \text{容器初始化} \xrightarrow{\text{ConfigurationClassPostProcessor}} \text{解析配置类} \xrightarrow{\text{加载自动配置}} \text{注册 Bean} $$ 该处理器是 Spring Boot **“约定优于配置”** 能力的核心实现者,将 Java 配置转换为运行时 Bean 结构[^1][^3]。 --- ### 相关问题 1. **`ConfigurationClassPostProcessor` 在 Spring 容器刷新流程中的哪个阶段执行?** 2. **如何自定义一个配置类并确保它被 `ConfigurationClassPostProcessor` 处理?** 3. **为什么 `@Configuration` 类需要 CGLIB 代理?能否禁用代理?** 4. **Spring Boot 的 `spring.factories` 机制与 `ConfigurationClassPostProcessor` 如何协作?** [^1]: Spring Boot 启动原理完整解析 [^2]: Spring Boot 自动配置实现原理(源码分析) [^3]: Spring Boot 属性加载原理解析 [^4]: `@SpringBootApplication` 与 `spring.factories` 的交互逻辑
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值