Spring Boot版本: 2.0.0.RELEASE

这里需要引入依赖 spring-boot-starter-web

这里有可能有个人的误解,请抱着怀疑态度看。

建议: 感觉自己也会被绕晕,所以有兴趣的使用IDE工具看下源码

1、Tomcat在什么时候被初始化了?

ServletWebServerApplicationContext中有段代码,如下:

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext

	@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
} /**
* 创建Web服务容器,如:Tomcat,Jetty等;具体创建的容器根据#getWebServerFactory得到
* 而WebServerFactory在BeanFactory获取,也就是在加载Bean时确定的,
* 这里通过Spring Boot自动配置了Tomcat,如果想要深入可以追着#getWebServerFactory看
* 下面有对应的不太详细的解释
*/
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
// .....
}
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration.EmbeddedTomcat#tomcatServletWebServerFactory
/**
* 这里就通过自动加载Bean时加载了关于Tomcat的WebServerFactory
* 至于为什么加载Tomcat而不是Jetty,就要多谢Bean加载时@ConditionalOnClass注解
* 因为我们引入依赖spring-boot-starter-web 其次,它又引入了 spring-boot-starter-tomcat依赖
* 因为存在{ Servlet.class, Tomcat.class, UpgradeProtocol.class }这些class,所以加载Tomcat
*/
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat { @Bean
public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
return new TomcatServletWebServerFactory();
} }
// .......

上述中说明了Tomcat的创建,而什么时候调用onRefresh并创建Tomcat呢?

其实onRefreshorg.springframework.context.support.AbstractApplicationContext一个未具体实现方法,交给子类实现,它在调用refresh()方法中调用。

org.springframework.boot.SpringApplication#run(java.lang.String...)调用了refreshContext,又间接调用上述refresh()方法

onRefresh调用后在refresh()中还调用了finishRefresh(),而重写了其方法finishRefreshServletWebServerApplicationContext就启动了Web服务,代码如下:

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#finishRefresh
@Override
protected void finishRefresh() {
super.finishRefresh();
WebServer webServer = startWebServer();
if (webServer != null) {
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
} private WebServer startWebServer() {
WebServer webServer = this.webServer;
if (webServer != null) {
// 这里如何启动具体可看org.springframework.boot.web.embedded.tomcat.TomcatWebServer
// 这里之所以我们启动了Spring Boot后程序还在运行是因为Tomcat启动线程在后台运行
webServer.start();
}
return webServer;
}
org.springframework.boot.web.embedded.tomcat.TomcatWebServer

	public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
} private void initialize() throws WebServerException {
//.....
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
// 大概意思是创建一个非守护线程来运行吧
startDaemonAwaitThread();
//....
} private void startDaemonAwaitThread() {
Thread awaitThread = new Thread("container-" + (containerCounter.get())) { @Override
public void run() {
TomcatWebServer.this.tomcat.getServer().await();
} };
awaitThread.setContextClassLoader(getClass().getClassLoader());
awaitThread.setDaemon(false);
awaitThread.start();
}

上面就大概说了Tomcat怎么在Spring Boot启动后加载创建的

2、Spring MVC在这里怎么工作的?

平时使用SSM开发时,使用Spring MVC 我们知道需要配置DispatcherServlet,而这里的是通过自动配置的,还对DispatcherServlet通过ServletRegistrationBean类进行封装,这里自动装配的代码在org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration中可见,代码如下:

org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletConfiguration
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(
this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(
this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(
this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
return dispatcherServlet;
} org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration
/**
* 这里通过构造器获取已经注册的 DispatcherServlet Bean
* 然后封装在ServletRegistrationBean
*/
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(
dispatcherServlet,
this.serverProperties.getServlet().getServletMapping());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}

封装了Servlet的ServletRegistrationBean各个Bean又会被ServletContextInitializerBeans进行管理。

在上述第一点中Tomcat创建加载中有个方法没有详述,就是ServletWebServerApplicationContext中的createWebServer方法,里面调用了一个getSelfInitializer方法,使用返回值作为参数,代码如下:

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext
private void createWebServer() {
//...
this.webServer = factory.getWebServer(getSelfInitializer());
//...
} /**
* Returns the {@link ServletContextInitializer} that will be used to complete the
* setup of this {@link WebApplicationContext}.
* @return the self initializer
* @see #prepareWebApplicationContext(ServletContext)
*/
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
// 又封装成了ServletContextInitializer
return this::selfInitialize;
} private void selfInitialize(ServletContext servletContext) throws ServletException {
//....
//getServletContextInitializerBeans这里就能获取到封装了Servlet或Filter等的ServletContextInitializer
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
// 这里通过回调Servlet或Filter能够获取到servletContext,并且将自己(Servlet或Filter)注册到servletContext,这里可能要去了解下Tomcat了
beans.onStartup(servletContext);
}
} /**
* Returns {@link ServletContextInitializer}s that should be used with the embedded
* web server. By default this method will first attempt to find
* {@link ServletContextInitializer}, {@link Servlet}, {@link Filter} and certain
* {@link EventListener} beans.
* @return the servlet initializer beans
*/
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
// 这里在new ServletContextInitializerBeans 时会将BeanFactory给它
// 它在构造器中将封装了Servlet或Filter等的ServletContextInitializer子类获取
// 并放入一个成员变量sortedList中
return new ServletContextInitializerBeans(getBeanFactory());
}

到此,大概就知道了Spring MVC中DispatcherServlet是怎么进入Tomcat的了,如果还想细究DispatcherServlet是怎么被一步步置入Tomcat容器中的,可以看下org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer中的代码,这里不展开了。

3、随便唠嗑

入职后好久没运动,被同事拉去打羽毛球,结果,气喘不上来,脑晕,感觉不好,第一天全身酸痛,第二天更痛。我认为,入职后第一条建议就是找时间运动。

这篇其实接着第一篇文章我的Spring Boot学习记录(一):自动配置的大致调用过程 就想要写的了,拖了好久。期间问自己,为什么干这种无趣的东西。不论如何,还是抽了时间写了,就这样吧。

这里有可能有个人的误解,请抱着怀疑态度看。

建议: 感觉自己也会被绕晕,所以有兴趣的使用IDE工具看下源码

我的Spring Boot学习记录(二):Tomcat Server以及Spring MVC的上下文问题的更多相关文章

  1. Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客

    ==他的博客应该不错,没有细看 Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客 http://blog.csdn.net/u012706811/article/det ...

  2. Spring Boot学习记录(二)–thymeleaf模板

    自从来公司后都没用过jsp当界面渲染了,因为前后端分离不是很好,反而模板引擎用的比较多,thymeleaf最大的优势后缀为html,就是只需要浏览器就可以展现页面了,还有就是thymeleaf可以很好 ...

  3. Spring boot学习1 构建微服务:Spring boot 入门篇

    Spring boot学习1 构建微服务:Spring boot 入门篇 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框 ...

  4. 2019-04-05 Spring Boot学习记录

    1. 使用步骤 ① 在pom.xml 增加父级依赖(spring-boot-starter-parent) ② 增加项目起步依赖,如spring-boot-starter-web ③ 配置JDK版本插 ...

  5. Spring Boot 学习方法论-如何正确的入门 Spring Boot

    想要入门 Spring Boot,那么什么样的教程是符合初学者学习的(没有太多的Java基础但有一些程序基础或者软件编程知识). 这恰好能够勾出很多问题,比如是文章图文教程适合还是视频教程适合零基础初 ...

  6. (44). Spring Boot日志记录SLF4J【从零开始学Spring Boot】

    在开发中打印内容,使用 System.out.println() 和 Log4j 应当是人人皆知的方法了. 其实在开发中我们不建议使用 System.out 因为大量的使用 System.out 会增 ...

  7. 【转载】Spring boot学习记录(二)-配置文件解析

    前言:本系列文章非本人原创,转自:http://tengj.top/2017/04/24/springboot0/ 正文 Spring Boot使用了一个全局的配置文件application.prop ...

  8. 【Spring Boot学习之二】WEB开发

    环境 Java1.8 Spring Boot 1.3.2 一.静态资源访问 动静分离:动态服务和前台页面图片分开,静态资源可以使用CDN加速;Spring Boot默认提供静态资源目录位置需置于cla ...

  9. 【转载】Spring boot学习记录(一)-入门篇

    前言:本系列文章非本人原创,转自:http://tengj.top/2017/04/24/springboot0/ 正文 首先声明,Spring Boot不是一门新技术.从本质上来说,Spring B ...

随机推荐

  1. 线段树(求单结点) hdu 1556 Color the ball

    Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  2. 数论 Day 12

    数论是个好东西 今天讲的是组合计数 组合计数 组合数学主要是研究一组离散对象满足一定条件的安排的存在性.构造及计数问题.计数理论是狭义组合数学中最基本的一个研究方向,主要研究的是满足一定条件的排列组合 ...

  3. m6A甲基化及预测方法工具总结

    DNA.RNA和蛋白三个层面的可逆修饰示意图(Fu et al. Nature Reviews Genetics, 2014) DNA和蛋白存在各种修饰,RNA也不例外,目前已知的RNA修饰已经超过上 ...

  4. mapper文件中“添加一条新数据并返回此数据的ID(主键)”的方法

    在mapper文件的insert语句前加上<selectKey>标签即可 如下: 添加前测试: 添加后测试:

  5. SpringCloud超简单的入门(1)--一些简单的介绍

    简介 简单来说,springcloud的就是由一组springboot应用(服务)组成,相互之间通过REST等方式进行通信. 两个springboot应用,其中一个作为服务提供者,一个作为服务消费者, ...

  6. JAVA集成JPush

    本篇集成为web项目手动集成JPush 一.获取AppKey.Master Secret https://docs.jiguang.cn 成为极光用户创建一个应用拿到(AppKey.Master Se ...

  7. java使用FileSystem上传文件到hadoop分布式文件系统配置

    Configuration conf = new Configuration(); conf.set("fs.defaultFS", "hdfs://sparkclust ...

  8. FBCTF平台安装

    一言难尽 = =开始不知道FBCTF只能安装在Ubuntu,在本地搭建半天好不容易弄起了PHP环境,打开错误,后来才知道只能在Ubuntu 14.04 LTS下安装= = FBCTF是Facebook ...

  9. Lucene 全文检索入门

    博客地址:http://www.moonxy.com 一.前言 Lucene 是 apache 软件基金会的一个子项目,由 Doug Cutting 开发,是一个开放源代码的全文检索引擎工具包,但它不 ...

  10. TCGA各种肿瘤数据的20多种不同玩法/挖掘方法

    肿瘤基因组图谱 (The Cancer Genome Atlas,TCGA) 计划是由美国国家癌症研究院(National Cancer Institute,NCI)和美国国家人类基因组研究所(Nat ...