我的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 ...
随机推荐
- 林大妈的JavaScript进阶知识(三):HTML5 History API
HTML5中新增了History API,它用于管理浏览器路由跳转的一个url栈.History是window对象的一部分,它也是一个对象,因此称它是BOM(类似DOM,Browser Object ...
- ReentrantReadWriteLock总结
ReentrantReadWriteLock的流程的一些特性: // ReentrantReadWriteLock.WriteLock.lock()特性: • 已持有读锁的线程不能再持有写锁: • 已 ...
- OPENLDAP 服务搭建和后期管理
LDAP 服务 本文首发:https://www.cnblogs.com/somata/p/OPENLDAPServerConfigAndPostManagement.html 本文主要在debian ...
- ExpandableListView 可折叠的下拉listview
ExpandableListView用法如下 1.定义布局文件main.xml文件 <?xml version="1.0" encoding="utf-8" ...
- 做一个logitic分类之鸢尾花数据集的分类
做一个logitic分类之鸢尾花数据集的分类 Iris 鸢尾花数据集是一个经典数据集,在统计学习和机器学习领域都经常被用作示例.数据集内包含 3 类共 150 条记录,每类各 50 个数据,每条记录都 ...
- CentOS7.2防火墙配置
一.查看firewall以及firewall服务的状态. # 查看firewall服务状态 systemctl status firewalld # 查看firewall状态 firewall-cmd ...
- iOS 13 正式发布,来看看有哪些 API 变动
iOS 13 已正式发布,网上对其用户体验上的新特性的描述也很多.对于开发来说,需要关注的另一方面是新系统在 API 层面做了哪些改动,从而会对我们现有的代码产生什么影响. 在这里,我们基于 iOS ...
- (三)Spring 高级装配 bean的作用域@Scope
1.默认情况下,spring通过@Autowared注入的bean是单例的bean,但有些情况是不满足的,例如:购物车,每个会话,或每个用户登录使用的购物车都是独立的 spring的定义的作用域: a ...
- python常用数据结构讲解
一:序列 在数学上,序列是被排成一排的对象,而在python中,序列是最基本的数据结构.它的主要特征为拥有索引,每个索引的元素是可迭代对象.都可以进行索引,切片,加,乘,检查成员等操作.在py ...
- ZK 网络故障应对法
网络故障可以说是分布式系统天生的宿敌.如果永远不发生网络故障,我们实际上可以设计出高可用强一致的分布式系统.可惜的是不发生网络故障的分布式环境还不存在,ZK 使用过程中也需要小心的应付网络故障. 让我 ...