欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Tomcat之Servlet容器Container。

程序员文章站 2022-03-22 16:50:02
...

Container是容器的父接口,所有子容器都必须实现这个接口,Container容器的设计用的是典型的责任链的设计模式,他由4个子容器组件构成,分别实Engine、Host、Context和Wrapper,这4个组件不是平行的,而是父子关系,Engine包含Host,Host包含Context,Context包含Wrapper。通常一个Servlet class对应一个Wrapper,如果有多个Servlet,则可以定义多个Wrapper;如果有多个Wrapper,则要定义一个更高的Container,如Context,Context通常对应如下的配置:

<Context path="/library/" docBase="D:\projects\library\deploy\target\library.war" reloadable="true" />

容器的整体设计

Context还可以定义在父容器Host中,Host不是必须的,但是要运行war程序,就必须要用Host,因为在war中必有web.xml文件,这个文件的解析就需要Host。如果要有多个Host就要定义一个top容器Engine。而Engine没有父容器了,一个Engine代表一个完整的Servlet引擎。

当Connector接受一个连接请求时,会将请求交给Container,Container是如何处理这个请求的?这4个组件是怎么分工的?怎么把请求传给特定的子容器的?又是如何将最终的请求交给Servlet处理的?下图是这个过程的时序图。

Tomcat之Servlet容器Container。

这里看到了Value,是不是很熟悉?没错,Value的设计在其他框架中也有用到,同样Pipeline的原理基本上也是相似的。他是一个管道,Engine和Host都会执行这个Pipeline,你可以在这个管道上增加任意的Value,Tomcat会挨个执行这些Value,而且4个组件都会有自己的一套Value集合。你怎么才能定义自己的Value呢?在server.xml文件中可以添加,如给Engine和Host增加一个Value,代码如下:

<Engine defaultHost="localhost" name="Catalina">
    <Value className="org.apache.catalina.values.RequestDumperValue" />
    ......
    <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
        <Value className="org.apache.catalina.values.FastCommonAccessLogValue" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="common" resolveHosts="false" />
        ......
    </Host>
</Engine>

StandardEngineValue、StandardHostValue是Engine和Host默认的Value,最后一个Value负责将请求传给他们的子容器,以继续往下执行。

前面是Engine和Host容器的请求过程,下面看Context和Wrapper容器是如何处理请求的。下图是处理请求的时序图。

Tomcat之Servlet容器Container。

从Tomcat 5 开始,子容器的路由放在了request中,在request中保存了当前请求正在处理的Host、Context和Wrapper。

Engine容器

Engine容器比较简单,他定义了一些基本的关联关系。

他的标准实现类是StandardEngine,注意Engine没有父容器,如果调用setParent方法将会报错。添加的子容器也只能是Host类型的。

他的初始化方法也就是初始化和他相关联的组件,以及一些事件的监听。

Host容器

Host是Engine的子容器,一个Host在Engine中代表一个虚拟主机,这个虚拟主机的作用就是运行多个应用,他负责安装和展开这些应用,并且标识这个应用以便能够区分他们。他的子容器通常是Context,他除了关联子容器外,还保存一个主机应有的信息。

除了所有容器都继承的ContainerBase外,StandardHost还实现了Deployer接口。

Deployer接口的实现是StandardHostDeployer,这个类实现了最主要的几个方法,Host可以调用这些方法完成应用的部署等。

Context容器

Context代表Servlet的Context,他具备了Servelt运行的基本环境,理论上只要有Context就能运行Servlet了。简单地Tomcat可以没有Engine和Host。

Context最重要的功能就是管理他里面的Servlet实例,Servlet实例在Context中是以Wrapper出现的。还有一点就是Context如何才能找到正确的Servlet来执行他呢?Tomcat 5以前是通过一个Mapper类来管理的,在Tomcat 5 以后这个功能被移到了Request中,在前面的时序图中就可以发现获取子容器都是通过Request来分配的。

Context准备Servlet的运行环境是从Start方法开始的。他的主要作用是设置各种资源属性和管理组件,还有一个非常重要的作用就是启动子容器和Pipeline。

我们知道Context的配置文件中有个reloadable属性,如下面的配置:

<Context path="/library/" docBase="D:\projects\library\deploy\target\library.war" reloadable="true" />

当这个reloadable设为true时,war被修改后Tomcat会自动重新加载这个应用。如何做到这点呢?这个功能是在StandardContext的backgroundProcess方法中实现的。

他会调用reload方法,而reload方法会先调用stop方法,然后再调用Start方法,完成Context的一次重新加载。可以看出,执行reload方法的条件是reloadable为true和应用被修改,那么这个backgroundProcess方法是怎么被调用的呢?

这个方法是在ContainerBase类中定义的内部类ContainerBackgroundProcessor中被周期调用的,这个类运行在一个后台线程中。他会周期的执行run方法,他的run方法会周期的调用所有容器的backgroundProcess方法,因为所有容器都会继承ContainerBase类,所以所有容器都能够在backgroundProcess方法中定义周期执行的事件。

Wrapper容器

Wrapper代表一个Servlet,他负责管理一个Servlet,包括Servlet的装载、初始化、执行及资源回收。Wrapper是最底层的容器,他没有子容器了,所以调用他的addChild将会报错。

Wrapper的实现类是StandardWrapper,StandardWrapper还实现了拥有一个Servlet初始化信息的ServletConfig,由此看出StandardWrapper将直接和Servlet的各种信息打交道。

LoadServlet是一个非常重要的方法,他基本上描述了对Servlet的操作,装载了Servlet后就会调用Servlet的init方法,同时会传一个StandardWrapperFacade对象给Servlet,这个对象包装了StandardWrapper.

Servlet可以获得信息都在StandardWrapperFacade里封装,这些信息又是在StandardWrapper对象中拿到的,所以Servlet可以通过ServletConfig拿到有限的容器的信息。

当Servlet被初始化完成后,就等着StandardWrapperValue去调用他的Servlice方法了,调用Service方法之前要调用Servlet所有的filter。

相关标签: tomcat