73.3 启动前自定义Environment或ApplicationContext

每个SpringApplication都有ApplicationListenersApplicationContextInitializers,用于自定义上下文(context)或环境(environment)。Spring Boot从META-INF/spring.factories下加载很多这样的内部使用的自定义,有很多方法可以注册其他的自定义:

  • 以编程方式为每个应用注册自定义,通过在SpringApplication运行前调用它的addListenersaddInitializers方法来实现。

  • 以声明方式为每个应用注册自定义,通过设置context.initializer.classescontext.listener.classes来实现。

  • 以声明方式为所有应用注册自定义,通过添加一个META-INF/spring.factories并打包成一个jar文件(该应用将它作为一个库)来实现。

SpringApplication会给监听器(即使是在上下文被创建之前就存在的)发送一些特定的ApplicationEvents,然后也会注册监听ApplicationContext发布的事件的监听器,查看Spring Boot特性章节中的23.5. 应用事件和监听器可以获取完整列表。

在应用上下文刷新前使用EnvironmentPostProcessor自定义Environment是可能的,每个实现都需要注册到META-INF/spring.factories

org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor

可以加载任意文件并将它们添加到环境中。例如,下面的例子从类路径加载一个YAML配置文件:

public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor {
private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
Resource path = new ClassPathResource("com/example/myapp/config.yml");
PropertySource<?> propertySource = loadYaml(path);
environment.getPropertySources().addLast(propertySource);
}
private PropertySource<?> loadYaml(Resource path) {
if (!path.exists()) {
throw new IllegalArgumentException("Resource " + path + " does not exist");
}
try {
return this.loader.load("custom-resource", path).get(0);
}
catch (IOException ex) {
throw new IllegalStateException(
"Failed to load yaml configuration from " + path, ex);
}
}
}

在默认情况下,环境已经准备好了所有通常由Spring Boot加载的属性源。因此,可以从环境中获取文件的位置。前面的示例将自定义资源属性源添加到列表的末尾,以便在任何其他位置定义的键具有优先级。自定义实现可以定义另一种顺序。

警告 虽然在@SpringBootApplication上使用@PropertySource似乎是在环境中加载自定义资源的一种方便而简单的方法,但是我们不推荐使用它,因为SpringBoot在刷新ApplicationContext之前就准备好了环境。使用@PropertySource定义的任何键加载太迟,对自动配置没有任何影响。