在SpringBoot出现之前,我们部署一个java应用的方式如下图所示
而SpringBoot出现之后,内嵌了Tomcat、Jetty这样的Servlet容器,即无需再将应用打包成war部署,将应用打包成jar,直接运行一个jar包就能启动一个web服务。
实现原理
Tomcat-embed
Springboot能够将Tomcat内嵌,是因为Tomcat提供了一套JavaAPI,能够通过Tomcat tomcat = new Tomcat()来创建一个Tomcat容器。只需要引入Maven依赖
Springboot源码解读
任意一个Springboot应用,都有一个main()函数作为应用的启动方法,里面调用了SpringApplication.run(MyApplication.class, args)
public ConfigurableApplicationContext run(String... args) {
ConfigurableApplicationContext context = null;
// 创建spring容器对象 ApplicationContext
context = createApplicationContext();
// 做一些初始化容器之前的预备操作
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 启动spring容器核心方法,包括启动tomcat
refreshContext(context);
// 做一些容器启动之后的操作(当前这个方法是空的)
afterRefresh(context, applicationArguments);
return context;
}
这里的refreshContext(context),对spring的refresh()进行了封装
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 在容器启动之前做一些准备操作
prepareRefresh();
// 通知子类去刷新实例工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 初始化当前容器中的实例工厂
prepareBeanFactory(beanFactory);
try {
// 允许容器中的基类对实例工厂进行后置处理
postProcessBeanFactory(beanFactory);
// 调用实例工厂中的后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册实例的后置处理器,在创建实例时进行拦截
registerBeanPostProcessors(beanFactory);
// 初始化消息资源
initMessageSource();
// 初始化容器中的事件处理器
initApplicationEventMulticaster();
// 初始化一些在特定容器中特殊的实例
onRefresh();
// 检查监听器的实例并注册它们
registerListeners();
// 实例化所有非懒加载的实例
finishBeanFactoryInitialization(beanFactory);
// 最后一步:发布一些响应事件
finishRefresh();
}
catch (BeansException ex) {
}
finally {
}
}
}
这个方法调用完,spring容器就基本完成了初始化过程,tomcat也是在这个方法内部完成的创建
创建WebServer容器
Springboot内嵌的各种web容器实例,都是在onRefresh()中进行创建的
protected void onRefresh() throws BeansException {
// For subclasses: do nothing by default.
}
@Override
protected void onRefresh() {
super.onRefresh();
createWebServer();
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 通过工厂创建WebServer实例
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
}
创建Tomcat实例
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
// 1.创建一个tomcat临时文件路径
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 2.创建连接协议,默认使用HTTP1.1协议,NIO网络模型
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
// 3.创建主机,并关闭热部署
tomcat.getHost().setAutoDeploy(false);
// 4.配置引擎
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
// 5.初始化TomcatEmbeddedContext
prepareContext(tomcat.getHost(), initializers);
// 6.启动tomcat并返回TomcatWebServer对象
return getTomcatWebServer(tomcat);
}
getTomcatWebServer(tomcat)方法
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}
TomcatWebServer(tomcat,autoStart)方法
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
initialize()方法
private void initialize() throws WebServerException {
synchronized (this.monitor) {
try {
// 给Engine命名
addInstanceIdToEngineName();
// 获取Host中的Context
Context context = findContext();
// 绑定Context的生命周期监听器
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
removeServiceConnectors();
}
});
// 启动tomcat,触发初始化监听器
this.tomcat.start();
// 启动过程中子线程的异常从主线程抛出
rethrowDeferredStartupExceptions();
// 给当前Context绑定类加载器
ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
// tomcat的所有线程都是守护线程,这里启动一个阻塞的非守护线程来确保tomcat能及时停止
startDaemonAwaitThread();
}
catch (Exception ex) {
}
}
}
启动Web服务
真正完成springboot启动的方法,依然是由TomcatWebServer这个类完成的,这个类封装了控制整个web服务声明周期的方法,比如initialize(), start(), stop()等等,而这个start()显然就是web服务启动的方法了。
@Override
public void start() throws WebServerException {
synchronized (this.monitor) {
Connector connector = this.tomcat.getConnector();
if (connector != null && this.autoStart) {
performDeferredLoadOnStartup();
}
logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
+ getContextPath() + "'");
}
}
总结
Spring框架基于接口的设计模式,使得整个框架具有良好的扩展性。
首先,通过继承AbstractApplicationContext,重写onRefresh()对web容器进行初始化,重写finishRefresh()启动web服务。
其次,spring抽象了WebServer接口,提供了“启动”和“停止”两个基本方法,具体方法由不同的web容器各自实现,其中tomcat的实现类叫TomcatWebServer。
最后,TomcatWebServer在构造方法中调用initialize()初始化tomcat,调用start()方法启动web服务,在spring容器销毁之前调用stop()完成tomcat生命周期。
参考:https://mp.weixin.qq.com/s/B0of5OtN89bAn_wSgYNqPw