本章讲述Container,它的作用是处理request和response对象。本章讲述org.apache.catalina.Container接口,四种container:Engine, Host, Context, and Wrapper中的两种Context和Wrapper,其余两种在13章讲
。此前会先说说pipelining机制。
一,Container接口
每个Connector都要有一个Container:
HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
说一下四种Container的作用:
- Engine. Represents the entire Catalina servlet engine.
- Host. Represents a virtual host with a number of contexts.
- Context. Represents a web application. A context contains one or more wrappers.
- Wrapper. Represents an individual servlet.
这四种类型的Container都有它们各自的接口,又都实现了Container接口,在org.apache.catalina下。它们都有各自的实现类,一般都以Standard开头,都继承于ContainerBase类,在org.apache.catalina.core包下。
一个可以正常运转的服务器实际上并不需要全部这四种Container。
Container是有层次关系的,每个container都可以有0到多个孩子container,可以通过
public void addChild(Container child);
public void removeChild(Container child);
public Container findChild(String name);
public Container[] findChildren();
等方法处理。
Container里可以包含很多其它组件Loader, Logger, Manager, Realm, and Resources,所以接口中有这些组件的set get方法。
Container被设计成admin可以在部署时随意安排它执行哪些操作,这是通过编辑server.xml做到的,而具体是通过pipeline的机制实现的。
二,pipeline机制
pipeline机制很像Filter机制,pipeline就像filter chain,而valve就像filter。像Filter 一样,pipeline可以处理通过它的request和response对象,从第一个valve开始,当某个valve处理完,就传给pipeline中的下一个valve,直到最后一个basic valve被调用。可以想象代码可能会类似这样:
// invoke each valve added to the pipeline
for (int n=0; n<valves.length; n++) {
valve[n].invoke( ... );
}
// then, invoke the basic valve
basicValve.invoke( ... );
但是在这里,Tomcat没有用这种方法,而是用了org.apache.catalina.ValveContext接口,说实话,现在也没明白这样有什么好
处。
首先在connector调用container的invoke方法时,container会简单的调用pipeline的invoke方法:
public void invoke(Request request, Response response) throws IOException, ServletException {
pipeline.invoke(request, response);
}
pipeline里有个inner class叫StandardPipelineValveContext,实现了ValveContext接口,此接口中有个方法:
public void invokeNext(Request request, Response response) throws IOException, ServletException {
int subscript = stage;
stage = stage + 1;
// Invoke the requested Valve for the current request thread
if (subscript < valves.length) {
valves[subscript].invoke(request, response, this);
} else if ((subscript == valves.length) && (basic != null)) {
basic.invoke(request, response, this);
} else {
throw new ServletException (sm.getString("standardPipeline.noValve"));
}
}
具体的不多说了,一看便知,主要是先把valve走一遍,最后搞basic valve。
tomcat 5和这有点区别,把ValveContext的实现类单搞出来了,而不再是inner class。
除了pipeline,valve,valvecontext接口外,还有个Contained接口,主要是把valve和container联系起来。
三, Wrapper, Context接口
它们的实现类会分别在11、12章讲解。
wrapper是最小的context,它只负责一个特定的servlet的init,service,destroy等生命周期。有两个重要方法:allocate和load。
其中load负责载入和初始化servlet,也就是用classloader装载类,然后调用它的init方法。而allocate负责分配这个已经初始化的servlet,其中会检查servlet是否实现javax.servlet.SingleThreadModel接口,关于此会在11章详解
。
四,Application
第一个只用wrapper的例子没什么说的,除了load那部分可以看看。
第二个例子关于context,使用了mapper,但在tomcat 5中已经不用了
。因为用context时,需要知道请求的是哪个servlet,也就是要知道用哪个wrapper负责处理,所以在最初,把uri请求和对应的wrapper名字对儿放到了一个hashmap里,在请求来的时候,再从这个map里找。起用有个接口叫Mapper,里面有5个方法:
public Container getContainer();
public void setContainer(Container container);
public String getProtocol();
public void setProtocol(String protocol);
public Container map(Request request, boolean update);
这里面getProtocol,setProtocol是为了context可以用不同的mapper处理不同的协议,比如http协议和https协议可以用两个不同的mapper处理。而map方法则是返回要处理请求的wrapper的。