我的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 ...
随机推荐
- 前端利器躬行记(4)——webpack进阶
webpack是一个非常强大的工具,除了前文所介绍的基础概念之外,还有各种进阶应用,例如Source Map.模块热替换.集成等,本文会对这些内容做依次讲解. 一. runtime和manifest ...
- HDU 1010 temp of the bone 解题报告 (DFS)
转载大佬的blog,很详细,学到了很多东西 奇偶剪枝:根据题目,dog必须在第t秒到达门口.也就是需要走t-1步.设dog开始的位置为(sx,sy),目标位置为(ex,ey).如果abs(ex-x)+ ...
- mysql 查询参数尾部有空格时被忽略
最近再使用mysql时,无意见发现,当我查询参数尾部输入若干个空格时,依然可以查出和不加空格相同的结果,像这样 select * from wa where name='be ' 等同于 select ...
- Spring Boot 多环境如何配置
Spring Boot 开发环境.测试环境.预生产环境.生产环境多环境配置 通常一个公司的应程序可能在开发环境(dev).测试环境(test).生产环境(prod)中运行.那么是不是需要拷贝不同的安装 ...
- 一次误用CSRedisCore引发的redis故障排除经历
前导 上次Redis MQ分布式改造完成之后, 编排的容器稳定运行了一个多月,昨天突然收到ETL端同事通知,没有采集到解析日志了. 赶紧进服务器看了一下,用于数据接收的receiver容器挂掉了, 尝 ...
- Net基础篇_学习笔记_第十一天_面向对象(构造函数)
VS封装字段快捷键: 提取方法 Ctrl+R,M 封装字段 Ctrl+R,E 提取接口 Ctrl+R,I. 构成函数: public Student() { Con ...
- Apollo源码解析-搭建调试环境
准备工作 本地运行时环境 JDK :1.8+ MySQL :5.6.5+ Maven :3.6.1 IDE :IntelliJ IDEA Apollo的表结构对timestamp使用了多个defaul ...
- jmeter运行第三方java项目
自己写了个简化系统操作的小工具,因为不想给别人用的时候占用本地资源于是写的是纯java项目,后面放到jmeter中通过beanshell sampler调用. java源码不贴了,把写好的项目导出成可 ...
- HTML定位——绝对定位和相对定位、固定定位
1.绝对定位 绝对定位指的是通过规定HTML元素在水平和垂直方向上的位置来固定元素,基于绝对定位的元素不会占据空间. 绝对定位的位置声明是相对于已定位的并且包含关系最近的祖先元素.如果当前需要被定为的 ...
- PTA A1015
A1015 Reversible Primes (20 分) 题目内容 A reversible prime in any number system is a prime whose "r ...