原文地址:https://www.iteye.com/blog/gearever-1533678

Tomcat提供了engine,host,context及wrapper四种容器。在总体结构中已经阐述了他们之间的包含关系。这四种容器继承了一个容器基类,因此可以定制化。当然,tomcat也提供了标准实现。

  • Engine:org.apache.catalina.core.StandardEngine
  • Host: org.apache.catalina.core.StandardHost
  • Context:org.apache.catalina.core.StandardContext
  • Wrapper:org.apache.catalina.core.StandardWrapper

所谓容器,就是说它承载了若干逻辑单元及运行时数据。好比,整个酒店是一个容器,它包含了各个楼层等设施;每个楼层也是容器,它包含了各个房间;每个房间也是容器,它包含了各种家电等等。 
首先来看一下容器类的类结构。 

基类ContainerBase
ContainerBase是个abstract基类。其类路径为:

Java代码  
  1. org.apache.catalina.core.ContainerBase

这里只列出一些比较核心功能的组件及方法。需要注意的是,类中的方法及属性很多,限于篇幅不全部列出来了。 
Enigne
Engine是最顶层的容器,它是host容器的组合。其标准实现类为:

Java代码  
  1. org.apache.catalina.core.StandardEngine

看一下StandardEngine的主要逻辑单元概念图。

从图中可以看出,engine有四大组件:

  • Cluster: 实现tomcat集群,例如session共享等功能,通过配置server.xml可以实现,对其包含的所有host里的应用有效,该模块是可选的。其实现方式是基于pipeline+valve模式的,有时间会专门整理一个pipeline+valve模式应用系列;
  • Realm:实现用户权限管理模块,例如用户登录,访问控制等,通过通过配置server.xml可以实现,对其包含的所有host里的应用有效,该模块是可选的;
  • Pipeline:这里简单介绍下,之后会有专门文档说明。每个容器对象都有一个pipeline,它不是通过server.xml配置产生的,是必须有的。它就是容器对象实现逻辑操作的骨架,在pipeline上配置不同的valve,当需要调用此容器实现逻辑时,就会按照顺序将此pipeline上的所有valve调用一遍,这里可以参考责任链模式;
  • Valve:实现具体业务逻辑单元。可以定制化valve(实现特定接口),然后配置在server.xml里。对其包含的所有host里的应用有效。定制化的valve是可选的,但是每个容器有一个缺省的valve,例如engine的StandardEngineValve,是在StandardEngine里自带的,它主要实现了对其子host对象的StandardHostValve的调用,以此类推。

配置的例子有:

Xml代码  
  1. <Engine name="Catalina" defaultHost="localhost">
  2. <Valve className="MyValve0"/>
  3. <Valve className="MyValve1"/>
  4. <Valve className="MyValve2"/>
  5. ……
  6. <Host name="localhost" appBase="webapps">
  7. </Host>
  8. </Engine>

需要注意的是,运行环境中,pipeline上的valve数组按照配置的顺序加载,但是无论有无配置定制化的valve或有多少定制化的valve,每个容器缺省的valve,例如engine的StandardEngineValve,都会在数组中最后一个。 
Host
Host是engine的子容器,它是context容器的集合。其标准实现类为:

Java代码  
  1. org.apache.catalina.core.StandardHost

StandardHost的核心模块与StandardEngine差不多。只是作用域不一样,它的模块只对其包含的子context有效。除此,还有一些特殊的逻辑,例如context的部署。Context的部署还是比较多的,主要分为:

  • War部署
  • 文件夹部署
  • 配置部署等

有时间单独再说吧。照例贴个核心模块概念图。 

Context
Context是host的子容器,它是wrapper容器的集合。其标准实现类为:

Java代码  
  1. org.apache.catalina.core.StandardContext

应该说StandardContext是tomcat中最大的一个类。它封装的是每个web app。
看一下StandardContext的主要逻辑单元概念图。 

Pipeline,valve,realm与上面容器一样,只是作用域不一样,不多说了。

  • Manager: 它主要是应用的session管理模块。其主要功能是session的创建,session的维护,session的持久化(persistence),以及跨context的session的管理等。Manager模块可以定制化,tomcat也给出了一个标准实现;
Java代码  
  1. org.apache.catalina.session.StandardManager

manager模块是必须要有的,可以在server.xml中配置,如果没有配置的话,会在程序里生成一个manager对象。

  • Resources: 它是每个web app对应的部署结构的封装,比如,有的app是tomcat的webapps目录下的某个子目录或是在context节点配置的其他目录,或者是war文件部署的结构等。它对于每个web app是必须的。
  • Loader:它是对每个web app的自有的classloader的封装。具体内容涉及到tomcat的classloader体系,会在一篇文档中单独说明。Tomcat正是有一套完整的classloader体系,才能保证每个web app或是独立运营,或是共享某些对象等等。它对于每个web app是必须的。
  • Mapper:它封装了请求资源URI与每个相对应的处理wrapper容器的映射关系。

以某个web app的自有的web.xml配置为例;

Xml代码  
  1. <servlet>
  2. <servlet-name>httpserver</servlet-name>
  3. <servlet-class>com.gearever.servlet.TestServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>httpserver</servlet-name>
  7. <url-pattern>/*.do</url-pattern>
  8. </servlet-mapping>

对于mapper对象,可以抽象的理解成一个map结构,其key是某个访问资源,例如/*.do,那么其value就是封装了处理这个资源TestServlet的某个wrapper对象。当访问/*.do资源时,TestServlet就会在mapper对象中定位到。这里需要特别说明的是,通过这个mapper对象定位特定的wrapper对象的方式,只有一种情况,那就是在servlet或jsp中通过forward方式访问资源时用到。例如,

Java代码  
  1. request.getRequestDispatcher(url).forward(request, response)

关于mapper机制会在一篇文档中专门说明,这里简单介绍一下,方便理解。如图所示。

Mapper对象在tomcat中存在于两个地方(注意,不是说只有两个mapper对象存在),其一,是每个context容器对象中,它只记录了此context内部的访问资源与相对应的wrapper子容器的映射;其二,是connector模块中,这是tomcat全局的变量,它记录了一个完整的映射对应关系,即根据访问的完整URL如何定位到哪个host下的哪个context的哪个wrapper容器。
这样,通过上面说的forward方式访问资源会用到第一种mapper,除此之外,其他的任何方式,都是通过第二种方式的mapper定位到wrapper来处理的。也就是说,forward是服务器内部的重定向,不需要经过网络接口,因此只需要通过内存中的处理就能完成。这也就是常说的forward与sendRedirect方式重定向区别的根本所在。
看一下request.getRequestDispatcher(url) 方法的源码。

Java代码  
  1. public RequestDispatcher getRequestDispatcher(String path) {
  2. // Validate the path argument
  3. if (path == null)
  4. return (null);
  5. if (!path.startsWith("/"))
  6. throw new IllegalArgumentException
  7. (sm.getString
  8. ("applicationContext.requestDispatcher.iae", path));
  9. // Get query string
  10. String queryString = null;
  11. int pos = path.indexOf('?');
  12. if (pos >= 0) {
  13. queryString = path.substring(pos + 1);
  14. path = path.substring(0, pos);
  15. }
  16. path = normalize(path);
  17. if (path == null)
  18. return (null);
  19. pos = path.length();
  20. // Use the thread local URI and mapping data
  21. DispatchData dd = dispatchData.get();
  22. if (dd == null) {
  23. dd = new DispatchData();
  24. dispatchData.set(dd);
  25. }
  26. MessageBytes uriMB = dd.uriMB;
  27. uriMB.recycle();
  28. // Use the thread local mapping data
  29. MappingData mappingData = dd.mappingData;
  30. // Map the URI
  31. CharChunk uriCC = uriMB.getCharChunk();
  32. try {
  33. uriCC.append(context.getPath(), 0, context.getPath().length());
  34. /*
  35. * Ignore any trailing path params (separated by ';') for mapping
  36. * purposes
  37. */
  38. int semicolon = path.indexOf(';');
  39. if (pos >= 0 && semicolon > pos) {
  40. semicolon = -1;
  41. }
  42. uriCC.append(path, 0, semicolon > 0 ? semicolon : pos);
  43. <span style="color: #ff0000;"> context.getMapper().map(uriMB, mappingData); </span>
  44. if (mappingData.wrapper == null) {
  45. return (null);
  46. }
  47. /*
  48. * Append any trailing path params (separated by ';') that were
  49. * ignored for mapping purposes, so that they're reflected in the
  50. * RequestDispatcher's requestURI
  51. */
  52. if (semicolon > 0) {
  53. uriCC.append(path, semicolon, pos - semicolon);
  54. }
  55. } catch (Exception e) {
  56. // Should never happen
  57. log(sm.getString("applicationContext.mapping.error"), e);
  58. return (null);
  59. }
  60. <span style="color: #ff0000;">Wrapper wrapper = (Wrapper) mappingData.wrapper; </span>
  61. String wrapperPath = mappingData.wrapperPath.toString();
  62. String pathInfo = mappingData.pathInfo.toString();
  63. mappingData.recycle();
  64. // Construct a RequestDispatcher to process this request
  65. return new ApplicationDispatcher
  66. (<span style="color: #ff0000;">wrapper</span>, uriCC.toString(), wrapperPath, pathInfo,
  67. queryString, null);
  68. }

红色部分标记了从context的mapper对象中定位wrapper子容器,然后封装在一个dispatcher对象内并返回。通过上面的阐述,也说明了为什么forward方法不能跨context访问资源了。

Wrapper
Wrapper是context的子容器,它封装的处理资源的每个具体的servlet。其标准实现类为:

Java代码  
  1. org.apache.catalina.core.StandardWrapper

应该说StandardWrapper是tomcat中比较重要的一个类。初认识它时,比较容易混淆。
先看一下StandardWrapper的主要逻辑单元概念图。 

Pipeline,valve与上面容器一样,只是作用域不一样,不多说了。
主要说说servlet对象与servlet stack对象。这两个对象在wrapper容器中只存在其中之一,也就是说只有其中一个不为空。当以servlet对象存在时,说明此servlet是支持多线程并发访问的,也就是说不存在线程同步的过程,此wrapper容器中只包含一个servlet对象(这是我们常用的模式);当以servlet stack对象存在时,说明servlet是不支持多线程并发访问的,每个servlet对象任一时刻只有一个线程可以调用,这样servlet stack实现的就是个简易的线程池,此wrapper容器中只包含一组servlet对象,它的基本原型是worker thread模式实现的。 
那么,怎么来决定是以servlet对象方式存储还是servlet stack方式存储呢?其实,只要在开发servlet类时,实现一个SingleThreadModel接口即可。
如果需要线程同步的servlet类,例如:

Java代码  
  1. public class LoginServlet extends HttpServlet implements javax.servlet.SingleThreadModel{ …… }

但是值得注意的是,这种同步机制只是从servlet规范的角度来说提供的一种功能,在实际应用中并不能完全解决线程安全问题,例如如果servlet中有static数据访问等,因此如果对线程安全又比较严格要求的,最好还是用一些其他的自定义的解决方案。

Wrapper的基本功能已经说了。那么再说一个wrapper比较重要的概念。严格的说,并不是每一个访问资源对应一个wrapper对象。而是每一种访问资源对应一个wrapper对象。其大致可分为三种:

  • 处理静态资源的一个wrapper:例如html,jpg等静态资源的wrapper,它包含了一个tomcat的实现处理静态资源的缺省servlet:
Java代码  
  1. org.apache.catalina.servlets.DefaultServlet
  • 处理jsp的一个wrapper:例如访问的所有jsp文件,它包含了一个tomcat的实现处理jsp的缺省servlet:
Java代码  
  1. org.apache.jasper.servlet.JspServlet

它主要实现了对jsp的编译等操作

  • 处理servlet的若干wrapper:它包含了自定义的servlet对象,就是在web.xml中配置的servlet。

需要注意的是,前两种wrapper分别是一个,主要是其对应的是DefaultServlet及JspServlet。这两个servlet是在tomcat的全局conf目录下的web.xml中配置的,当app启动时,加载到内存中。

Xml代码  
  1. <servlet>
  2. <servlet-name>default</servlet-name>
  3. <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
  4. <init-param>
  5. <param-name>debug</param-name>
  6. <param-value>0</param-value>
  7. </init-param>
  8. <init-param>
  9. <param-name>listings</param-name>
  10. <param-value>false</param-value>
  11. </init-param>
  12. <load-on-startup>1</load-on-startup>
  13. </servlet>
  14. <servlet>
  15. <servlet-name>jsp</servlet-name>
  16. <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
  17. <init-param>
  18. <param-name>fork</param-name>
  19. <param-value>false</param-value>
  20. </init-param>
  21. <init-param>
  22. <param-name>xpoweredBy</param-name>
  23. <param-value>false</param-value>
  24. </init-param>
  25. <load-on-startup>3</load-on-startup>
  26. </servlet>

至此,阐述了tomcat的四大容器结构。 有时间接着探讨tomcat如何将这四大容器串起来运作的。

转载于:https://www.cnblogs.com/davidwang456/articles/11452693.html

tomcat架构分析(容器类)【转】相关推荐

  1. tomcat架构分析(容器类)

    Tomcat提供了engine,host,context及wrapper四种容器.在总体结构中已经阐述了他们之间的包含关系.这四种容器继承了一个容器基类,因此可以定制化.当然,tomcat也提供了标准 ...

  2. tomcat架构分析 (Session管理)【转】

    原文地址:https://www.iteye.com/blog/gearever-1546423 Session管理是JavaEE容器比较重要的一部分,在app中也经常会用到.在开发app时,我们只是 ...

  3. tomcat架构分析(valve源码导读)【转】

    原文地址:https://www.iteye.com/blog/gearever-1540028 源码面前,了无秘密                               ----侯捷  在to ...

  4. tomcat架构分析(connector BIO 实现)【转】

    原文地址:https://www.iteye.com/blog/gearever-1841586 在tomcat架构分析(概览)中已经介绍过,connector组件是service容器中的一部分.它主 ...

  5. tomcat架构分析(概览)【转】

    原文地址: https://www.iteye.com/blog/gearever-1532822 Tomcat是目前应用比较多的servlet容器.关于tomcat本身的特点及介绍,网上已经有很多描 ...

  6. tomcat架构分析(valve机制)

    出处:http://gearever.iteye.com 关于tomcat的内部逻辑单元的存储空间已经在相关容器类的blog里阐述了.在每个容器对象里面都有一个pipeline及valve模块.它们是 ...

  7. tomcat架构分析(valve机制)【转】

    原文地址:https://www.iteye.com/blog/gearever-1536022 关于tomcat的内部逻辑单元的存储空间已经在相关容器类的blog里阐述了.在每个容器对象里面都有一个 ...

  8. tomcat架构分析 (connector NIO 实现)【转】

    原文地址:https://www.iteye.com/blog/gearever-1844203 ller线程中维护的这个Selector标为主Selector.  Poller是NIO实现的主要线程 ...

  9. 应用服务器——tomcat架构分析

    先mark,后续补充 https://blog.csdn.net/qq_38245537/article/details/79009448

最新文章

  1. Java设计模式(代理模式-模板方法模式-命令模式)
  2. Python环境下的数据库编程
  3. OpenVINO安装之安装openCL
  4. leetcode 375. Guess Number Higher or Lower II | 375. 猜数字大小 II(动态规划思路总结)
  5. 八种Docker容器开发模式解析
  6. “妈,你当年咋看上我爸的?”网友晒爸妈结婚照,笑抽了!
  7. 【BZOJ1568】【Tyvj3490】Blue Mary开公司 李超线段树
  8. c#位数不够0补充完_人教版四年级上册数学第六单元《除数是两位数的除法》
  9. android listview 行高度自适应,Android ListView 长度自适应item的内容 计算listview的高度...
  10. java action 参数_Structs复习 Action传递参数
  11. 天正电气插入图块非常小与比例不符合
  12. ESXI7.0与6.7官网下载地址
  13. 应用市场显示服务器错误的是,win10应用商店打不开服务器出错怎么办
  14. CRACK小试牛刀:关于GALGAME银色遥远爆破记录
  15. 计算机二级考试题库 操作题,2016计算机二级考试题库:《C++》基本操作题练习...
  16. Arduino 寄存器(ADC)
  17. iOS开发之集成支付宝
  18. 汇编语言寄存器AX,BX,CX,DX
  19. iPhone X 适配
  20. 从原型图到成品:步步深入 CSS 布局

热门文章

  1. 太阳能工程联箱知识_暖通设计|太阳能热水系统设计参考手册(供参考)
  2. 计算机语言低下限高上限,原神双雷阵容厉不厉害
  3. 全志uboot修改_全志SDK编译问题解决二:build uboot only
  4. oracle if 嵌套语句吗,Lua嵌套if语句
  5. 数学系列 - 概率论 - 泊松分布和(负)指数分布
  6. CCF 2018年题目题解 - Python
  7. ubuntu查看OpenCV的版本和安装的库
  8. C++用顶层函数重载操作符(一)
  9. python 快速排序
  10. opencv 入门 demo