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层与数据库打交 ...
随机推荐
- 编码中的setCharacterEncoding 理解<转自-fancychendong>
1.pageEncoding="UTF-8"的作用是设置JSP编译成Servlet时使用的编码. 2.contentType="text/html;charset=UTF ...
- JPA学习笔记(8)——映射双向一对多关联关系
双向一对多关联关系 前面的博客讲的都是单向的,而本问讲的是双向的(双向一对多 = 双向多对一) 什么是双向? 我们来对照一下单向和双向 单向/双向 User实体类中是否有List< Order& ...
- 最全Pycharm教程(28)——Pycharm搜索导航之搜索应用实例
1.主题 这里我们将介绍Pycharm另外一项强力的搜索导航功能.如果你希望知道某个特定的类或方法都在project中的哪些地方发挥了作用.也就是找出其全部的usages,这将是一个很巨大而繁琐的pr ...
- POJ 2533 Longest Ordered Subsequence(DP 最长上升子序列)
Longest Ordered Subsequence Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 38980 Acc ...
- 深入理解计算机系统_3e 第六章家庭作业 CS:APP3e chapter 6 homework
6.22 假设磁道沿半径均匀分布,即总磁道数和(1-x)r成正比,设磁道数为(1-x)rk: 由题单个磁道的位数和周长成正比,即和半径xr成正比,设单个磁道的位数为xrz: 其中r.k.z均为常数. ...
- 设置两个div是总是不能重合,浏览器user agent stylesheet问题
如图 两个div之间总是有一个空行,设置了margin为0还是没卵用,f12调试发现 多了一个user agent stylesheet样式,经百度是浏览器自带的样式 重新为div内的元素ul设置cs ...
- 判断json数据是否包含key
1.("key" in jsonObj) 如果有返回true 没有返回false 2.jsonObj.hasOwnProperty("key") 如果有返回tr ...
- mysql连接错误问题
ERROR 1130: Host '192.168.1.3' is not allowed to connect to this MySQL server 解决方法:1. 改表法.可能是你的帐号不允许 ...
- java 拦截器
一.前言 这是一篇关于 java 拦截器的文章,是我的写 java web 所遇见的问题.当我们写好一个网站,必须要通过登陆页面才可以进入这个系统.那么我们就得写个 java 拦截器,如果是通过登录 ...
- JavaScript循环实例
几个经典的循环案例: 1.一张纸的厚度是0.0001米,将纸对折,对折多少次厚度超过珠峰高度8848米 var i=0; var h=0.0001; while(true){ i++; h=h*2; ...