总述:
- 自定义ContextRefreshedListener实现其onApplicationg()方法监听ContextRefreshedEvent事件。
- 实现ApplicationRunner/CommandLineRunner接口的run()方法,可定义初始化顺序,且一定是应用程序启动最后一步执行。
1.启动后初始化
如何理解?
SpringBoot启动后,需要做一些业务相关的自定义的初始化工作。
2.SpringBoot启动后自定义初始化
2.1 自定义监听器监听ContextRefreshedEvent事件
代码如下:
@Component
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("容器初始化结束,可以执行自定义初始化");
}
}
输出如下:
为什么在自定义监听器当中可以执行自定义初始化工作?
(1)对于ContextRefreshedEvent事件:
在SpringBoot程序启动过程中,ContextRefreshedEvent事件在容器初始化完成后会被发布。这意味着在SpringBoot启动结束后,可以通过自定义监听器监听ContextRefreshedEvent事件,然后在自定义监听器重写的onApplication方法中进行初始化操作。
代码如下:
//程序启动主要方法
public ConfigurableApplicationContext run(String... args) {
//...
refreshContext(context);
//...
return context;
}
//容器刷新主要方法
public void refresh() throws BeansException, IllegalStateException {
//...
// Last step: publish corresponding event.
finishRefresh();
//...
}
protected void finishRefresh() {
//...
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));//发布ContextRefreshedEvent事件
//...
}
(2)对于自定义监听器ContextRefreshedListener:
所有自定义监听器都需要实现ApplicationListener的onApplication()方法,这里用于自定义初始化的ContextRefreshedListener自定义监听器,只是监听了容器启动快结束时候的ContextRefreshedEvent事件。
SpringBoot在初始化 ApplicationContext 的时候,会从当前的 bean 中找到 ApplicationListener 类型的 bean,将这些 bean 注册到 ApplicationContext 的事件发布器上,具体实现原理见这篇文章。
protected void registerListeners() {
...
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
..
}
2.2 实现ApplicationRunner/CommandLineRunner接口的run方法
这两个接口是SpringBoot提供的专门用于初始化的,只需要实现他们的run()方法即可。此外,还可以在上述2个接口实现类上加入Order注解,以达到顺序初始化的目的。
@Order(1)
@Component
public class AppStartupRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("初始化代码");
}
}
有了前面自定义ContextRefreshedListener监听器,为什么还要提供这两个初始化接口?
为了将context当中监听器和初始化做隔离。面提到的ContextRefreshedEvent事件和对应的自定义ContextRefreshedListener监听器,本质上还是利用事件监听器做初始化。而ApplicationRunner
和CommandLineRunner接口是专门用于初始化工作的接口。
此外,上述2个接口在应用启动过程中,必定是在SpringBoot程序启动的最后一步调用的,源码如下:
public ConfigurableApplicationContext run(String... args) {
//...
callRunners(context, applicationArguments);
}
callRunners具体实现:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);//按照order排序
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
//先执行实现ApplicationRunner接口的自定义runner
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
最终实现其实还是调用runner.run()方法:
(runner).run(args);
2.3 ApplicationRunner和CommandRunner接口有什么区别?
这两个接口的不同之处在于:ApplicationRunner 中 run 方法的参数封装为了ApplicationArguments对象,而CommandLineRunner接口中run方法的参数为String数组。
源码对比如下:
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
参考链接: