Tomcat与SpringMVC结合分析(一)
关键字:
Bootsrap,Catalina,Server,Service,Engine,Host,Context,Wrapper,Valve,Pipeline,ContextConfig,ServletConfig,ContextLoaderListener,DispatcherServlet.
一:环境配置
我用的分析的工具是eclipse,源码用maven的自动下载源码的功能.然后在maven中添加相应的dependence.这样但我们ctrl+leftmouse的时候就会自动帮我们下载源代码.
SpringMVC版本为3.2.4,虽然现在基本用的是Spring4.x或是SpringBoot,但基本的原理没有变动太大所以有典型性.
Tomcat的版本为8.0.47.
servlet-api的版本为3.1.0.
二:Tomcat的基本原理

1.调用在Bootstrap中的主函数main()程序入口调用自身的start()方法,在start()方法中实例化org.apache.catalina.startup.Catalina类调用其start()方法,在Catalina的start()中调用load()载入tomcat文件夹目录下conf文件夹下的server.xml并创建Server类,然后调用Server的start()方法,到这里我们的tomcat就运行起来了.
2.Server类代表整个Tomcat服务器,这里有必要介绍一下为什么Tomcat叫Servlet容器,在Tomcat中包含四个容器,分别为Engine,Host,Context,Wrapper.Engine是Host的父容器,依次类推.四者都继承自Container接口.一般在Tomcat中一个Engine实例中包含一个Host实例,一个Host实例中包含一个Context实例,一个Context代表一个WEB程序并包含多个Wrapper实例,一个Wrapper代表一个Servlet类.
3.一次请求的大题流程是这样的,首先由Connector获取到Http请求,封装ServletRequest和ServletResponse对象并派发给Context管道中的所有Valve,执行完所有Valve后执行基础Valve根据请求的url映射到对应的Wrapper,然后调用个Wrapper管道中的所有Valve,最后调用基础Valve在如这个Wrapper类所封装的Servlet,实例化后调用器Service方法对用户的请求进行处理,最后将结果负载在response中返回给用户.
三:从源码开始分析Tomcat运行原理
1.Bootsrap作为整个Tomcat的入口中的main()方法,doemon为Bootstrap类型.


在start()中载入并实例化Catalina并调用了start()方法.

在start()中调用load()方法载入了Server类,然后调用了其start()方法启动Tomcat.

2.下面让我们仔细分析Tomcat是怎么以优雅的方式配置Server类的.

在createStartDigester内部

这里先解释一下Digester是一个用于解析xml的小框架,内部通过封装sax解析来实现的.介绍一下Digester的功能:
A.在遇到patter对应的标签时创建对应的对象,将对象压栈,然后标签结束后再将这个对象出栈.

通过上面的代码配置在遇到<Server>标签时创建StandardServer对象,并将该对象压进Digester内部的stack中,遇到</Server>的时候将StandardServer对象出栈.
B.开启对应标签的attribute属性填充到实例化的类中的功能.

C.这是最关键的地方,在遇到对应的匹配规则后在end()的方法调用栈顶下面紧挨着的对象的指定的方法并将栈顶的对象作为参数穿进去.

刚开始的时候我很迷惑,因为在整个程序中都没有看到Catalina什么时候显式的设置了Server类型的属性,后来我才发现这个秘密:还是在Catalina的load()方法中.
在真正开始Digester的parse()方法之前先将自身压进了自身的栈中,然后我们再看下面

我们结合Tomcat中conf目录下的server.xml文件,<Server>就是根元素.所以解析最后出栈的肯定是<Server>标签对应的StandardServer对象.我接着看这几行代码.
因为最开始将Catalina类自身以this传了进来所以栈底是Catalina对象,<server>标签结束的时候会调用Ctalina的setServer()方法将StandardServer对象的实例作为参数传进来.到这里我们成功设置了server属性,调用StandardServer的start()方法.
3.StandardServer中的start()方法继承自LifecycleBase然后又调用自己的startInternal().

在startInternal()中启动了由Digester从server.xml中解析出来的默认的Service组件
再看一下Catalina中的代码

遇到<Service>后会实例化StandardService实例然后在解析</Service>结束后会将StandardService的实例添加到StandardServer实例中.
4.StandardService中一次调用了StandardEngine和StandardHost的start()方法,但是我们始终找不到StandardContext是什么时候被初始化的让我们看看程序是怎么创建StandardContext实例的.

还是回到Catalina初始化Digester实例时的代码,我们看对于<Host>标签加的一些规则

这里的比较核心的就是HostConfig,它继承自LifecycleListenerRule,当StandardHost启动的时候会调用监听器的启动事件的处理逻辑.
我们认真对待这块,当调用StandardHost的start()方法实际上调用的是父类LifecycleBase的start()方法.

这里调用子类的startInternal()方法,StandardHost和ContainerBase都实现了这个方法,根据多态规则首先会调用最底层子类的方法

然后在这里又会调用父类ContainerBase类的startInternal()方法,让我们看这个方法.

这个方法会出发生命周期start的状态.

在setStateInternal()的方法中获取上面的枚举类型的LifecycleState中String类型的成员变量,STARTING类型对应Lifecycle.START_EVENT类型的事件,然后看fireLifecycleEvent()方法.

这里将实践类型传给了LifecycleSupport中维护的LifecycleListener类型的集合,并调用其中核心方法lifecycleEvent().
所以回到上面创建Digester的时候我们定义了将匹配规则,在解析到<Host>的时候会开始实例化HostConfig类型的LifecycleListener并将其添加到StandardHost当中.然后在start()方法调用后会触发HostConfig中的lifecycleEvent()方法.

调用start()方法.
在start()中调用了deployApps()方法.

让我们直接看Tomcat我们部署的项目的载入方式,项目的文件夹为webapps.

向Digester解析生成的线程池中提交一个DeployDirectory类的实例.
在里面又调用了HostConfig类的deployDirectory()方法.

在deployDirectory()方法中前面对路径进行了一系列的判断

让我们看看这个路径.

有时候我们的的项目的相关目录下并没有这个文件.

所以直接进入了else块直接生成了StandardContext实例初始化参数后直接添加到了父容器StandardHost中.
5.StandardContext类的初始化
让我们先看看在HostConfig中是怎么配置StandardContext的.

首先先给他添加了一个生命周期事件监听器.

在StandardContext中我们找到了这个监听器,ContextConfig.想必到这里大家如果看懂前面HostConfig的执行原理,这个也不难理解,在这里我就不仔细的介绍了,当Context启动的时候肯定会出发start类型的时间,然后调用ContextConfig的lifecycleEvent()方法.让我们看看在这个ContextConfig中都做了什么.


调用configureStart()方法中调用webConfig()载入我们WEB-INF文件夹下面的web.xml.

这里也是用Digester进行解析的,添加的匹配规则无非是对Servlet,Filter等我们在开发中经常用到的配置标签的解析并将生成的实体类填充在WebXml类型的实例中.还有一点值得提示的是在第一次获取ServletContext的时候会对其进行初始化为ApplicationContext.

最后在configureContext方法中将填充完毕的WebXml实例中的相关数据填充到Context中.

下面的代码接着上面的.

这里看Servlet填充的过程,用Wrapper包装Servlet再将这些Wrapper填充到Context中.
让我们接着看在StandardContext的startInternal()我们比较感兴趣的地方.

这里会将以字符串数组保存的listener数组进行初始化.

对实例化的listener进行分类

对初始化的监听器分类我们这里关心ServletContextListener类型的监听器,判断如果符合该类型的监听器后便会加入方法中临时创建的名为lifecycleListeners的ArrayList中.

设置刚生成的lifecycleListenders.
生成事件,调用ServletContextListener类型实例的contextInitialized()方法,这就是Tomcat和SpringMVC勾搭开始的地方.具体的内容我们在下一篇讲,睡了.
Tomcat与SpringMVC结合分析(一)的更多相关文章
- SpringBoot中Tomcat和SpringMVC整合源码分析
概述 SpringBoot中集成官方的第三方组件是通过在POM文件中添加组件的starter的Maven依赖来完成的.添加相关的Maven依赖之后,会引入具体的jar包,在SpringBoot启动 ...
- Servlet和Tomcat底层源码分析
Servlet 源码分析 Servlet 结构图 Servlet 和 ServletConfig 都是顶层接口,而 GenericServlet 实现了这两个顶层接口,然后HttpServlet ...
- Tomcat 8.5 架构分析
官方文档:Apache Tomcat 8 Architecture 以下分析的是 Version 8.5. Tomcat 组件关系图 根据 Architecture Overview 绘制: Serv ...
- spring boot 加载web容器tomcat流程源码分析
spring boot 加载web容器tomcat流程源码分析 我本地的springboot版本是2.5.1,后面的分析都是基于这个版本 <parent> <groupId>o ...
- tomcat与springmvc 结合 之---第17篇 StandContext容器和SpringMVC的WebApplicationContext的联系
writedby 张艳涛, 上一篇分析了,dispatcherservlet通过getServletConfig 方法获取了web.xml定义的<param-init>属性的过程 那么在如 ...
- 三思考,实现自己定义404页:Tomcat、SpringMVC精确匹配、重写DispatchServlet
第1种方式:Tomcat直接处理 web.xml <error-page> <error-code>404</error-code> <location> ...
- springmvc(一) springmvc框架原理分析和简单入门程序
springmvc这个框架真的非常简单,感觉比struts2还更简单,好好沉淀下来学习~ --WH 一.什么是springmvc? 我们知道三层架构的思想,并且如果你知道ssh的话,就会更加透彻的理解 ...
- IDEA+Tomcat+Maven+SpringMVC基于Java注解配置web工程
1.在IDEA中新建Maven工程,使用archetype. 2.添加Maven依赖 <dependencies> <dependency> <groupId>ju ...
- SpringMVC学习(一)———— springmvc框架原理分析和简单入门程序
一.什么是springmvc? 我们知道三层架构的思想,并且如果你知道ssh的话,就会更加透彻的理解这个思想,struts2在web层,spring在中间控制,hibernate在dao层与数据库打交 ...
随机推荐
- Bayan 2015 Contest Warm Up D题(GCD)
D. CGCDSSQ time limit per test 2 seconds memory limit per test 256 megabytes input standard input ou ...
- hdu 4939
题意: 长度为n个单位的map,每一个单位须要时间t来走完. 每一个单位能够放置一个塔.一共同拥有三种塔,每种塔的作用不同: 1.仅仅能攻击当前单位.每秒x点伤害(红塔) 2.攻击当前单位之后的全部单 ...
- .NET Core容器化@Docker
温馨提示:本文适合动手演练,效果更佳. 1. 引言 我们知道. NET Core最大的特性之一就是跨平台,而对于跨平台,似乎大家印象中就是可以在非Windows系统上部署运行.而至于如何操作,可能就 ...
- Angular专题系列之一:初识Anjularjs
Angular作为一个前端的主流框架,以其强大的特性正在被越来越多的程序员们所使用,那么,什么是anjular呢?angularjs是一个javascript framework,一个js的框架,可以 ...
- 在java项目中加入百度富文本编辑器
富文本编辑器在项目中很常见,他可以将文本,图片等信息存入数据库,在编辑一些图文混排的信息的时候很有用,比如商城项目的商品详情页,包含很多带有样式的文字和图片. 此前一直使用的百度的富文本编辑器uedi ...
- ssm学习(五)--加入分页插件
之前我们的查询列表是将所有的数据查询出来,并没有做分页,当数据很少的时候,是不需要分页,但是如果数据很多的时候,所有数据显示在一个页面显然是不合适的. 之前用hibernate的时候,可以直接通过查询 ...
- mysql 常用sql语句 二
mysql 查询 基本查询 select * from users; 去重 select distinct(name),high from users order by high desc; ## 注 ...
- svn搭建服务器--- 绝对好使---杜恩德
如何创建 SVN 服务器,并搭建自己的 SVN 仓库 听语音 | 浏览:1289 | 更新:2016-09-10 17:45 1 2 3 4 5 6 7 分步阅读 百度经验:jingyan.baidu ...
- 微信小程序——轮播图实现
小程序的轮播图,也就是他其中的一个控件可以算是直接上代码: 这是WXML 页面 代码: <view class='carousel_div'> <swiper class=" ...
- Confluence 持续集成平台部署记录
1.1 Confluence简介 Confluence是一个专业的企业知识管理与协同软件,也可以用于构建企业wiki.使用简单,但它强大的编辑和站点管理特征能够帮助团队成员之间共享信息.文档协作.集体 ...