【springboot源码分析】- ApplicationContextInitializer的作用及源码分析

文章目录

  • 1 ApplicationContextInitializer的作用
  • 2 测试ApplicationContextInitializer功能
  • 2 ApplicationContextInitializer源码
  • 3 总结一下相关的面试题

本博客源码地址
https://github.com/suchahaerkang/spring-boot-study.git

1 ApplicationContextInitializer的作用

ApplicationContextInitializer 是在spring容器刷新之前执行的一个回调函数。它的作用是向springboot容器中注册属性
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第1张图片

2 测试ApplicationContextInitializer功能

将ApplicationContextInitializer的实现类注册到容器中有三种方式:
1)在spring.factories内配置ApplicationContextInitializer的实现类的方式来注册
首先自己写一个ApplicationContextInitializer的实现类

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-22 20:34
 */
@Order(1)
public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        //从容器中获取环境变量组件
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        //自己自定义一些属性放在map集合中
        Map<String, Object> map = new HashMap<>();
        map.put("key1", "value1");
        //包装为MapPropertySource组件
        MapPropertySource mapPropertySource = new MapPropertySource("firstInitializer", map);
        //存到环境变量中去
        environment.getPropertySources().addLast(mapPropertySource);
        System.out.println("FirstInitializer属性注册成功...");
    }
}

然后在src/main/resources目录下新建/META-INF/spring.factories,将FirstInitializer组件的类路径配置进去
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第2张图片
在UserService中写一个方法来获取环境变量中key为key1的value值
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第3张图片
在UserController中写一个方法调用UserService的getInitializerValue()方法
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第4张图片
启动项目测试结果如下
在这里插入图片描述
2)自己手动注册
重新再自定义一个初始化器

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-22 20:34
 */
@Order(2)
public class SecondInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        //从容器中获取环境变量组件
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        //自己自定义一些属性放在map集合中
        Map<String, Object> map = new HashMap<>();
        map.put("key2", "value2");
        //包装为MapPropertySource组件
        MapPropertySource mapPropertySource = new MapPropertySource("secondInitializer", map);
        //存到环境变量中去
        environment.getPropertySources().addLast(mapPropertySource);
        System.out.println("SecondInitializer属性注册成功...");
    }
}

手动注册初始化器

@SpringBootApplication
@MapperScan("com.wolfx.springbootstudy.dao")
public class SpringBootStudyApplication {

    public static void main(String[] args) {
        //SpringApplication.run(SpringBootStudyApplication.class, args);

        SpringApplication springApplication = new SpringApplication(SpringBootStudyApplication.class);
        //手动注册初始化器
        springApplication.addInitializers(new SecondInitializer());
        springApplication.run(args);
    }

}

Controller和Service文件
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第5张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第6张图片
运行结果
在这里插入图片描述
3)在application.properties文件中进行配置
首先我们再自定义一个初始化器

/**
 * @description:
 * @author: sukang
 * @date: 2020-03-22 20:34
 */
@Order(3)
public class ThirdInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        //从容器中获取环境变量组件
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        //自己自定义一些属性放在map集合中
        Map<String, Object> map = new HashMap<>();
        map.put("key3", "value3");
        //包装为MapPropertySource组件
        MapPropertySource mapPropertySource = new MapPropertySource("thirdInitializer", map);
        //存到环境变量中去
        environment.getPropertySources().addLast(mapPropertySource);
        System.out.println("ThirdInitializer属性注册成功...");
    }
}

然后在application.properties文件中进行配置将ThirdInitializer注册到容器中去

#initializer
context.initializer.classes=com.wolfx.springbootstudy.initializer.ThirdInitializer

Controller和Service文件
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第7张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第8张图片
测试结果
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第9张图片
以上的结果说明这三种方式都成功的将初始化器注册到了容器中了。上面我们编写三个初始化实现类FirstInitializer,SecondInitializer和ThirdInitializer的order值分别是1,2,3 ,按理说应该是按照这个顺序回调他们重写的initialize()方法,但是在控制台中打印的结果和我们想象的不一样
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第10张图片
下面我们带着这个问题来看源码,看一下ApplicationContextInitializer回调方法的执行时机

2 ApplicationContextInitializer源码

我们以创建SpringApplication为入口
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第11张图片
进入getSpringFactoriesInstances()方法
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第12张图片
我们看一下SpringFactoriesLoader组件的作用
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第13张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第14张图片
我们进入loadSpringFactories()方法
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第15张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第16张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第17张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第18张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第19张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第20张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第21张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第22张图片
前面我们知道ApplicationContextInitializer是在容器刷新之前回调其方法实现自定义一些属性值,那么我们从SpringApplication.run()方法进去看一下源码
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第23张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第24张图片
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第25张图片
在此我们已经清楚了通过SpringFactoriesLoader和手动注册的初始化器是怎么注册的已经怎么回调他们的方法的,但是我们还是不知道
ThirdInitializer为啥Order为3,但是第一个执行回调方法,其实他是通过DelegatingApplicationContextInitializer这个组件实现的。DelegatingApplicationContextInitializer组件也是ApplicationContextInitializer的一个实现类并且order为0
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第26张图片
DelegatingApplicationContextInitializer配置在spring.factories中,所以会被SpringFactoriesLoader读取注册到容器中去,等执行run()方法的
时候,首先回调的是DelegatingApplicationContextInitializer的initialize()方法,因为它的order为0,所以最优先执行
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第27张图片
执行DelegatingApplicationContextInitializer.initialize(ConfigurableApplicationContext context)方法
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第28张图片
总结一下初始化器的实现类initialize()方法的回调流程
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第29张图片
总结一下实现方式
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第30张图片
一般推荐使用第一种方式
【springboot源码分析】- ApplicationContextInitializer的作用及源码分析_第31张图片

3 总结一下相关的面试题

1)介绍一下SpringFactoriesLoader?
SpringFactoriesLoader其实就是spring的工厂加载类,作用是完成扩展点的实现载入

2)SpringFactoriesLoader是如何加载工厂类的?
通过读取/META-INF/spring.factories文件,将其转为Property对象,然后获取遍历value值,得到相应的类路径名,并且根据Order排序

3)系统初始化器的作用?
它其实就是spring容器的一个回调接口,在容器刷新之前执行,主要作用是给可以自定义一些属性值

你可能感兴趣的:(spring-boot)