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层与数据库打交 ...
随机推荐
- Codeforces Round #436 (Div. 2)
http://codeforces.com/contest/864 第一次打cf的月赛-- A 题意:给你一个数列,问你能不能保证里面只有两种数且个数相等.2<=n<=100,1<= ...
- 高效管理http连接
1.Http连接基础 Http协议承载了互联网上的主要流量,然而说到传输,还要回归到最基本的网络分层模型TCP/IP.TCP/IP是全球计算机及网络设备都在使用的一种常用的分组交互网络分层协议集.客户 ...
- Linux学习记录--文件IO操作相关系统编程
文件IO操作相关系统编程 这里主要说两套IO操作接口,各自是: POSIX标准 read|write接口.函数定义在#include<unistd.h> ISO C标准 fread|fwr ...
- 让Android Support V4中的SwipeRefreshLayout支持上拉载入很多其它
前言 原来的Android SDK中并没有下拉刷新组件,可是这个组件确实绝大多数APP必备的一个部件.好在google在v4包中出了一个SwipeRefreshLayout.可是这个组件仅仅支持下拉刷 ...
- Oracle ROWID具体解释
1.ROWID定义 ROWID:数据库中行的全局唯一地址 对于数据中的每一行,rowid伪列返回行的地址.rowid值主要包括下面信息: 对象的数据对象编号 该行所在的数据文件里的数据块 该行中数据块 ...
- spool命令、创建一个表,创建而且copy表,查看别的用户下的表,rowid行地址 索引的时候使用,表的增删改查,删除表,oracle的回收站
1.spool命令 spool "D:\test.txt" spool off SQL> host cls 2.创建一个表 SQL> --条件(1):有创建 ...
- JAVA入门[11]-jsp几点基础语法
一.include <%@include file="copyright.jsp"%> <jsp:include page="copyright.jsp ...
- ASP.NET Core 使用 Alipay.AopSdk.Core 常见问题解答
1.Alipay.AopSdk.Core.AopException:"您使用的私钥格式错误,请检查RSA私钥配置,charset = UTF-8" 出现这个问题,就是配置不正确.首 ...
- All-In-One方式-安装openstack
ALL-IN-ONE安装openstack******************************************************************************* ...
- spring mvc获取绝对路径的几种方法
1.首先如果是在一个controller方法中,则很简单,直接用下面语句. @RequestMapping("categoryHome") public ModelAndView ...