Spring Boot-加载配置文件ConfigDataEnvironmentPostProcessor

org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor是 Spring Boot 中的一个关键组件,它负责处理配置数据(config data),并将其应用于 Spring 应用上下文中。下面我们详细解读这个类的产生背景、作用和工作原理。

产生背景

在 Spring Boot 中,配置数据是指来自各种外部来源(如 YAML 文件、properties 文件、环境变量、命令行参数等)的配置信息。Spring Boot 提供了一种机制来自动读取这些配置信息,并将它们绑定到应用中的 Bean 上。为了更好地管理和处理这些配置数据,Spring Boot 引入了配置数据功能,其中包括 `ConfigData` 和 `ConfigDataLocation` 等概念。

作用

`ConfigDataEnvironmentPostProcessor` 的主要作用是处理配置数据,并将这些数据合并到应用的环境配置中。具体来说,它执行以下几个关键任务:

  1. 加载配置数据:从不同的配置源(如文件系统、类路径、云配置中心等)加载配置数据。

  2. 解析配置数据:解析加载的配置数据,并将其转换为 Spring 可以理解的格式。

  3. 合并配置数据:将解析后的配置数据合并到 `ConfigurableEnvironment` 中。

  4. 处理占位符:解析配置数据中的占位符,并将其替换为实际的值。

工作原理

`ConfigDataEnvironmentPostProcessor` 的工作原理涉及以下几个步骤:

  1. 实例化:在 Spring 应用启动时,`ConfigDataEnvironmentPostProcessor` 会作为 `BeanFactoryPostProcessor` 的一部分被实例化。

  2. 配置数据定位:通过 `ConfigDataLocationResolver` 来确定配置数据的位置。

  3. 加载配置数据:使用 `ConfigDataLoader` 来加载配置数据。

  4. 解析配置数据:使用 `ConfigDataParser` 来解析加载的数据。

  5. 合并配置数据:将解析后的配置数据合并到 `ConfigurableEnvironment` 中。

  6. 处理占位符:在合并过程中处理配置数据中的占位符。

源码流程:

这个类有个成员变量postProcessorsFactory,是个函数,函数签名是接受ClassLoader类型参数,返回EnvironmentPostProcessorsFactory类型参数。这个类的无参构造是在SpringApplication实例化时被调用的。这里主要是要理解、熟悉函数作为参数传递的用法。这个函数实际传的值是org.springframework.boot.env.EnvironmentPostProcessorsFactory#fromSpringFactories(),

下面就是执行监听器回调,最后调用了this.postProcessorsFactory.apply(classLoader);来得到一个EnvironmentPostProcessorsFactory类型实例,其实际类型其实是ReflectionEnvironmentPostProcessorsFactory

java 复制代码
public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {

	private final Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory;


	/**
	 * Create a new {@link EnvironmentPostProcessorApplicationListener} with
	 * {@link EnvironmentPostProcessor} classes loaded through {@code spring.factories}.
	 */
	public EnvironmentPostProcessorApplicationListener() {
		this(EnvironmentPostProcessorsFactory::fromSpringFactories, new DeferredLogs());
	}

	EnvironmentPostProcessorApplicationListener(
			Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory, DeferredLogs deferredLogs) {
		this.postProcessorsFactory = postProcessorsFactory;
		this.deferredLogs = deferredLogs;
	}

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent();
		}
		if (event instanceof ApplicationFailedEvent) {
			onApplicationFailedEvent();
		}
	}


	private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
		ConfigurableEnvironment environment = event.getEnvironment();
		SpringApplication application = event.getSpringApplication();
		for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
				event.getBootstrapContext())) {
			postProcessor.postProcessEnvironment(environment, application);
		}
	}

	List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
			ConfigurableBootstrapContext bootstrapContext) {
		ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
		EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
		return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
	}
}

还是读取spring.factories文件得到 EnvironmentPostProcessor用于可扩展的设计。

这个类的方法不好理解,这里用了Instantiator,构建Instantiator时第二个参数是一个方法,告诉Instantiator,在创建EnvironmentPostProcessor实例的时候,如果构造函数或setter方法中有DeferredLogFactory类型的参数,则使用logFactory作为实际值,下面几个add作用也是一样。

java 复制代码
class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProcessorsFactory {

	@Override
	public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,
			ConfigurableBootstrapContext bootstrapContext) {
		Instantiator<EnvironmentPostProcessor> instantiator = new Instantiator<>(EnvironmentPostProcessor.class,
				(parameters) -> {
					parameters.add(DeferredLogFactory.class, logFactory);
					parameters.add(Log.class, logFactory::getLog);
					parameters.add(ConfigurableBootstrapContext.class, bootstrapContext);
					parameters.add(BootstrapContext.class, bootstrapContext);
					parameters.add(BootstrapRegistry.class, bootstrapContext);
				});
		return (this.classes != null) ? instantiator.instantiateTypes(this.classes)
				: instantiator.instantiate(this.classLoader, this.classNames);
	}


}

org.springframework.boot.context.config.ConfigDataEnvironment#processAndApply

相关推荐
吾疾唯君医6 小时前
Java SpringBoot集成积木报表实操记录
java·spring boot·spring·导出excel·积木报表·数据文件下载
正儿八经的少年10 小时前
Spring Boot 两种激活配置方式的作用与区别
java·spring boot·后端
疯狂成瘾者10 小时前
Spring Boot 项目中的 SMTP 邮件验证码服务技术解析
java·spring boot·后端
啃臭11 小时前
AOP和反射
java·spring boot
河阿里12 小时前
SpringBoot:Spring Task定时任务完整使用教学
java·spring boot·spring
五阿哥永琪14 小时前
从0开始做一个导出功能,完整流程
spring boot
java1234_小锋15 小时前
SpringBoot可以同时处理多少请求?
java·spring boot·后端
海棠Flower未眠16 小时前
Spring Boot 3 + JPA多模块系统对MySQL和DORIS进行多数据源集成实战(荣耀典藏版)
spring boot·后端·mysql
北风朝向16 小时前
Spring Boot 集成 Open WebUI 实现 AI 流式对话
人工智能·spring boot·状态模式
海棠Flower未眠17 小时前
Spring Boot 2.4后,特定配置文件不能再使用spring.profiles.include的解决思路
数据库·spring boot·spring