springboot的启动原理和流程
本文基于springboot 2.2.13版本
一、springboot启动的原理
基于springboot的web应用本质上还是一个spring应用,所以也会有一个spring容器,也需要一个web容器。
在以前基于ssm进行开发的时候,使用的web容器是外部的,比如tomcat,整个应用打包后会被部署在外部tomcat容器中,而在springboot应用中使用的是内嵌的web容器。
所以总结下来,springboot的启动过程中涉及一个spring容器和一个内嵌的tomcat。
我们先用一段代码来模拟下springboot启动过程中容器的创建和内嵌tomcat的启动
springboot为web应用提供了一个容器AnnotationConfigServletWebServerApplicationContext
public class BootTest {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
context.register(MyAppConfig.class);
context.refresh();
}
@Configuration
static class MyAppConfig{
// 产生内嵌web容器的bean
@Bean
public ServletWebServerFactory servletWebServerFactory(){
TomcatServletWebServerFactory servletWebServerFactory = new TomcatServletWebServerFactory();
servletWebServerFactory.setPort(8080);
return servletWebServerFactory;
}
}
}
上边的代码先创建了一个spring容器,然后注册了一个配置类,为了方便演示直接把配置类定义成了内部了类。
在配置类中添加了一个ServletWebServerFactorybean,这个bean从名字就可以看出来是用来创建内嵌web容器的。
运行上边的main方法从控制台打印信息就可以看出来tomcat服务已经正常启动了,只不过没有注册任何web资源到tomcat中,所以不能访问。

下面我们尝试利用配置类给tomcat中注册一些springmvc相关的内容
public class BootTest {
public static void main(String[] args) {
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
context.register(MyAppConfig.class);
context.refresh();
}
@Configuration
static class MyAppConfig{
//内嵌tomcat
@Bean
public ServletWebServerFactory servletWebServerFactory(){
TomcatServletWebServerFactory servletWebServerFactory = new TomcatServletWebServerFactory();
servletWebServerFactory.setPort(8080);
return servletWebServerFactory;
}
@Bean
public DispatcherServlet dispatcherServlet(){
return new DispatcherServlet();
}
//spring容器通过这个bean用来注册servlet到内嵌tomcat中
@Bean
public ServletRegistrationBean<DispatcherServlet> servletRegistrationBean(DispatcherServlet dispatcherServlet){
ServletRegistrationBean<DispatcherServlet> registrationBean = new ServletRegistrationBean<DispatcherServlet>();
registrationBean.setServlet(dispatcherServlet);
registrationBean.addUrlMappings("/");
return registrationBean;
}
//注册处理器映射器 RequestMappingHandlerMapping
@Bean
public RequestMappingHandlerMapping handlerMapping(){
return new RequestMappingHandlerMapping();
}
//处理器适配器
@Bean
public RequestMappingHandlerAdapter handlerAdapter(){
RequestMappingHandlerAdapter handlerAdapter = new RequestMappingHandlerAdapter();
// 添加一个转换器 为了处理json数据
handlerAdapter.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
return handlerAdapter;
}
@Bean
public HelloController helloController(){
return new HelloController();
}
}
@RequestMapping("/hello")
static class HelloController{
//返回json数据给客户端
@GetMapping("/test1")
@ResponseBody
public Map<String,String> test1(){
Map<String,String> map = new HashMap<>();
map.put("code","200");
map.put("message","success");
return map;
}
}
}
上边利用配置类给spring容器中配置了springmvc运行需要的四大器和Controller,再次启动main方法就可以通过
http://localhost:8080/hello/test1来访问内嵌tomcat
那么spring容器是在什么时机利用ServletWebServerFactory创建tomcat的?
其实是在这个容器AnnotationConfigServletWebServerApplicationContext的onRefresh方法中从容器里获取
ServletWebServerFactory类型的bean并创建web容器,这个方法定义在父类ServletWebServerApplicationContext中
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
而这个onRefresh方法是在spring容器的refresh方法中调用的
二、springboot的启动流程
上面讲解了springboot应用启动的本质是spring容器+内嵌tomcat,接下来我们来看下一个标准springboot应用的启动流程
一个springboot应用中我们一般会写出这样的启动类
@SpringBootApplication
public class BootTest2 {
public static void main(String[] args) {
SpringApplication.run(BootTest2.class,args);
}
}
所以启动流程就是从SpringApplication.run这个静态方法开始的,整个流程可以总结为两个大的步骤
2.1 创建一个SpringApplication对象
这个步骤主要做了这几步操作
2.1.1 应用类型推断
这一步会推断应用类型,这个应用类型会决定后边创建的spring容器的类型
2.1.2 创建初始化器
这一步会收集应用中ApplicationContextInitializer.class这个接口的实现类然后创建对象设置到SpringApplication对象的属性中
2.1.3 创建事件监听器
这一步会收集应用中ApplicationListener.class这个接口的实现类然后创建对象设置到SpringApplication对象的属性中
2.1.4 主类推断
2.2 执行SpringApplication对象的run方法
在这一步主要做了以下步骤
2.2.1 创建事件发布器
这一步会收集应用中SpringApplicationRunListener.class这个接口的实现类,然后创建对象,默认情况下springboot只提供了一个实现类EventPublishingRunListener,可以使用此对象在springboot启动过程中的不同阶段发布事件,为了兼容有多个实现类的情况,springboot使用SpringApplicationRunListeners对事件发布器类进行了一个包装。
当事件发布器发布了某一个事件时,对应的事件监听器就会收到事件然后执行。
2.2.2 发布starting事件
2.2.3 对启动参数args做一个封装
封装启动参数为ApplicationArguments对象
2.2.4创建并准备环境对象
在这一步中会创建环境对象 ConfigurableEnvironment ,springboot会使用这个对象来读取启动参数,配置文件等配置中的参数,我们常用的application.properties就是通过这个对象读取的。
在这个过程中会发布environmentPrepared事件,注意给环境对象中配置针对application.properties文件的解析器就是通过ConfigFileApplicationListener这个监听器监听 environmentPrepared事件实现的。
2.2.5 打印banner
2.2.6 创建spring容器
根据前边推导的应用类型创建对应的spring容器
2.2.7执行初始化器
执行前边收集到的初始化器,ApplicationContextInitializer实现类,对spring容器进行加强。
2.2.8 发布contextPrepared事件
2.2.9 加载bean配置信息到spring容器中
调用SpringApplication.load方法加载各种途径的bean配置信息到容器中
2.2.10 发布contextLoaded事件
2.2.11 执行spring容器的refresh方法
容器刷新
2.2.12 发布 started事件
2.2.13 调用Runner接口
从容器中获取 ApplicationRunner和CommandLineRunner这两个接口的实现类,然后执行
2.2.14 发布running事件
至此springboot就启动完成了。
关于具体的源码就不贴了,按着这个脉络去看springboot的启动源码就可以对应上了。
浙公网安备 33010602011771号