Spring 容器在刷新之前会初始化 ConfigurableApplicationContext 的回调接口,简单来说,就是在调用 [[refreshContext]] 刷新容器之前会执行此类的 initialize 方法。通过该拓展点,用户可以在整个spring容器还没被初始化之前做一些事情。

1、拓展分析

1.1 执行流程

如上图所示,主要入口为 applyInitializers 方法,该方法唯一作用是直接将已经注册的 Initializers 对象进行调用,完成相应的初始化操作。

// 3. 在这里将所有已经注册进来的 ApplicationContextInitializer 进行调用
protected void applyInitializers(ConfigurableApplicationContext context) {
    for (ApplicationContextInitializer initializer : getInitializers()) {
        Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
        Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
        initializer.initialize(context);
    }
}

1.2 注册流程

上述提到有一个注册 ApplicationContextInitializer 实现的地方,该注册方法在 SpringApplication 初始化的时候被调用。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = deduceWebApplicationType();
    // 注册初始化对象位置
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

重点关注的就是 getSpringFactoriesInstances 方法,从命名中不难猜出来该方法是获取目标对象,并返回一个实现类的数组,完成注册。逐层追踪下去,可以发现最终在一个 getSpringFactoriesInstances 方法中进行实例化对象。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                                                       classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

整体流程就是 获取类加载器 --> 获取拓展 spring.factories 类名(这里使用了 Java 的 [[SPI]] 机制) --> 加载具体对象,最终将所有需要执行注册的 ApplicationContextInitializer 返回。

2、拓展使用

针对于该拓展可以想到的场景可能为,在最开始激活一些配置,或者利用这时候 class 还没被类加载器加载的时机,进行动态字节码注入等操作。

扩展方式为:

public class TestApplicationContextInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {}
}

因为这时候 spring 容器还没被初始化,所以想要自己的扩展的生效,有以下三种方式:

  1. 在启动类中用 springApplication.addInitializers(new TestApplicationContextInitializer()) 语句加入;

  2. 配置文件配置 context.initializer.classes=com.example.demo.TestApplicationContextInitializer

  3. Spring SPI 扩展,在 spring.factories 中加org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer