Spring Boot-0-系统初始化器

在Spring Boot项目的启动过程中,我们可以手动的去通过系统初始化器,往容器中设置系统变量,或者是进行一些其他的操作,本文将看一下系统初始化器的几种定义方式.

准备工作

首先新建一个空白的Spring Boot项目,启动测试没问题之后,开始下面的操作.

第一种方式

首先新建一个类 FirstInitializer 然后实现 ApplicationContextInitializer<ConfigurableApplicationContext> 然后实现 initialize 方法,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Order(1)
@Slf4j
public class FirstInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 获取环境变量对象
ConfigurableEnvironment environment = applicationContext.getEnvironment();

Map<String, Object> map = new HashMap<>(1);
map.put("k1", "v1");

MapPropertySource mapPropertySource = new MapPropertySource("FirstInitializer", map);

// 添加到环境变量中
environment.getPropertySources().addLast(mapPropertySource);

log.info("FirstInitializer done...");
}
}

这里就是往环境变量中添加了一个属性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方法,返回如下:
image

返回正常,说明这个初始化器是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
3
context:
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 配置文件指定的方式优先于其他方式