ApplicationContextInitializer 类是SpringBoot对外预留的拓展点,定义的系统初始化器会在系统正在执行refreshContext 之前的 prepareContext() 中执行初始化执行。

    ApplicationContextInitializer是Spring框架原有的东西,这个类的主要作用就是在ConfigurableApplicationContext类型(或者子类型)的ApplicationContext做refresh之前,允许我们对ConfiurableApplicationContext的实例做进一步的设置和处理。

    1. private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    2. SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    3. //..... 忽略无关代码
    4. applyInitializers(context);
    5. //..... 忽略无关代码
    6. }
    7. protected void applyInitializers(ConfigurableApplicationContext context) {
    8. for (ApplicationContextInitializer initializer : getInitializers()) {
    9. Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
    10. ApplicationContextInitializer.class);
    11. Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
    12. initializer.initialize(context);
    13. }
    14. }

    我们可以通过自定义系统初初始化器来实自己的需求, 主要是通过继承 ApplicationContextInitializer<ConfigurableApplicationContext> 来实现逻辑,比如天机自定义的配置,创建新的监听器等等。

    1. /**
    2. * 自定义系统初始化器
    3. */
    4. @Order(1)
    5. public class FirstInitializer implements
    6. ApplicationContextInitializer<ConfigurableApplicationContext> {
    7. public static final Logger log = LoggerFactory.getLogger(FirstInitializer.class);
    8. @Override
    9. public void initialize(ConfigurableApplicationContext context) {
    10. ConfigurableEnvironment environment = context.getEnvironment();
    11. Map<String, Object> map = new HashMap<>();
    12. map.put("test-key", "测试的环境变量");
    13. MapPropertySource propertySource = new MapPropertySource("firstInitializer", map);
    14. environment.getPropertySources().addLast(propertySource);
    15. log.info("自定义初始化器初始化完成:{}", this.getClass().getName());
    16. }
    17. }

    启用自定义的系统初始化器有两种方法:

    • [x] 在 resource/MATE-INF 目录下创建 spring.factoties 文件,文件内填写自定义加载器的全类名,这是利用了SpringBoot在启动初期,会扫描JAR包下面的 spring.factories 文件来实现的。

      1. org.springframework.context.ApplicationContextInitializer= \
      2. com.zhoutao123.spring.springboot.init.FirstInitializer
    • [x] 或者在系统初始化器启动的时候,手动构造SpringApplication,然后设置系统初始化器,如下 ```java SpringApplication application = new SpringApplication(Application.class); application.addInitializers(new FirstInitializer()); application.run(args);

    1. // 其原理是添加在当前上下文的initizlizes中
    2. public void addInitializers(ApplicationContextInitializer<?>... initializers) {
    3. this.initializers.addAll(Arrays.asList(initializers));
    4. }
    1. - [x] SpringBoot的配置文件中添加(优先于前两者),实现原理是委托给DelegatingApplicationContextInitializer 系统初始化器
    2. ```java
    3. context.initializer.classes: com.zhoutao123.spring.springboot.init.FirstInitializer

    其实现原理是将在系统配置中的系统初始化器委托 DelegatingApplicationContextInitializer 系统初始化器,因为 order = 0,所以委托给 DelegatingApplicationContextInitializer 的系统初始化器会优先被执行。

    1. public class DelegatingApplicationContextInitializer
    2. implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
    3. //寻找环境变量的key
    4. private static final String PROPERTY_NAME = "context.initializer.classes";
    5. // 执行优先级
    6. private int order = 0;
    7. @Override
    8. public void initialize(ConfigurableApplicationContext context) {
    9. ConfigurableEnvironment environment = context.getEnvironment();
    10. List<Class<?>> initializerClasses = getInitializerClasses(environment);
    11. if (!initializerClasses.isEmpty()) {
    12. //应用初始化
    13. applyInitializerClasses(context, initializerClasses);
    14. }
    15. }
    16. // 从环境中获取Class
    17. private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
    18. String classNames = env.getProperty(PROPERTY_NAME);
    19. List<Class<?>> classes = new ArrayList<>();
    20. if (StringUtils.hasLength(classNames)) {
    21. for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
    22. classes.add(getInitializerClass(className));
    23. }
    24. }
    25. return classes;
    26. }

    配置完成之后,我们需要验证系统上下文的环境中已经正确配置了当前的值,可以创建一个Bean来验证。

    1. @Component
    2. public class EnvService implements ApplicationContextAware {
    3. private static final Logger log = LoggerFactory.getLogger(EnvService.class);
    4. @Override
    5. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    6. String value = applicationContext.getEnvironment().getProperty("test-key");
    7. log.info("从环境中获取到的 Value = {}", value);
    8. assert Objects.equals(value,"测试的环境变量");
    9. }
    10. }

    如果Spring配置文件中所定义或者注解自动注入的Bean类实现了ApplicationContextAware 接口,那么在加载Spring配置文件时,会自动调用ApplicationContextAware 接口中的方法:public void setApplicationContext (ApplicationContext context) throws BeansException

    在控制台输出界面我们可以看到输出的日志,证明自定义的系统初始化器配置完成.

    1. Initializing Spring embedded WebApplicationContext
    2. Root WebApplicationContext: initialization completed in 582 ms
    3. 从环境中获取到的 Value = 测试的环境变量
    4. Initializing ExecutorService 'applicationTaskExecutor'
    5. Tomcat started on port(s): 8080 (http) with context path ''
    6. Started Application in 1.229 seconds (JVM running for 1.601)

    re