述
在Spring Boot项目的启动过程中,我们可以手动的去通过系统初始化器,往容器中设置系统变量,或者是进行一些其他的操作,本文将看一下系统初始化器的几种定义方式.
准备工作
首先新建一个空白的Spring Boot项目,启动测试没问题之后,开始下面的操作.
第一种方式
首先新建一个类 FirstInitializer
然后实现 ApplicationContextInitializer<ConfigurableApplicationContext>
然后实现 initialize
方法,具体代码如下:
1 | @Order(1) |
这里就是往环境变量中添加了一个属性k1,对应的值是v1,完成之后,还需要通过 spring.factories
文件来指定一下初始化器,否则这个类是不会被加载的
在 resource/META-INF/
目录下创建 spring.factories
文件,内容如下:1
org.springframework.context.ApplicationContextInitializer=com.zhou.springboot.example.initializers.FirstInitializer
测试验证
到这里,这个系统初始化器就可以正常的被spring容器加载,然后我们写一个测试类去验证,创建 TestService
,
代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14@Service
public class TestService implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public String test(){
return applicationContext.getEnvironment().getProperty("k1");
}
}
然后再写一个controller去调用就好了,启动项目,先看一下启动日志,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.4.RELEASE)
2020-01-31 11:43:42.107 INFO 9188 --- [ main] c.z.s.e.initializers.FirstInitializer : FirstInitializer done...
2020-01-31 11:43:42.158 INFO 9188 --- [ main] com.zhou.springboot.example.App : Starting App on DESKTOP-B1V47DV with PID 9188 (D:\Demo\SpringBoot\spring-boot-example\build\classes\java\main started by Administrator in D:\Demo\SpringBoot\spring-boot-example)
2020-01-31 11:43:42.160 INFO 9188 --- [ main] com.zhou.springboot.example.App : No active profile set, falling back to default profiles: default
2020-01-31 11:43:44.138 INFO 9188 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-01-31 11:43:44.153 INFO 9188 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
.......................
这里可以看到,我们的初始化器是正常被执行了的,然后调用controller方法,返回如下:
返回正常,说明这个初始化器是OK的.
第二种方式
下面来看一下第二种方式, 新建一个类 SecondInitializer
代码是和上面的 FirstInitializer
一样的,复制过来修改一下就好,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@Order(2)
@Slf4j
public class SecondInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 获取环境变量对象
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String, Object> map = new HashMap<>(1);
map.put("k2", "v2");
MapPropertySource mapPropertySource = new MapPropertySource("secondInitializer", map);
// 添加到环境变量中
environment.getPropertySources().addLast(mapPropertySource);
log.info("SecondInitializer done...");
}
}
然后这次要修改一下启动类,代码如下:1
2
3
4
5
6
7
8@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(App.class);
springApplication.addInitializers(new SecondInitializer());
springApplication.run(args);
}
}
这里通过 SpringApplication
对象来添加初始化器,然后启动项目看一下启动日志,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 . ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.4.RELEASE)
2020-01-31 13:24:43.569 INFO 12272 --- [ main] c.z.s.e.initializers.FirstInitializer : FirstInitializer done...
2020-01-31 13:24:43.576 INFO 12272 --- [ main] c.z.s.e.initializers.SecondInitializer : SecondInitializer done...
2020-01-31 13:24:43.602 INFO 12272 --- [ main] com.zhou.springboot.example.App : Starting App on DESKTOP-B1V47DV with PID 12272 (D:\Demo\SpringBoot\spring-boot-example\build\classes\java\main started by Administrator in D:\Demo\SpringBoot\spring-boot-example)
2020-01-31 13:24:43.604 INFO 12272 --- [ main] com.zhou.springboot.example.App : No active profile set, falling back to default profiles: default
2020-01-31 13:24:45.813 INFO 12272 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
......................
通过日志可以看到两个初始化器都是被正常加载的,然后他们加载的顺序也是我们通过 @Order
指定的顺序,然后也可以通过上面写的controller方法去验证一下
第三种方式
下面来看第三种方式, 还是先创建一个类 ThirdInitializer
,代码和前面的一样,还是复制过来修改一哈,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@Order(3)
@Slf4j
public class ThirdInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 获取环境变量对象
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Map<String, Object> map = new HashMap<>(1);
map.put("k3", "v3");
MapPropertySource mapPropertySource = new MapPropertySource("thirdInitializer", map);
// 添加到环境变量中
environment.getPropertySources().addLast(mapPropertySource);
log.info("ThirdInitializer done...");
}
}
然后这里要通过 application.yml
去添加这个初始化器,如下:1
2
3context:
initializer:
classes: com.zhou.springboot.example.initializers.ThirdInitializer
然后启动项目,看下输出日志,如下:1
2
3
4
5
6
7
8
9
10
11 . ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.4.RELEASE)
2020-01-31 13:38:15.059 INFO 15848 --- [ main] c.z.s.e.initializers.ThirdInitializer : ThirdInitializer done...
2020-01-31 13:38:15.064 INFO 15848 --- [ main] c.z.s.e.initializers.FirstInitializer : FirstInitializer done...
2020-01-31 13:38:15.064 INFO 15848 --- [ main] c.z.s.e.initializers.SecondInitializer : SecondInitializer done...
这里可以看到,这个初始化器是被正常加载了,但是这里有个问题,我们在 ThirdInitializer
指定的 @Order(3)
正常来说,他应该是最后一个执行的,但是这里变成了第一个执行的,这里是因为,在 application
配置中指定的系统初始化器是优先于前两种方法的
总结
- 三种方法的共同点都是要实现
ApplicationContextInitializer
接口 @Order
的值越小越先执行application
配置文件指定的方式优先于其他方式