SpringBoot项目通过SpringApplication.run()运行,分为两步
首先new了一个SpringApplication,之后再调用run()方法 ,下面我们就看看这两步
通过debug进入到SpringApplication的创建中
首先判断当前项目是否是web项目,再在所有jar包的META-INF/spring.factories文件中查找ApplicationContextInitializer和ApplicationListener并保存,之后再通过所有配置类获得带有main方法的主运行类
获得的所有ApplicationContextInitializer类型
获得所有的ApplicationListener类型,到这里SpringApplication就创建完成
通过debug进入其中
在这里首先通过spring.factories文件获得所有SpringApplicationRunListener,循环调用他们的starting方法
封装命令行参数
接下来就是准备环境,通过debug进入其中
发现,首先创建环境,在回调SpringApplicationRunListener的environmentPrepared()方法,表示环境准备完成
之后就创建ApplicationContext,进入其中
发现,通过判断创建不同容器,最后通过反射创建,因为我们这里是web环境,就会得到AnnotationConfigServletWebServerApplicationContext
接下来,准备上下文
进入其中
发现将之前创建的容器添加到其中,主要调用了applyInitializers(context);方法,这个方法时干啥的呢?进入其中
发现在循环所有的ApplicationContextInitializer,回调他们的initialize()方法,而这些ApplicationContextInitializer就是在SpringApplication创建时,在spring.factories文件中加载的ApplicationContextInitializer。所有的ApplicationContextInitializer的initialize()方法回调完毕,又回调SpringApplicationRunListeners的contextPrepared()方法
接下来
回调SpringApplicationRunListeners的contextLoaded()方法,至此prepareContext()方法就调用完毕,之后来到refreshContext(context);方法,刷新容器,就是IOC容器初始化的过程
IOC初始化调用refresh()方法(不熟悉IOC容器的初始化的可以看https://blog.csdn.net/qq_36625757/article/details/83856575这篇博客)
refreshContext(context);执行完毕,IOC容器就创建完毕了,所有的bean就创建完毕,如果是web应用Tomcat或其他Servlet容器也创建完毕,之后就调用所有SpringApplicationRunListeners的started()方法
最后调用callRunners(context, applicationArguments);方法,进入其中
发现从容器中出的ApplicationRunner及CommandLineRunner,并先调用ApplicationRunner在调用CommandLineRunner,说明他们之间存在优先级,之后再调用所有SpringApplicationRunListeners的running()方法
最后SpringBoot启动完成,返回IOC容器
1.配置在META-INF/spring.factories的ApplicationContextInitializer,SpringApplicationRunListener
2.通过容器获得的ApplicationRunner,CommandLineRunner,下面我们就看看这几个类
1.创建ApplicationContextInitializer实现
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
//需要配置在spring.factories文件中
public class HelloApplicationContextInitializer implements ApplicationContextInitializer {//监听ConfigurableApplicationContext
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("HelloApplicationContextInitializer.initialize()方法运行啦!!!!!!!!!!!!!!!!!!!!");
}
}
2.创建SpringApplicationRunListener的实现类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
//需要配置在spring.factories文件中
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
//必须要有这个构造器
public HelloSpringApplicationRunListener(SpringApplication application, String[] args) {}
//这边在spring刚拿到所有的SpringApplicationRunListener实现类后就直接调用了starting 通知各个SpringApplicationRunListener的实现类
@Override
public void starting() {
System.out.println("HelloSpringApplicationRunListener.starting()调用啦!!!!!!!!!!!!");
}
//在准备好了ConfigurableEnvironment环境的时候通知各个实现类
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
Object osName = environment.getSystemProperties().get("os.name");//获得操作系统名称
System.out.println("HelloSpringApplicationRunListener.environmentPrepared()调用啦!!!!!!!!!!!!"+osName);
}
//在spring 结束applyInitializers后进行通知
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener.contextPrepared()调用啦!!!!!!!!!!!!");
}
//在加载完资源类(启动类)后触发
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener.contextLoaded()调用啦!!!!!!!!!!!!");
}
//在所有的bean 都初始化后 进行触发
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener.started()调用啦!!!!!!!!!!!!");
}
//在调用完所有的ApplicationRunner和CommandLineRunner实现类后调用
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("HelloSpringApplicationRunListener.running()调用啦!!!!!!!!!!!!");
}
//应用程序发生故障时调用
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("HelloSpringApplicationRunListener.failed()调用啦!!!!!!!!!!!!");
}
}
3.创建ApplicationRunner的实现类
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component//加入到容器中
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("HelloApplicationRunner.run()方法调用啦!!!!!!!!!!!!!!");
}
}
4.创建CommandLineRunner的实现类
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component//加入到容器中
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("HelloCommandLineRunner.run()方法调用啦!!!!!!!!!!!!!!!");
}
}
5.配置ApplicationContextInitializer及SpringApplicationRunListener,在类路径下创建spring.factories文件,编辑为
org.springframework.context.ApplicationContextInitializer=\
com.peng.demo.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.peng.demo.listener.HelloSpringApplicationRunListener
6.运行发现,在应用启动过程中回调了不同的方法,至于在啥时候回调的,前面流程分析中已有介绍
查看了SpringBoot启动流程,最主要就是ApplicationContextInitializer,SpringApplicationRunListener,ApplicationRunner,CommandLineRunner四个接口实现类方法的回调,这四个接口的主要作用就是在SpringBoot 启动初始化的过程中可以通过四个接口实现类方法回调来让用户在启动的各个流程中可以加入自己的逻辑