需要搞清楚几个重要的事件回调机制
配置在META-INF/spring.factories
- ApplicationContextInitializer
- SpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
CommandLineRunner
新建一个空项目,就勾选web,找到启动类,每个方法上面写的注释,可以看一下:
1 | @SpringBootApplication |
看到这里,就应该看到了,启动流程分为两步
- 创建SpringApplication对象
- 运行run方法
创建SpringApplication对象
1 | //上面的构造方法点进去 |
setInitializers()方法
看方法名就知道,这个是初始化方法,初始化什么东西呢?再看传的参数ApplicationContextInitializer,就是一开始我们提到的类。我们看这个是怎么获取的
1 | //第一步: |
所以,从上面的源码,我们一步一步点击进去看,我们就能发现,他最终都是加载到 META-INF/spring.factories 目录下的 ApplicationContextInitializer 当然,到目前为止这里只是初始化
setListeners()方法
一眼就能看出来,这里是设置监听方法
1 | setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); |
一眼看过去,这个setListener方法传的参数熟不熟悉?就是我们上面初始化的时候传的参数是同一个方法。所以,这里设置监听设置哪些监听方法也是META-INF/spring.factories 目录下的listener方法,我们看一下这个文件内容:
这些都是是自动配置类的内容
运行Run方法
1 | public ConfigurableApplicationContext run(String... args) { |
getRunListeners()方法
我们看到上面第一个有注释的位置:getRunListeners方法
1 | private SpringApplicationRunListeners getRunListeners(String[] args) { |
方法熟悉吗?就是我们上面初始化的时候调用的那个方法,只是这里的参数传的是:SpringApplicationRunListener,我们看最开始说的,这就是我们要了解的第二个内容,回调都是怎么调用的
方法里面的注释也写了,先获取监听事件,然后回调starting方法,我们看一下这个接口有那些回调方法:
1 | public interface SpringApplicationRunListener { |
就这些回调,这里还用了1.8的新特性,default关键字,接口里面的方法可以有方法体
prepareEnvironment()
看到第二个写注释的位置,眼熟吗?可不就是跟上面回调方法名字相同么?我们点进去看一下
1 | private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, |
准备完环境之后,调用environmentPrepared的回调
createApplicationContext()
1 | protected ConfigurableApplicationContext createApplicationContext() { |
创建applicationContext,这里跟1.5不一样,1.5就只有两种:一种是web的ioc容器,一种是默认的ioc容器。2.X这里有三种:DEFAULT_CONTEXT_CLASS,DEFAULT_SERVLET_WEB_CONTEXT_CLASS,DEFAULT_REACTIVE_WEB_CONTEXT_CLASS,实际字符串比较长,可以去看一下源码。然后用BeanUtils通过反射创建。
prepareContext()方法
1 | private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, |
准备上下文环境;将environment保存到ioc中;而且applyInitializers()
1 | protected void applyInitializers(ConfigurableApplicationContext context) { |
这里就将我们一开始从配置文件里面读取,然后创建ApplicationContextInitializer初始化。
当环境准备好之后,就回调了SpringApplicationRunListener的contextPrepared();
当所有的都准备好了之后,回调SpringApplicationRunListener的contextLoaded();
到这里,所有的环境都准备好了,需要打印的logo也加进去了。
refreshContext()
刷新容器这个方法,我们可以点到具体的功能实现里面,可以看到,这里就是扫描,创建,加载所有的组件,配置类,组件,自动配置等。
到这里,这个方法创建完之后,所有的控制器就创建完了,所有的组件,bean等,都在控制台打印出来了。如果是web应用,还会创建嵌入式的tomcat。我们spring boot项目内嵌tomcat,就是在这里创建的。
afterRefresh()
1.5版本这个方法里面回调的是callRunners方法,而2.X版本,现在这是个空方法里面并没有实现。callRunners被提出来了,放到了最后面。
started(),running()
在1.5版本这里,也就是afterRefresh()之后,应该是调用的SpringApplicationRunListeners的finished()方法。
在2.X版本之后,去掉了finished方法,改成了调用started方法,然后调用running方法。我们上面有一个starting方法,从这里名字就可以看出来,相当于,首先是正在启动当中,然后就是启动完成了,正在运行了。
callRunners()
1 | private void callRunners(ApplicationContext context, ApplicationArguments args) { |
从IOC容器中(不是配置文件)获取所有的的ApplicationRunner和CommandLineRunner进行回调。这也是最开始说的两个注意的地方
并且,这里有个先后顺序,先回调的ApplicationRunner,后回调的CommandLineRunner
这里也是run方法,最后执行的地方。从这里就是真正的开启了run。
最后一步,返回那个context就是返回IOC容器对象。到这里,我们的spring boot就启动完成了。
这就是我们的spring boot的启动原理。初始化,listener的回调,Runner的回调都说的很清楚。