Spring Boot 中的 Tomcat 是如何启动的?
作者:木木匠
https://my.oschina.net/luozhou/blog/3088908
从 Main 方法说起
public class TomcatdebugApplication {
public static void main(String[] args) {
SpringApplication.run(TomcatdebugApplication.class, args);
}
}
run方法是调用ConfigurableApplicationContext方法,源码如下:createApplicationContext() 和refreshContext(context),接下来我们来看看这两个方法做了什么。Class<!--?--> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
webApplicationType来判断创建哪种类型的 Servlet,代码中分别对应着 Web 类型(SERVLET),响应式 Web 类型(REACTIVE),非 Web 类型(default),我们建立的是Web类型,所以肯定实例化DEFAULT_SERVLET_WEB_CONTEXT_CLASSAnnotationConfigServletWebServerApplicationContext类,我们来用图来说明下这个类的关系。ServletWebServerApplicationContext,这就是我们真正的主角,而这个类最终是继承了AbstractApplicationContext,了解完创建上下文的情况后,我们再来看看刷新上下文,相关代码如下:refresh(context)方法,最后是强转成父类AbstractApplicationContext调用其refresh()方法,该代码如下:public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.这里的意思就是调用各个子类的onRefresh()
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
onRefresh()方法是调用其子类的实现,根据我们上文的分析,我们这里的子类是ServletWebServerApplicationContext。createWebServer()就是启动 Web 服务,但是还没有真正启动 Tomcat,既然webServer是通过ServletWebServerFactory来获取的,我们就来看看这个工厂的真面目。走进 Tomcat 内部
TomcatServletWebServerFactory.getWebServer()的实现。Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
configureEngine,这连接器我们勉强能理解(不理解后面会述说),那这个Engine是什么呢?我们查看tomcat.getEngine()的源码:if (service.getContainer() != null) {
return service.getContainer();
}
Engine engine = new StandardEngine();
engine.setName( "Tomcat" );
engine.setDefaultHost(hostname);
engine.setRealm(createDefaultRealm());
service.setContainer(engine);
return engine;
}
Container接口* hierarchy. Therefore, the implementation's <code>setParent()</code> method
* should throw <code>IllegalArgumentException</code>.
*
* @author Craig R. McClanahan
*/
public interface Engine extends Container {
//省略代码
}
/**
* <p>
* The parent Container attached to a Host is generally an Engine, but may
* be some other implementation, or may be omitted if it is not necessary.
* </p><p>
* The child containers attached to a Host are generally implementations
* of Context (representing an individual servlet context).
*
* @author Craig R. McClanahan
*/
public interface Host extends Container {
//省略代码
}
/*** </p><p>
* The parent Container attached to a Context is generally a Host, but may
* be some other implementation, or may be omitted if it is not necessary.
* </p><p>
* The child containers attached to a Context are generally implementations
* of Wrapper (representing individual servlet definitions).
* </p><p>
*
* @author Craig R. McClanahan
*/
public interface Context extends Container, ContextBind {
//省略代码
}
/**</p><p>
* The parent Container attached to a Wrapper will generally be an
* implementation of Context, representing the servlet context (and
* therefore the web application) within which this servlet executes.
* </p><p>
* Child Containers are not allowed on Wrapper implementations, so the
* <code>addChild()</code> method should throw an
* <code>IllegalArgumentException</code>.
*
* @author Craig R. McClanahan
*/
public interface Wrapper extends Container {
//省略代码
}
Engine是最高级别的容器,其子容器是Host,Host的子容器是Context,Wrapper是Context的子容器,所以这4个容器的关系就是父子关系,也就是Engine>Host>Context>Wrapper。我们再看看Tomcat类的源码:Tomcat的getServer()我们可以知道,Tomcat的最顶层是Server,Server 就是Tomcat的实例,一个Tomcat一个Server;通过getEngine()我们可以了解到 Server 下面是 Service,而且是多个,一个 Service 代表我们部署的一个应用,而且我们还可以知道,Engine容器,一个service只有一个;根据父子关系,我们看setHost()源码可以知道,host容器有多个;同理,我们发现addContext()源码下,Context也是多个;addServlet()表明Wrapper容器也是多个,而且这段代码也暗示了,其实Wrapper和Servlet是一层意思。另外我们根据setConnector源码可以知道,连接器(Connector)是设置在service下的,而且是可以设置多个连接器(Connector)。Tomcat是一个Server,一个Server下有多个service,也就是我们部署的多个应用,一个应用下有多个连接器(Connector)和一个容器(Container),容器下有多个子容器,关系用图表示如下:Engine下有多个Host子容器,Host下有多个Context子容器,Context下有多个Wrapper子容器。总结
new SpringApplication()实例来启动的,启动过程主要做如下几件事情:> 1. 配置属性 > 2. 获取监听器,发布应用开始启动事件 > 3. 初始化输入参数 > 4. 配置环境,输出banner > 5. 创建上下文 > 6. 预处理上下文 > 7. 刷新上下文 > 8. 再刷新上下文 > 9. 发布应用已经启动事件 > 10. 发布应用启动完成事件下期展望
Spring Boot 中的 Tomcat 是如何启动的?的更多相关文章
- Spring Boot中Tomcat是怎么启动的
Spring Boot一个非常突出的优点就是不需要我们额外再部署Servlet容器,它内置了多种容器的支持.我们可以通过配置来指定我们需要的容器. 本文以我们平时最常使用的容器Tomcat为列来介绍以 ...
- spring boot 中容器 Jetty、Tomcat、Undertow
spring boot 中依赖tomcat <dependency> <groupId>org.springframework.boot</groupId> < ...
- spring boot不要放在tomcat下启动,因为自身就带了集成tomcat
spring boot不要放在tomcat下启动,因为自身就带了集成tomcat
- Spring Boot(三):Spring Boot中的事件的使用 与Spring Boot启动流程(Event 事件 和 Listeners监听器)
前言:在讲述内容之前 希望大家对设计模式有所了解 即使你学会了本片的内容 也不知道什么时候去使用 或者为什么要这样去用 观察者模式: 观察者模式是一种对象行为模式.它定义对象间的一种一对多的依赖关系, ...
- spring boot中的约定优于配置
Spring Boot并不是一个全新的框架,而是将已有的Spring组件整合起来. Spring Boot可以说是遵循约定优于配置这个理念产生的.它的特点是简单.快速和便捷. 既然遵循约定优于配置,则 ...
- 201. Spring Boot JNDI:Spring Boot中怎么玩JNDI
[视频&交流平台] àSpringBoot视频:http://t.cn/R3QepWG à SpringCloud视频:http://t.cn/R3QeRZc à Spring Boot源 ...
- Spring Boot 中配置文件application.properties使用
一.配置文档配置项的调用(application.properties可放在resources,或者resources下的config文件夹里) package com.my.study.contro ...
- Spring Boot 中使用 jpa
本文原文版权归 CSDN Hgihness 所有,此处为转载+技术收藏,如有再转请自觉于篇头处标明原文作者及出处,这是大家对作者劳动成果的自觉尊重!! 作者:Hgihness 原文:http://bl ...
- Spring Boot 中使用 MyBatis 整合 Druid 多数据源
2017 年 10 月 20 日 Spring Boot 中使用 MyBatis 整合 Druid 多数据源 本文将讲述 spring boot + mybatis + druid 多数据源配置方 ...
随机推荐
- 你真的懂git rebase吗?
前段时间由于某种原因,开始接手开发公司前端Vue搭建的项目 该前端项目采用的是基于git rebase的形式去合并代码,而我之前使用git一直都是采用merge的形式合并分支代码,对于rebase一概 ...
- Behavior行为
创建公用的js 模块 封装起来 let behavior = Behavior({//定义属性 properties: { type: String, img: String, content: St ...
- <el-menu>菜单标签(里面可以包括:<el-submenu>和<el-menu-item>)
<el-menu> 1.router属性,若使用router属性menu-item的index将对应router的path属性 2.mode,下拉菜单的模式分为horizontal和ver ...
- python+selenium实现发送一封带附件的邮件
163邮件登录首页 登录成功断言是否有退出按钮 点击退出退出登录 from selenium import webdriver import unittest import time class Vi ...
- js+php大文件分片上传
1 背景 用户本地有一份txt或者csv文件,无论是从业务数据库导出.还是其他途径获取,当需要使用蚂蚁的大数据分析工具进行数据加工.挖掘和共创应用的时候,首先要将本地文件上传至ODPS,普通的小文件通 ...
- StringUtils.join()
org.apache.commons.lang.StringUtils; StringUtils.join(null) = null StringUtils.join([]) ...
- MySQL删除外键约束问题
当我们在一个表中添加字段约束的时候: ALTER TABLE product ADD CONSTRAINT product_fk FOREIGN KEY(category_id) REFERENCES ...
- 最新版本的MySQL的下载和安装(Release: 8.0.12)
1.打开百度搜索[Myql],或直达官网https://dev.mysql.com/ 2.点选[Download按钮],跳转到下载页面,拉到底部再点选[Community Download]社区版[免 ...
- 服务器上安装搭建node环境
一.版本 : node v4.4.3 npm v2.15.1 pm2 v1.1.3 express v4.13.4 二.node安装与环境配置 新建node安装目录,与node项目目录 ...
- (转)C#_WinForm接收命令行参数
本文转载自:http://blog.csdn.net/lysc_forever/article/details/38356007 首先,我要仔细的声明下,本文讲的是接受命令行参数,让程序启动.而不是启 ...