[Ngbatis源码学习][SpringBoot] ApplicationContextInitializer接口类的使用和原理解读

ApplicationContextInitializer接口类的使用和原理解读

在看Ngbatis源码的过程中,看到了自定义的ApplicationContextInitializer实现类,对ApplicationContextInitializer接口不是特别的理解,所以趁此机会总结下对其的理解和使用。

1. 作用

org.springframework.context.ApplicationContextInitializer#initialize
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
/**

* Initialize the given application context.<br/>
* @param applicationContext the application to configure<br/>
*/<br/>

void initialize(C applicationContext); }

2. 加载方式

通过具体的实现类实现ApplicationContextInitializer接口。

Springboot的扩展点ApplicationContextInitializer接口的实现主要分为两步:

  1. 实现ApplicationContextInitializer接口
  2. 把实现类注册到Spring容器中

其中把实现了ApplicationContextInitializer接口的实现类注册到Spring容器中,主要有三种方式:

  • spring.factories
  • 启动类中配置
  • application.properties

以下一一介绍。

2.1. spring.factories

META-INF/spring.factorties
org.springframework.context.ApplicationContextInitializer=com.knqiufan.config.MyApplicationContextInitializer

2.2. 启动类中配置

在SpringBoot的启动类中,使用SpringApplication.addInitializers(new MyApplicationContextInitializer())完成自定义MyApplicationContextInitializer的注册:

@SpringBootApplication
public class TestApplication {

public static void main(String[] args) {<br/>
    SpringApplication springApplication = new SpringApplication(TestApplication.class);<br/>
    springApplication.addInitializers(new MyApplicationContextInitializer());<br/>
    springApplication.run(args);<br/>
}<br/>

}

2.3. application.properties

在application.properties文件中预置以下配置内容,即可完成自定义MyApplicationContextInitializer的注册:

context.initializer.classes=com.knqiufan.config.MyApplicationContextInitializer

3. 初始化时机

ApplicationContextInitializer接口的实现类的初始化,是在SpringApplication类的构造函数中。

org.springframework.boot.SpringApplication#getSpringFactoriesInstancesorg.springframework.boot.SpringApplication#setInitializers
/**

  • SpringApplication构造函数
    */
    @SuppressWarnings({ “unchecked”, “rawtypes” })
    public SpringApplication(ResourceLoader resourceLoader, Class&lt;?&gt;… primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, “PrimarySources must not be null”);
    this.primarySources = new LinkedHashSet&lt;&gt;(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 获得所有实现类的集合
    this.bootstrapRegistryInitializers = new ArrayList&lt;&gt;(
    getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    // 给属性 List&lt;ApplicationContextInitializer&lt;?&gt;&gt; initializers 赋值
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
    }
org.springframework.boot.SpringApplication#getSpringFactoriesInstances
private &lt;T&gt; Collection&lt;T&gt; getSpringFactoriesInstances(Class&lt;T&gt; type, Class&lt;?&gt;[] parameterTypes, Object… args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 使用Set保证名称唯一,以防止重复
Set&lt;String&gt; names = new LinkedHashSet&lt;&gt;(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List&lt;T&gt; instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}

可以看到调用的是 SpringFactoriesLoader.loadFactoryNames 静态方法来加载所有 ApplicationContextInitializer 的实现类名称。

SpringFactoriesLoader.loadFactoryNamesMETA-INF/spring.factories
public static List&lt;String&gt; loadFactoryNames(Class&lt;?&gt; factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
} String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
} private static Map&lt;String, List&lt;String&gt;&gt; loadSpringFactories(ClassLoader classLoader) {
Map&lt;String, List&lt;String&gt;&gt; result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
Map&lt;String, List&lt;String&gt;&gt; result = new HashMap(); try {
// 获取 META-INF/spring.factories 下配置的所有实现类
Enumeration&lt;URL&gt; urls = classLoader.getResources(“META-INF/spring.factories”); // … 省略部分代码 … // 加载的ApplicationContextInitializer实现类加入缓存
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException(“Unable to load factories from location [META-INF/spring.factories]”, var14);
}
}
}

4. 执行时机

org.springframework.boot.SpringApplication#prepareContextorg.springframework.boot.SpringApplication#applyInitializers
/**
  • Spring容器准备上下文环境方法
    */
    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
    ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
    ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    // 执行 ApplicationContextInitializer 实现类的 initialize() 方法
    applyInitializers(context);
    //… 省略其他代码 …
    } /**
  • 在此方法中执行 ApplicationContextInitializer 实现类中的 initialize() 方法
    */
    @SuppressWarnings({ “rawtypes”, “unchecked” })
    protected void applyInitializers(ConfigurableApplicationContext context) {
    // 获取所有实现类,并进行遍历
    for (ApplicationContextInitializer initializer : getInitializers()) {
    Class&lt;?&gt; requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
    ApplicationContextInitializer.class);
    Assert.isInstanceOf(requiredType, context, “Unable to call initializer.”);
    // 执行实现类的initialize方法
    initializer.initialize(context);
    }
    }
  • org.springframework.boot.SpringApplication#getInitializers
    org.springframework.boot.SpringApplication#setInitializersorg.springframework.boot.SpringApplication#getInitializers

    源码如下:

    /**
  • 返回使用 Order 排序过的 ApplicationContextInitializer 实现类的Set列表
  • Returns read-only ordered Set of the {@link ApplicationContextInitializer}s that
  • will be applied to the Spring {@link ApplicationContext}.
  • @return the initializers
    */
    public Set&lt;ApplicationContextInitializer&lt;?&gt;&gt; getInitializers() {
    return asUnmodifiableOrderedSet(this.initializers);
    } private static &lt;E&gt; Set&lt;E&gt; asUnmodifiableOrderedSet(Collection&lt;E&gt; elements) {
    List&lt;E&gt; list = new ArrayList&lt;&gt;(elements);
    list.sort(AnnotationAwareOrderComparator.INSTANCE);
    return new LinkedHashSet&lt;&gt;(list);
    }
  • 5. 内置实现类

    Springboot内部也有一些内置的实现类,用于辅助Spring相关功能的实现。简单介绍几个:

    DelegatingApplicationContextInitializerContextIdApplicationContextInitializerConfigurationWarningsApplicationContextInitializerServerPortInfoApplicationContextInitializer

    5.1. DelegatingApplicationContextInitializer

    ApplicationContextInitializer 其中一个的实现方式就是在 application.properties 配置文件中对实现类进行配置。

    DelegatingApplicationContextInitializer的作用就是找到application.properties文件中配置的实现类实例化,并执行initialize()方法。

    源码很好理解,如下:

    /**
  • {@link ApplicationContextInitializer} that delegates to other initializers that are
  • specified under a {@literal context.initializer.classes} environment property.
  • 获取 context.initializer.classes 这个环境变量中配置的实现类,并执行
    *
  • @author Dave Syer
  • @author Phillip Webb
  • @since 1.0.0
    */
    public class DelegatingApplicationContextInitializer
    implements ApplicationContextInitializer&lt;ConfigurableApplicationContext&gt;, Ordered { // NOTE: Similar to org.springframework.web.context.ContextLoader // 定义的环境变量名称
    private static final String PROPERTY_NAME = “context.initializer.classes”; private int order = 0; @Override
    public void initialize(ConfigurableApplicationContext context) {
    // 根据配置上下文获取配置信息
    ConfigurableEnvironment environment = context.getEnvironment();
    // 获取所有配置的实现类
    List&lt;Class&lt;?&gt;&gt; initializerClasses = getInitializerClasses(environment);
    if (!initializerClasses.isEmpty()) {
    // 执行实现类的 initialize 方法
    applyInitializerClasses(context, initializerClasses);
    }
    } private List&lt;Class&lt;?&gt;&gt; getInitializerClasses(ConfigurableEnvironment env) {
    // 获取 PROPERTY_NAME 环境变量名配置的所有实现类类名
    String classNames = env.getProperty(PROPERTY_NAME);
    List&lt;Class&lt;?&gt;&gt; classes = new ArrayList&lt;&gt;();
    if (StringUtils.hasLength(classNames)) {
    for (String className : StringUtils.tokenizeToStringArray(classNames, “,”)) {
    // 获取实现类
    classes.add(getInitializerClass(className));
    }
    }
    return classes;
    } /**
    • 获取实现类
      */
      private Class&lt;?&gt; getInitializerClass(String className) throws LinkageError {
      try {
      Class&lt;?&gt; initializerClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
      Assert.isAssignable(ApplicationContextInitializer.class, initializerClass);
      return initializerClass;
      }
      catch (ClassNotFoundException ex) {
      throw new ApplicationContextException(“Failed to load context initializer class [” + className + “]”, ex);
      }
      } /**
    • 执行实现类的 initialize 方法
      */
      private void applyInitializerClasses(ConfigurableApplicationContext context, List&lt;Class&lt;?&gt;&gt; initializerClasses) {
      Class&lt;?&gt; contextClass = context.getClass();
      List&lt;ApplicationContextInitializer&lt;?&gt;&gt; initializers = new ArrayList&lt;&gt;();
      for (Class&lt;?&gt; initializerClass : initializerClasses) {
      initializers.add(instantiateInitializer(contextClass, initializerClass));
      }
      // 执行实现类的 initialize 方法
      applyInitializers(context, initializers);
      } private ApplicationContextInitializer&lt;?&gt; instantiateInitializer(Class&lt;?&gt; contextClass, Class&lt;?&gt; initializerClass) {
      Class&lt;?&gt; requireContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass,
      ApplicationContextInitializer.class);
      Assert.isAssignable(requireContextClass, contextClass,
      () -&gt; String.format(
      “Could not add context initializer [%s] as its generic parameter [%s] is not assignable ”
      + “from the type of application context used by this context loader [%s]: ”,
      initializerClass.getName(), requireContextClass.getName(), contextClass.getName()));
      return (ApplicationContextInitializer&lt;?&gt;) BeanUtils.instantiateClass(initializerClass);
      } @SuppressWarnings({ “unchecked”, “rawtypes” })
      private void applyInitializers(ConfigurableApplicationContext context,
      List&lt;ApplicationContextInitializer&lt;?&gt;&gt; initializers) {
      initializers.sort(new AnnotationAwareOrderComparator());
      // 遍历所有 ApplicationContextInitializer 接口的实现类,执行实现类的 initialize 方法
      for (ApplicationContextInitializer initializer : initializers) {
      initializer.initialize(context);
      }
      } public void setOrder(int order) {
      this.order = order;
      } @Override
      public int getOrder() {
      return this.order;
      } }
  • 5.2. ContextIdApplicationContextInitializer

    ContextIdApplicationContextInitializer用于设置 Spring 应用上下文 ID,如果在application.properties中未设置spring.application.name,则默认为 “application”。

    以下只展示重点源码,有兴趣可以直接去查看完整代码:

    public class ContextIdApplicationContextInitializer
    implements ApplicationContextInitializer&lt;ConfigurableApplicationContext&gt;, Ordered { // … 省略部分代码 … @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
    ContextId contextId = getContextId(applicationContext);
    applicationContext.setId(contextId.getId());
    applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
    } private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
    // … 省略部分代码 …
    return new ContextId(getApplicationId(applicationContext.getEnvironment()));
    }
    // 设置 Spring 应用上下文 ID,若没设置则默认为 “application”
    private String getApplicationId(ConfigurableEnvironment environment) {
    String name = environment.getProperty(“spring.application.name”);
    return StringUtils.hasText(name) ? name : “application”;
    } // … 省略部分代码 …
    }

    5.3. ConfigurationWarningsApplicationContextInitializer

    ConfigurationWarningsApplicationContextInitializer用于报告 Spring 容器的一些常见的错误配置。

    该初始化器为 context 增加了一个 Bean 的后置处理器。这个处理器是在注册 BeanDefinition 实例之后生效的,用于处理注册实例过程中产生的告警信息,其实就是通过日志打印出告警信息。

    以下只展示重点源码,有兴趣可以直接去查看完整代码:

    public class ConfigurationWarningsApplicationContextInitializer
    implements ApplicationContextInitializer&lt;ConfigurableApplicationContext&gt; { private static final Log logger = LogFactory.getLog(ConfigurationWarningsApplicationContextInitializer.class); /**
  • 增加了一个 Bean 的后置处理器,在注册 BeanDefinition 实例之后生效。
  • 用于处理注册实例过程中产生的告警信息,其实就是通过日志打印出告警信息。
    */
    @Override
    public void initialize(ConfigurableApplicationContext context) {
    context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
    } /**
  • Returns the checks that should be applied.
  • @return the checks to apply
    */
    protected Check[] getChecks() {
    return new Check[] { new ComponentScanPackageCheck() };
    } /**
  • {@link BeanDefinitionRegistryPostProcessor} to report warnings.
    */
    protected static final class ConfigurationWarningsPostProcessor
    implements PriorityOrdered, BeanDefinitionRegistryPostProcessor { // … 省略部分代码 … @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    for (Check check : this.checks) {
    String message = check.getWarning(registry);
    if (StringUtils.hasLength(message)) {
    warn(message);
    }
    } }
    // 输出警告信息日志
    private void warn(String message) {
    if (logger.isWarnEnabled()) {
    logger.warn(String.format(“%n%n** WARNING ** : %s%n%n”, message));
    }
    } } // … 省略部分代码 …
    }
  • 5.4. ServerPortInfoApplicationContextInitializer

    ServerPortInfoApplicationContextInitializer除了实现了ApplicationContextInitializer接口外,还实现了ApplicationListener接口,ServerPortInfoApplicationContextInitializer作用就是把自己作为一个监听器注册到Spring的上下文环境中。

    public class ServerPortInfoApplicationContextInitializer implements
    ApplicationContextInitializer&lt;ConfigurableApplicationContext&gt;, ApplicationListener&lt;WebServerInitializedEvent&gt; { @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
    // 注册为监听器
    applicationContext.addApplicationListener(this);
    } @Override
    public void onApplicationEvent(WebServerInitializedEvent event) {
    String propertyName = “local.” + getName(event.getApplicationContext()) + “.port”;
    setPortProperty(event.getApplicationContext(), propertyName, event.getWebServer().getPort());
    }
    // … 省略部分代码 … }

    6. 总结

    • 在Spring容器刷新前,所有实现类的org.springframework.context.ApplicationContextInitializer#initialize方法都会被调用,所以可以通过实现ApplicationContextInitializer接口对Spring上下文环境作一些配置或操作。
    • ApplicationContextInitializer接口的实现方式有三种,可以根据项目需要选择合适的
    • 深入理解了ApplicationContextInitializer接口实现类的初始化时机和执行时机
    • 了解了一些内部实现类的作用和实现方法,可以学习到Spring本身是如何利用扩展接口实现一些功能,在实际的项目开发中具有一定的参考意义。