【SpringBoot源码解析】第三章:SpringBoot通过打成war包的方式是如何启动的
缘起
在前面几章的讲解中,我们知道,当我们执行以下代码时,springboot会启动一个内置的tomcat,并且加载对应的starter.那么如果我们不采用java -jar的方式启动springboot的应用,他也就没有去执行run方法,那么他又是如何做到自动装配的呢?
SpringBoot通过war的方式是如何启动的
关于SPI
在说这些之前,我们先要了解一个东西,SPI。关于SPI可以去了解我的另一篇文章
SPI在springboot中的应用
我们看spring-web这个项目的spi文件javax.servlet.ServletContainerInitializer
文件内容如下
org.springframework.web.SpringServletContainerInitializer
SpringServletContainerInitializer
代码如下
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
//省略其余代码
}
可以看到这个类继承了一个叫ServletContainerInitializer
的接口
public interface ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException;
}
这个接口是干嘛的呢?ServletContainerInitializer
是 Servlet 3.0 新增的一个接口,主要用于在容器启动阶段通过编程风格注册Filter
, Servlet
以及Listener
,以取代通过web.xml
配置注册。这样就利于开发内聚的web应用框架。容器启动阶段依据java spi
获取到所有ServletContainerInitializer
的实现类,然后执行其onStartup
方法.
也就是说,我们把 ServletContainerInitializer
的实现类写在 META-INF / services / javax.servlet.ServletContainerInitializer
文件中,那么Tomcat等容器启动的时候就会去调用所有实现类的onStartup
方法。
@HandlesTypes注解是干嘛的呢?
SpringServletContainerInitializer
就是ServletContainerInitializer
的实现类,可以看到SpringServletContainerInitializer
加上了一个@HandlesTypes(WebApplicationInitializer.class)
的注解,这个注解的作用就是容器启动的时候调用实现类的onStartup
方法的时候,会把注解中标注的接口的实现类当做参数传递进去。
我们看SpringServletContainerInitializer
的onStartup
方法,在容器启动的时候会调用这个方法,同时set
集合参数webAppInitializerClasses
即为@HandlesTypes
中标注的WebApplicationInitializer
的实现类
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
//省略其余代码
for (WebApplicationInitializer initializer : initializers) {
//依次回调参数实现类的onStartup方法
initializer.onStartup(servletContext);
}
}
最后一段代码initializer.onStartup(servletContext);
就是把所有的WebApplicationInitializer
的实现类的onStartup
方法调用一遍。我们看看这个类的所有实现类
看到实现类中有一个SpringBootServletInitializer
,这个类是我们要重点关注的对象,先来看看这个类的注释
也就是说这个类是当我们以war包的方式让外部tomcat运行时才需要关注的类。
我们接着看这个类的onStartup
方法
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
// no-op because the application context is already initialized
}
});
}
}
createRootApplicationContext
方法
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
//其余代码略
return run(application);
}
run
方法
protected WebApplicationContext run(SpringApplication application) {
return (WebApplicationContext) application.run();
}
可以看到最终上诉会调用到SpringApplication的无参run方法,那么到了这一步,就跟我们通过main方法启动是一个道理了
public static void main(String[] args) {
SpringApplication.run(HppaApplication.class, args);
}
总结
SpringBoot通过打成war包的方式运行,其本质上是利用了Servlet3.0规范中的Tomcat启动时会去调用ServletContainerInitializer
接口的onStartup
方法,同时把使用类注解@HandlesTypes
中标注的接口的实现类作为参数传入到onStartup
中,并依次调用其实现类的onStartup
方法。而SpringServletContainerInitializer
实现了ServletContainerInitializer
,同时标注了@HandlesTypes(WebApplicationInitializer.class)
,
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
//省略其余代码
}
那么也就是说,Tomcat启动时,最终会去调用WebApplicationInitializer
的实现类的onStartup
方法,而SpringBootServletInitializer
实现了WebApplicationInitializer
public abstract class SpringBootServletInitializer implements WebApplicationInitializer
那也就是最终会调用SpringBootServletInitializer
的onStartup
方法,而这个onStartup
方法最终其实是调用了application.run()
,也就类似于我们通过Main方法启动了。为了方便理解,我画了下图
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 『三个白帽』某题的writeup
- 创造tips的秘籍——PHP回调后门
- Joomla远程代码执行漏洞分析(总结)
- 那些年我拿下的demo站之方维O2O
- emlog自动备份插件泄露整站数据库备份漏洞
- md5(unix)原理分析
- XDCTF2015代码审计全解
- 使用腾讯云TCB云函数抓取微信情报信息
- 数据库PostrageSQL-服务器配置预写式日志
- 分享一个jsonp劫持造成的新浪某社区CSRF蠕虫
- 数据库PostrageSQL-服务器配置资源消耗
- 数据库PostrageSQL-服务器配置连接和认证
- 浏览器安全一 / Chrome XSS Auditor bypass
- 数据库PostrageSQL-服务器配置文件位置
- 数据库PostrageSQL-服务器配置设置参数