我的Spring Boot学习记录(二):Tomcat Server以及Spring MVC的上下文问题
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呢?
其实onRefresh是org.springframework.context.support.AbstractApplicationContext一个未具体实现方法,交给子类实现,它在调用refresh()方法中调用。
而org.springframework.boot.SpringApplication#run(java.lang.String...)调用了refreshContext,又间接调用上述refresh()方法
在onRefresh调用后在refresh()中还调用了finishRefresh(),而重写了其方法finishRefresh的ServletWebServerApplicationContext就启动了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的上下文问题的更多相关文章
- Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客
==他的博客应该不错,没有细看 Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客 http://blog.csdn.net/u012706811/article/det ...
- Spring Boot学习记录(二)–thymeleaf模板
自从来公司后都没用过jsp当界面渲染了,因为前后端分离不是很好,反而模板引擎用的比较多,thymeleaf最大的优势后缀为html,就是只需要浏览器就可以展现页面了,还有就是thymeleaf可以很好 ...
- Spring boot学习1 构建微服务:Spring boot 入门篇
Spring boot学习1 构建微服务:Spring boot 入门篇 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框 ...
- 2019-04-05 Spring Boot学习记录
1. 使用步骤 ① 在pom.xml 增加父级依赖(spring-boot-starter-parent) ② 增加项目起步依赖,如spring-boot-starter-web ③ 配置JDK版本插 ...
- Spring Boot 学习方法论-如何正确的入门 Spring Boot
想要入门 Spring Boot,那么什么样的教程是符合初学者学习的(没有太多的Java基础但有一些程序基础或者软件编程知识). 这恰好能够勾出很多问题,比如是文章图文教程适合还是视频教程适合零基础初 ...
- (44). Spring Boot日志记录SLF4J【从零开始学Spring Boot】
在开发中打印内容,使用 System.out.println() 和 Log4j 应当是人人皆知的方法了. 其实在开发中我们不建议使用 System.out 因为大量的使用 System.out 会增 ...
- 【转载】Spring boot学习记录(二)-配置文件解析
前言:本系列文章非本人原创,转自:http://tengj.top/2017/04/24/springboot0/ 正文 Spring Boot使用了一个全局的配置文件application.prop ...
- 【Spring Boot学习之二】WEB开发
环境 Java1.8 Spring Boot 1.3.2 一.静态资源访问 动静分离:动态服务和前台页面图片分开,静态资源可以使用CDN加速;Spring Boot默认提供静态资源目录位置需置于cla ...
- 【转载】Spring boot学习记录(一)-入门篇
前言:本系列文章非本人原创,转自:http://tengj.top/2017/04/24/springboot0/ 正文 首先声明,Spring Boot不是一门新技术.从本质上来说,Spring B ...
随机推荐
- Linux命令分类
系统信息arch 显示机器的处理器架构(1)uname -m 显示机器的处理器架构(2)uname -r 显示正在使用的内核版本dmidecode -q 显示硬件系统部件 - (SMBIOS / DM ...
- 降维和PCA
简介 要理解什么是降维,书上给出了一个很好但是有点抽象的例子. 说,看电视的时候屏幕上有成百上千万的像素点,那么其实每个画面都是一个上千万维度的数据:但是我们在观看的时候大脑自动把电视里面的场景放在我 ...
- 【Offer】[3-1] 【找出数组中重复的数字】
题目描述 思路 Java代码 代码链接 题目描述 在一个长度为n的数组里的所有数字都在0~n-1的范围内.数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次. 请找出数组中任 ...
- C#开发BIMFACE系列27 服务端API之获取模型数据12:获取构件分类树
系列目录 [已更新最新开发文章,点击查看详细] BIMFACE官方示例中,加载三维模型后,模型浏览器中左上角默认提供了“目录树”的功能,清晰地展示了模型的完整构成及上下级关系. 本篇介绍如何获 ...
- 从矩阵(matrix)角度讨论PCA(Principal Component Analysis 主成分分析)、SVD(Singular Value Decomposition 奇异值分解)相关原理
0. 引言 本文主要的目的在于讨论PAC降维和SVD特征提取原理,围绕这一主题,在文章的开头从涉及的相关矩阵原理切入,逐步深入讨论,希望能够学习这一领域问题的读者朋友有帮助. 这里推荐Mit的Gilb ...
- 为什么有的编程规范要求用 void 0 代替 undefined
Undefined Undefined 类型表示未定义,它的类型只有一个值,就是 undefined. 任何变量在被赋值前它的值都是 undefined,但是在 JavaScript 引擎中,unde ...
- getMeasuredHeight()与getHeight() 以及MeasureSpec.getSize()
getMeasuredHeight()返回的是原始测量高度,与屏幕无关,getHeight()返回的是在屏幕上显示的高度.实际上在当屏幕可以包裹内容的时候,他们的值是相等的,只有当view超出屏幕后, ...
- Unity3D_02_基类MonoBehaviour/自带函数以及脚本执行的生命周期
导引: 其中Time,Input,Physics都是Unity中的全局变量.GameObject是游戏中的基本物件.GameObject是由Component组合而成的,GameObject本身必须有 ...
- Javaweb设置session过期时间
在Java Web开发中,Session为我们提供了很多方便,Session是由浏览器和服务器之间维护的.Session超时理解为:浏览器和服务器之间创建了一个Session,由于客户端长时间(休眠时 ...
- TestNG(一) TestNG实战在idea中创建module
1.在ider里创建一个Module 2.直接点击下一步 3.输入Groupld h和Artifactid名称,点击下一步 4.点击Finish 创建完成