• Tomcat Context 组件介绍(转载)


    来源:http://diecui1202.iteye.com/blog/1037370

    Context代表一个Web应用,它运行在某个指定的虚拟主机(Host)上;每个Web应用都是一个WAR文件,或是一个包含WAR解压后的文件的目录;


    Connector组件接收到http请求后,通过将请求URI的最长可能前缀与每个Context的path进行匹配,然后选择相应的Web应用来处理这个http请求。之后,Context会根据web application deployment descriptor文件中定义的servlet映射,会选择一个正确的Servlet来处理请求。Servlet映射必须定义在该Web应用目录层次结构中的/WEB-INF/web.xml中。

    一、几个重要的概念
    1、context frgament file
    Context片断文件,即描述Context配置的xml文件;它有三个层级:
    a) Engine Scoped:适用于Engine下的所有Web应用程序,位于$CATALINA_BASE/conf/context.xml;
    b) Host Scoped:适用于指定的Host组件下的应用程序,位于$CATALINA_BASE/[Engine]/[Host]/context.xml.default;
    c) Web Application Scoped:代表一个独立的应用程序,它可以位于$CATALINA_BASE/[Engine]/[Host]/[ContextPath].xml或应用程序中的META-INF/context.xml;

    对于应用程序中内嵌的context.xml文件,有两个局限性:
    1) 当server.xml文件中的Host节点指定deployXML属性为false时,则会忽略Web应用程序内嵌的context.xml;
    2) 内嵌的context.xml不能指定context path;
    2、appBase、docBase&context path
    a) appBase:Host组件中需要部署的应用的目录,如webapps;也可以指定绝对路径,将Web应用程序放在tomcat之外;
    b) docBase:应用程序资源所有的路径;应用程序的资源包括HTML、CSS、JS、jar、class文件等,其中静态资源直接放在该docBase或其子目录下,jar文件放在WEB-INF/lib目录下,class文件则放在WEB-INF/classes目录下;
    c) context path:唯一代表一个应用;

    二、如何配置Context?
    配置context有多种方式,如下:
    1、将应用文件夹或war文件直接copy到tomcat的webapps目录下,这样tomcat启动的时候会将webapps目录下的文件夹或war文件的内容当成应用部署。这种方式最简单且无须书写任何配置文件。
    2、在tomcat的server.xml配置文件中的Host节点下增加Context子节点,如:
    Java代码 复制代码 收藏代码
    1. <Context path="/test" docBase="D:private omcat est.war" />  
    <Context path="/test" docBase="D:private	omcat	est.war" />
    其中,path即context path;docBase指向应用所在的文件夹或war文件,可以是绝对路径,也可以是相对路径(相对该Context所在的Host的appBase属性值);
    3、在tomcat的conf/[Engine]/[Host]目录下新建xml文件,文件名为context path,内容如下:
    Java代码 复制代码 收藏代码
    1. <Context docBase="D:private omcat est.war"  
    2.          privileged="true" antiResourceLocking="false" antiJARLocking="false">   
    •   <!-- Link to the user database we will get roles from -->   
    •   <ResourceLink name="users" global="UserDatabase"  
    •                 type="org.apache.catalina.UserDatabase"/>   
    • </Context>  
    <Context docBase="D:private	omcat	est.war"
             privileged="true" antiResourceLocking="false" antiJARLocking="false">
      <!-- Link to the user database we will get roles from -->
      <ResourceLink name="users" global="UserDatabase"
                    type="org.apache.catalina.UserDatabase"/>
    </Context>

    其中,docBase与第二种方式中的含义一样;

    当Host的autoDeploy属性值为true时,以上三种配置Context的方式中,只有第1、3两种方式配置署的应用不需要重启tomcat即可完成部署;第二种方式需要重启tomcat;另外,第1种方式不能指定特定的context path;

    三、启动Context
    在《Tomcat Host组件》这篇文档中提到,应用的部署是由HostConfig完成的;Host组件在初始化过程中会扫描三种不同类型的应用:context.xml描述文件、war包、文件夹;对于直接通过修改server.xml文件来配置的应用(上一节中的第2种场景),则是在启动tomcat解析server.xml文件时根据Context子节点来构造相应的应用;

    不管是哪种方式配置Context(context.xml描述文件、war包、文件夹、配置server.xml的Context节点),最终都是构造出一个StandardContext实例,并将其作为子容器添加到Host组件中;与Host组件类似,在构造StandardContext实例时,会为每个StandardContext对象构造一个ContextConfig实例,该ContextConfig实例实现了LifecycleListener接口,它会监听StandardContext实例的各个生命周期事件,并针对不同的事件做出不同的响应;

    当StandardContext实例以子容器被添加到Host组件中时,会触发StandardContext实例的start()方法,至此Context工作由此展开:
    1、Context初始化和启动
    Context初始化时,主要会触发init事件,并通过ContextConfig来完成初始化工作;
    Context启动时主要完成如下事情:
    a) 触发before_start事件来处理文件锁的问题(见下一小节before_start事件);
    b) 根据docBase是war包还是文件夹,初始化应用程序资源的访问对象;
    c) 初始化context特定的classloader:WebappLoader;
    d) 计算work directory并初始化ServletContext:tomcat会为每个Context生成一个work directory,位于$CATALINA_BASE/work/[Engine]/[Host]目录下,work directory目录名根据context path而来(将“/”转换为“_”);work directory目录用于存放由jsp生成的servlet的class文件;
    e) 启动内嵌组件,如Loader等;
    f) 触发start事件来解析web应用描述符文件:如Servlet配置、Filter配置、Listener配置、session-timeout、welcome-file以及servlet参数等;
    g) 启动Listerer;
    h) 根据backgroundProcessorDelay决定是否需要开启后台线程:该后台线程可以用于管理session超时、监听类的变化来决定是否需要重启Context;当Context的reloadable属性为true时,如果应用程序的资源发生变化时,会通过backgroundProcess()来完成应用的重启;
    i) 根据Servlet配置的loadOnStartup参数及大小顺序决定是否需要初始化Servlet及其初始化顺序;
    2、ContextConfig事件处理
    a) init事件
    ContextConfig在处理init事件时,主要工作是:
    1) 实例化了两个Digester类,分别为webDigester和contextDigester;webDigester用于解析web.xml配置文件,contextDigester用于解析前面提到的三个层级的context fragment file;在解析context fragment file时,主要的工作是解析内嵌元素,如Listener, Loader, Manager, Parameter, Resources, Valve以及WatchedResource,并将它们设置到该Context对应的属性上;;
    2) 调整docBase属性值(即应用程序资源所在的位置):

    • 在配置Context时如果没有指定docBase,则根据context path计算得出(“/”会替换成“#”,空会替换成ROOT),并转化为绝对路径;
    • 根据Host中配置的unpackWARs属性,决定是否将配置在tomcat之外的war包解压到appBase下,并以context path为目录名(“/”会替换成“#”);此时docBase指向解压后的文件夹;如果的unpackWARs为false,则不会自动解压,此时docBase不变;

    用流程图来表述如下:

    b) before_start事件
    在该过程中,主要是针对反文件锁做了一定的处理;在windows下,当ClassLoader加载类时,会打开相应的jar包,此时该jar包会被锁定,是不能被删除的;在tomcat中,当Context的antiResourceLocking设置为true时,如果删掉应用程序的war包时,对应的文件夹也会被删除,这是怎么做到的呢?
    很简单,把应用程序的资源拷贝到一个临时的目录,然后将docBase指向该临时目录,这样锁定的都是副本;
    c) start事件
    当start事件发生时,ContextConfig会针对该Context完成如下工作:
    1) 解析应用程序描述符:
       a) $CATALINA_BASE/conf/web.xml:作用于所有的Web应用程序;
       b) $CATALINA_BASE/conf/[Engine]/[Host]]/web.xml.default:作用于指定Host下的所有Web应用程序;
       c) WEB-INF/web.xml:仅作用于当前Web应用程序;
    2) 新版本中增加了对Listener、Filter、Servlet注解的支持;
    3) 对web.xml里设置的一些roles进行一些简单的校验,,然后全部放到context持有的字符串数组securityRoles中;(这块没有仔细研究过,熟悉的同学可以解释一下);
    4) 根据web.xml里配置的login-config等权限安全访问来确定是否为本context配置一个用于确定访问权限的Valve;如果需要,则通过addValve添加到pipeline中;
    d) destroy事件
    在Context需要reload时,会触发destory事件,此时则会将该Context的work directory目录删除;
    e) stop事件
    此时会清除所有与Context相关的配置,如子容器、从web.xml读取的配置信息(如Listener、Filter、Servlet等);

    四、Context对请求的处理
    Context在初始化时,会创建StandardContextValve的实例并加入pipeline中;Host组件在接收到请求后,会调用以下方法将请求转交给Context来处理:

    Java代码 复制代码 收藏代码
    1. context.getPipeline().getFirst().invoke(request, response);  
    context.getPipeline().getFirst().invoke(request, response);

    也就是会调用StandardContextValve的invoke方法,其具体流程如下:
    1、先检查所请求的资源是否是保护资源,如果是,则直接返回客户端“资源不存在”(404);
    2、如果应用在reload,则sleep 1s;此处是通过忙等待实现;同时用新的WebappLoader替换当前的ClassLoader;
    3、如果找不到该请求对应的Wrapper(Servlet),则同样返回404;
    4、触发ServletRequestListener的requestInitialized()方法;
    5、将请求交由wrapper进行处理;

    Java代码 复制代码 收藏代码
    1. wrapper.getPipeline().getFirst().invoke(request, response);  
    wrapper.getPipeline().getFirst().invoke(request, response);

    6、wrapper处理完后,再触发发ServletRequestListener的requestDestroyed()方法;

  • 相关阅读:
    set与map
    统计一个字符串中出现了多少个不同的字符
    求序列中所有不同的连续子串的数量
    79、idea IDE Eval Reset
    78、idea控制台报 java: 无效的目标发行版: 14
    16、docker安装minio
    77、idea中添加maven项目右侧无maven
    76、mysql5.7安装教程
    74、js向上递归
    72、解决IntelliJIDEA没有Spring Initializr
  • 原文地址:https://www.cnblogs.com/xiaoerlang/p/3358692.html
Copyright © 2020-2023  润新知