运行流程分析、自定义事件监听回调机制
一、运行流程分析
1、启动流程:
首先观察SpringBoot启动类源码:
SpringApplication.run(SpringbootDemoApplication.class, args);
//进入run方法===》发现:
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[]{source}, args);
}
//再进入run方法===》发现:
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return (new SpringApplication(sources)).run(args);
}
由最后的方法可以发现,启动应用主要做了两件事:①、创建SpringApplication
对象;②、运行run
方法。
对此进行分析:
①、创建SpringApplication对象:(initialize(sources))
private void initialize(Object[] sources) {
/* 1、保存主配置类 */
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
/*2、判断当前是否一个web应用*/
this.webEnvironment = deduceWebEnvironment();
//3、从依赖包类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起来
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//4、从依赖包类路径下找到META-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//5、从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
通过源码可以发现其有两个关键点:(附断点调试截图)
保存所有的ApplicationContextInitializer
:
保存所有的ApplicationListener
到此SpringApplication对象
就创建完了,接下来就运行其run方法。
②、运行run方法:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//1、声明一个空的IOC容器
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//2、获取SpringApplicationRunListeners;从类路径下META-INF/spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
//3、回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//4、封装传入的命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//5、准备环境:创建环境完成后回调SpringApplicationRunListener.environmentPrepared(),表示环境准备完成
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
//6、打印banner信息(就是spring的图标,可以自己替换)
Banner printedBanner = printBanner(environment);
//7、创建ApplicationContext;决定创建web的IOC还是普通的IOC
context = createApplicationContext();
//8、用来处理异常错误报告的,【catch】模块中使用
analyzers = new FailureAnalyzers(context);
//9、准备上下文环境;将【environment】对象保存到IOC中;
//回调第一步中保存的所有的【ApplicationContextInitializer】的【initialize()】方法
//回调所有第一步中保存的【SpringApplicationRunListener】的【contextPrepared()】方法;
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//10、【prepareContext()】运行完成以后回调所有的第一步中保存的【SpringApplicationRunListener】的【contextLoaded()】方法;
//11、刷新容器:既开始IOC容器初始化(如果是web应用还会创建嵌入式的Tomcat);
//12、扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
refreshContext(context);
//13、从IOC容器中获取所有的【ApplicationRunner】和【CommandLineRunner】进行回调
//【ApplicationRunner】先回调,【CommandLineRunner】再回调
afterRefresh(context, applicationArguments);
//14、所有的【SpringApplicationRunListener】回调【finished()】方法
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//15、整个SpringBoot应用启动完成以后返回IOC容器;
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
2、事件回调机制:
由上述源码run()
方法中发现几个重要的事件回调机制:
类型 | 描述 | 获取方式 |
---|---|---|
ApplicationContextInitializer | IOC容器初始化时被回调 | 需要配置在META-INF/spring.factories,因为SpringBoot启动流程中是从spring.factories中获取的 |
SpringApplicationRunListener | SpringBoot启动过程中多次被回调 | 需要配置在META-INF/spring.factories,因为SpringBoot启动流程中是从spring.factories中获取的 |
ApplicationRunner | 容器启动完成后被回调 | 需要放在IOC容器中,因为SpringBoot启动流程中是从IOC容器中取出的 |
CommandLineRunner | ApplicationRunner 之后被回调 |
需要放在IOC容器中,因为SpringBoot启动流程中是从IOC容器中取出的 |
二、自定义事件监听机制
1、ApplicationContextInitializer
(配置在META-INF/spring.factories)
①、创建自定义ApplicationContextInitializer
:
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
/* 初始化时会执行,并传入容器 */
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("【初始化时】 ------- ApplicationContextInitializer...initialize..."+applicationContext);
}
}
②、配置META-INF/spring.factories
在resources
文件夹下创建文件夹META-INF
,而后在创建文件spring.factories
#仿照依赖包中配置方式,第一行【接口】第二行【实现类】
org.springframework.context.ApplicationContextInitializer=\
com.wangcw.springboot.listener.MyApplicationContextInitializer
2、SpringApplicationRunListener
(配置在META-INF/spring.factories)
①、创建自定义SpringApplicationRunListener
:
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
//必须有的构造器,否则启动会报错
public MySpringApplicationRunListener (SpringApplication application, String[] args){
}
/* 有启动流程分析可知,其执行顺序是由上至下的 */
@Override
public void starting() {
System.out.println("【应用运行监听】----------SpringApplicationRunListener...starting...");
}
/* 参数传入环境信息 */
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
Object o = environment.getSystemProperties().get("os.name");
System.out.println("【应用运行监听】----------SpringApplicationRunListener...environmentPrepared.."+o);
}
/* 参数传入容器 */
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("【应用运行监听】----------SpringApplicationRunListener...contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("【应用运行监听】----------SpringApplicationRunListener...contextLoaded...");
}
@Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("【应用运行监听】----------SpringApplicationRunListener...finished...");
}
}
②、配置META-INF/spring.factories
#仿照依赖包中配置方式,第一行【接口】第二行【实现类】
org.springframework.context.ApplicationContextInitializer=\
com.wangcw.springboot.listener.MyApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.wangcw.springboot.listener.MySpringApplicationRunListener
3、ApplicationRunner
(只需要放在ioc容器中)
创建ApplicationRunner
并加入到容器即可。
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("【容器启动完成后被回调】--------------ApplicationRunner...run....");
}
}
4、CommandLineRunner
(只需要放在ioc容器中)
创建CommandLineRunner
并加入到容器即可。
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("【容器启动完成后被回调】--------------CommandLineRunner...run..."+ Arrays.asList(args));
}
}
启动验证即可!