一、 ApplicationContextInitializer 介绍

  首先看spring官网的介绍:

springboot 设置实体类字段必填_java

   翻译一下:

  • 用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。(剪短说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext
  • 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等。
  • 可排序的(实现Ordered接口,或者添加@Order注解)

ApplicationContextInitializer

二、三种实现方式

MyApplicationContextInitializer 并实现 ApplicationContextInitializer



1 public class MyApplicationContextInitializer implements ApplicationContextInitializer {
2     @Override
3     public void initialize(ConfigurableApplicationContext applicationContext) {
4         System.out.println("-----MyApplicationContextInitializer initialize-----");
5     }
6 }



  2.1、mian函数中添加

  优雅的写一个SpringBoot的main方法



1 @SpringBootApplication
2 public class MySpringBootApplication {
3     public static void main(String[] args) {
4         SpringApplication application = new SpringApplication(MySpringBootApplication.class);
5         application.addInitializers(new MyApplicationContextInitializer());
6         application.run(args);
7     }
8 }



 

  运行,查看控制台:生效了

  

springboot 设置实体类字段必填_配置文件_02

  2.2、配置文件中配置



context.initializer.classes=org.springframework.boot.demo.common.MyApplicationContextInitializer



 

  

springboot 设置实体类字段必填_初始化_03

  2.3、SpringBoot的SPI扩展---META-INF/spring.factories中配置



org.springframework.context.ApplicationContextInitializer=org.springframework.boot.demo.common.MyApplicationContextInitializer



 

  

springboot 设置实体类字段必填_初始化_04

 

三、排序问题

  如图所示改造一下mian方法。打一个断点,debug查看排序情况。

  

springboot 设置实体类字段必填_初始化_05

MyApplicationContextInitializer



1 @Order(Ordered.HIGHEST_PRECEDENCE)
2 public class MyApplicationContextInitializer implements ApplicationContextInitializer{
3     @Override
4     public void initialize(ConfigurableApplicationContext applicationContext) {
5         System.out.println("-----MyApplicationContextInitializer initialize-----");
6     }
7 }



 

  下面我们通过debug分别验证二章节中提到的三种方法排序是否都是可以的。

application.getInitializers()

  

springboot 设置实体类字段必填_java_06

  然后再分别验证2.2和2.3章节中的方法。排序都是可以实现的。

@Order(Ordered.LOWEST_PRECEDENCE)

 四、通过源码分析ApplicationContextInitializer何时被调用

MyApplicationContextInitializer

  

springboot 设置实体类字段必填_配置文件_07

ApplicationContextInitializer 在容器刷新前的准备阶段被调用。 refreshContext(context);

ApplicationContextInitializer



1     public ConfigurableApplicationContext run(String... args) {
 2         //记录程序运行时间
 3         StopWatch stopWatch = new StopWatch();
 4         stopWatch.start();
 5         // ConfigurableApplicationContext Spring 的上下文
 6         ConfigurableApplicationContext context = null;
 7         Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
 8         configureHeadlessProperty();
 9         //从META-INF/spring.factories中获取监听器
10         //1、获取并启动监听器
11         SpringApplicationRunListeners listeners = getRunListeners(args);
12         listeners.starting();
13         try {
14             ApplicationArguments applicationArguments = new DefaultApplicationArguments(
15                     args);
16             //2、构造容器环境
17             ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
18             //处理需要忽略的Bean
19             configureIgnoreBeanInfo(environment);
20             //打印banner
21             Banner printedBanner = printBanner(environment);
22             ///3、初始化容器
23             context = createApplicationContext();
24             //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
25             exceptionReporters = getSpringFactoriesInstances(
26                     SpringBootExceptionReporter.class,
27                     new Class[]{ConfigurableApplicationContext.class}, context);
28             //4、刷新容器前的准备阶段
29             prepareContext(context, environment, listeners, applicationArguments, printedBanner);
30             //5、刷新容器
31             refreshContext(context);
32             //刷新容器后的扩展接口
33             afterRefresh(context, applicationArguments);
34             stopWatch.stop();
35             if (this.logStartupInfo) {
36                 new StartupInfoLogger(this.mainApplicationClass)
37                         .logStarted(getApplicationLog(), stopWatch);
38             }
39             listeners.started(context);
40             callRunners(context, applicationArguments);
41         } catch (Throwable ex) {
42             handleRunFailure(context, ex, exceptionReporters, listeners);
43             throw new IllegalStateException(ex);
44         }
45 
46         try {
47             listeners.running(context);
48         } catch (Throwable ex) {
49             handleRunFailure(context, ex, exceptionReporters, null);
50             throw new IllegalStateException(ex);
51         }
52         return context;
53     }



 

refreshContext(context);



1 private void prepareContext(ConfigurableApplicationContext context,
2                             ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
3                             ApplicationArguments applicationArguments, Banner printedBanner) {
4     context.setEnvironment(environment);
5     postProcessApplicationContext(context);
6     applyInitializers(context);
7     ...
8 }



 

applyInitializers 中遍历调用每一个被加载的 ApplicationContextInitializer 的  initialize(context);  方法,并将 ConfigurableApplicationContext 的实例传递给 initialize



1 protected void applyInitializers(ConfigurableApplicationContext context) {
2     for (ApplicationContextInitializer initializer : getInitializers()) {
3         Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
4                 initializer.getClass(), ApplicationContextInitializer.class);
5         Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
6         initializer.initialize(context);
7     }
8 }



 

ApplicationContextInitializer