《How Tomcat Works》读书笔记(五)Container

摘要:上一章看到了一个简单的容器 HttpConnector connector = new HttpConnector(); SimpleContainer container = new SimpleContainer(); connector.setContainer(container); 容器是一个处理servlet请求,生成响应对象的组件。需要实现org.apache.cat

 

上一章看到了一个简单的容器

HttpConnector connector = new HttpConnector(); SimpleContainer container = new SimpleContainer(); connector.setContainer(container);

1 (35).jpg

 

容器是一个处理servlet请求,生成响应对象的组件。需要实现org.apache.catalina.Container接口。

tomcat中一共有四种概念级的容器:

Engine. 代表整个容器的servlet引擎。 Host.代表一个拥有一定数量Context的虚拟主机。
Context.代表一个Web项目.一个Context包含一个或多个Wrapper。 Wrapper.代表单独的一个servlet。

自上至下,等级递减,Engine最高,Wrapper最小。一个Engine可以有多个Host,一个Host可以有多个Context,一个Context可以有多个Wrapper,但是Wrapper等级最低,没资格带小弟,就只能表示他自己(一个servlet)。

每一个层级都对应一个接口:即Engine, Host, Context, Wrapper 他们都会继承 Container接口。看一下他们的关系图。

//添加 public void addChild(Container child); //删除 public void removeChild(Container child); //查找 public Container findChild(String name); //查找全部 public Container[] findChildren(); Pipelining Tasks

一个容器还可以包含很多个部件:如Loader(负责加载servlet), Logger, Manager, Realm, Resources.
容器可以挑选需要的部件自行设置。

更有趣的是,tomcat管理员可以在部署的时候修改配置文件(server.xml)来决定一个容器做什么。
这个功能就是Pipelining Tasks来实现的。

任务管道:一个容器会包含一个"任务管道",在container的invoke方法被调用时,会产生一个或多个value(相当于一个task,不知道为啥要定义成value,呵呵),这个value会被加入这个pipeline(管道)中,pipeline会逐个取出value并调用,知道pipeline里没有value为止。pipeline中会有一个baseValue,会在最后被调用。

当容器的invoke方法被调用时,它不需要知道怎么去做,把这个问题交给pipeline(调用pipeline的invoke方法),看一下他们的接口。

//代表Value上下文,包含一个value数组和baseValue。 public interface ValveContext { public String getInfo(); //调用下一个value public void invokeNext(Request request, Response response) throws IOException, ServletException; } //包含一个ValueContext public interface Pipeline { public Valve getBasic();//获取base value public void setBasic(Valve valve);//设置base value public void addValve(Valve valve);//增加一个value public Valve[] getValves();//获取全部的value //container会调用该方法,把问题交给pipeline处理,pipeline在这里会调用valueContext的invokeNext方法 public void invoke(Request request, Response response) throws IOException, ServletException; public void removeValve(Valve valve);//删除一个value } public interface Valve { public String getInfo(); //ValueContext会调用这个方法 public void invoke(Request request, Response response, ValveContext context) throws IOException, ServletException; } //一个Value类可以选择实现该接口,表明这个Value的实现和至多一个容器绑定在了一起。 public interface Contained { public Container getContainer(); public void setContainer(Container container); } 再看一个标准的ValueContext都做了什么就清楚啦。

public final class StandardValveContext implements ValveContext { protected static StringManager sm = StringManager.getManager(Constants.Package); protected String info = "org.apache.catalina.core.StandardValveContext/1.0"; protected int stage = 0; protected Valve basic = null;//base value protected Valve valves[] = null;//values public String getInfo() { return info; } public final void invokeNext(Request request, Response response) throws IOException, ServletException { int subscript = stage;//values[]的下标 stage = stage + 1; if (subscript < valves.length) {//逐个调用 valves[subscript].invoke(request, response, this); } else if ((subscript == valves.length) && (basic != null)) {//最后调用base value basic.invoke(request, response, this); } else { throw new ServletException (sm.getString("standardPipeline.noValve")); } } void set(Valve basic, Valve valves[]) { stage = 0; this.basic = basic; this.valves = valves; } } 我们再来回顾总结一下上面的内容。
一个container有一个pipeline. container的invoke方法调用pipeline的invoke方法.

//container public void invoke(Request request, Response response) throws IOException, ServletException { pipeline.invoke(request, response); } pipeline的invoke方法调用container中所有value然后调用container的basic valve的invoke方法.

//pipeline public void invoke(Request request, Response response) throws IOException, ServletException { //调用valueContext (new SimplePipelineValveContext()).invokeNext(request, response); } 在一个wrapper中, basic valve负责加载与之一对一关联的servlet类,并响应请求.

public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { SimpleWrapper wrapper = (SimpleWrapper) getContainer(); Servlet servlet = null; try { //加载servlet servlet = wrapper.allocate(); //处理请求 servlet.service(request.getRequest(), response.getResponse()); } catch (ServletException e){} } 在一个有很多子容器(wrapper)的context中, basic valve用一个mapper(tomcat 4)来查找子容器.如果找到一个子容器,就调用子容器的invoke方法,然后就又回到第一步.以此类推.

Wrapper wrapper = null; try { wrapper = (Wrapper) context.map(request, true); } if(wrapper != null){ wrapper.invoke(request, response); }

//wrapper 看看是不是和第一步一样啦? public void invoke(Request request, Response response) throws IOException, ServletException { pipeline.invoke(request, response); } 《How Tomcat Works》源代码下载地址