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. 分库分表之后,id 主键如何处理?

    其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定不对啊,需要一个全局唯一的 id 来支持.所以这都是你实际生产环境中必须考虑的 ...

  2. 前端利器躬行记(5)——Git

    Git是一款开源的分布式版本控制系统,它的出现和Linux紧密相关.Linux内核项目组为了能更好地管理和维护Linux内核开发,于2002年开始启用商业的分布式版本控制系统BitKeeper.虽然软 ...

  3. response对响应的设置

    1.response对象设置响应行状态码: protected void doGet(HttpServletRequest request, HttpServletResponse response) ...

  4. Qt线程实现分析-moveToThread vs 继承

    最近抽空研究了下QThread,使用起来方式多种多样,但是在使用的同时,我们也应该去了解Qt的线程它到底是怎么玩儿的. Qt的帮助文档里讲述了2种QThread的使用方式,一种是moveToThrea ...

  5. abp(net core)+easyui+efcore实现仓储管理系统——EasyUI之货物管理一 (十九)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...

  6. 【linux】【jenkins】自动化运维四 整合gitlab、docker发布java项目

    jenkins发布java项目 过程参考发布vue项目.https://www.cnblogs.com/jxd283465/p/11543431.html 大同小异. vue建立的是Freestyle ...

  7. 前端基础-BOM和DOM学习

    JavaScript分为 ECMAScript,BOM,DOM. BOM:是指浏览器对象模型,使JavaScript有能力与浏览器进行对象. DOM:是指文档对象模型,通过它,可以访问HTML文档的所 ...

  8. UnicodeDecodeError: 'gbk' codec can't decode byte 0xb0 in position 279: illegal multibyte sequence

    with open(r'E:\yy\mysql.txt') as wk: print(wk.readlines()) Traceback (most recent call last): File & ...

  9. Selenium+python操作id为动态变化的frame(iframe)

    先定位到一组frame:ele = dr.find_elements_by_tag_name('iframe')此时获得一组frame 再通过index取需要切进去的frame并取到该frame的id ...

  10. java算法基础范例

    题目1:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?  1.程序分析: 兔子的规律为数列1,1,2,3 ...