本文接下来介绍并分析servlet容器,servlet容器是用来处理请求servlet资源,并为web客户端填充response对象的模块。

servlet容器是org.apache.catalina.Container接口的实例,在tomcat中,有四种类型的容器,分别为Engine、Host 、Context和Wrapper。

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

这些容器构成一个自顶向下的等级结构,高等级的容器可以具有多个直接下属等级的容器实例(子容器),这有点类似于composite模式,不过还是有差别的

org.apache.catalina.Container接口声明如下

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

上面方法均为操作子容器的相关方法

容器可以包含一些支持的组件,诸如载入器、记录器、管理器、领域和资源等,我们可以通过编辑server.xml文件来决定使用哪种容器。

下面我们来分析servlet容器是怎么执行任务的,这里就要提到servlet容器的管道模型,管道包含了该servlet容器将要调用的任务,而阀则表示一个具体的执行任务;在servlet容器的管道中,有一个基础阀,也可以添加任意数量的额外阀,阀的数量通常是指额外添加的阀的数量,不包括基础阀

这里就好像servlet编程中的过滤器模型,管道好比过滤器链,而阀则是具体的过滤器;基础阀总是最后一个执行的。

这里涉及几个相关的接口,包括Pipeline、Valve、ValveContext 和Contained

Pipeline接口声明如下

public interface Pipeline {

    public Valve getBasic();

    public void setBasic(Valve valve);

    public void addValve(Valve valve);

    public Valve[] getValves();

    public void invoke(Request request, Response response)
throws IOException, ServletException; public void removeValve(Valve valve); }

Valve接口声明如下

public interface Valve {
public String getInfo(); public void invoke(Request request, Response response,
ValveContext context)
throws IOException, ServletException;
}

ValveContext接口声明

public interface ValveContext {

    public String getInfo();

    public void invokeNext(Request request, Response response)
throws IOException, ServletException;
}

Contained接口声明

public interface Contained {

    public Container getContainer();

    public void setContainer(Container container);

}

阀可以选择是否实现该接口,设置阀与一个servlet容器相关连

下面我们来学习Wrapper容器,Wrapper容器表示一个独立的servlet定义,负责管理其基础servlet类的生命周期,它继承了Container接口,另外添加了额外方法声明。其中比较重要的方法声明是load()方法和allocate()方法,均与载入及初始化servlet类相关(供基础阀调用,基础阀持有Wrapper容器实例引用)

下面来分析一个简单的Wrapper类,该类实现了org.apache.catalina.Wrapper接口和org.apache.catalina.Pipeline接口

public class SimpleWrapper implements Wrapper, Pipeline {

  // the servlet instance
private Servlet instance = null;
private String servletClass;
private Loader loader; private SimplePipeline pipeline = new SimplePipeline(this);
protected Container parent = null; public SimpleWrapper() {
pipeline.setBasic(new SimpleWrapperValve());
} public synchronized void addValve(Valve valve) {
pipeline.addValve(valve);
} public Servlet allocate() throws ServletException {
// Load and initialize our instance if necessary
if (instance==null) {
try {
instance = loadServlet();
}
catch (ServletException e) {
throw e;
}
catch (Throwable e) {
throw new ServletException("Cannot allocate a servlet instance", e);
}
}
return instance;
} private Servlet loadServlet() throws ServletException {
if (instance!=null)
return instance; Servlet servlet = null;
String actualClass = servletClass;
if (actualClass == null) {
throw new ServletException("servlet class has not been specified");
} Loader loader = getLoader();
// Acquire an instance of the class loader to be used
if (loader==null) {
throw new ServletException("No loader.");
}
ClassLoader classLoader = loader.getClassLoader(); // Load the specified servlet class from the appropriate class loader
Class classClass = null;
try {
if (classLoader!=null) {
classClass = classLoader.loadClass(actualClass);
}
}
catch (ClassNotFoundException e) {
throw new ServletException("Servlet class not found");
}
// Instantiate and initialize an instance of the servlet class itself
try {
servlet = (Servlet) classClass.newInstance();
}
catch (Throwable e) {
throw new ServletException("Failed to instantiate servlet");
} // Call the initialization method of this servlet
try {
servlet.init(null);
}
catch (Throwable f) {
throw new ServletException("Failed initialize servlet.");
}
return servlet;
} public Loader getLoader() {
if (loader != null)
return (loader);
if (parent != null)
return (parent.getLoader());
return (null);
}public void invoke(Request request, Response response)
throws IOException, ServletException {
pipeline.invoke(request, response);
} public void load() throws ServletException {
instance = loadServlet();
}
// method implementations of Pipeline
public Valve getBasic() {
return pipeline.getBasic();
} public void setBasic(Valve valve) {
pipeline.setBasic(valve);
} public Valve[] getValves() {
return pipeline.getValves();
} public void removeValve(Valve valve) {
pipeline.removeValve(valve);
} }

上面的SimpleWrapper类由于实现了org.apache.catalina.Pipeline接口接口,同时与该接口相关的实现方法都是调用引用的成员变量SimplePipeline pipeline = new SimplePipeline(this)的对应方法,因此我们可以理解为SimpleWrapper类为SimplePipeline的包装类

在它的invoke()方法里面调用了成员变量的SimplePipeline pipeline = new SimplePipeline(this)的invoke()方法,这里构造函数传入SimpleWrapper实例本身,可以猜想是为了获取其载入器及具体的servlet实现类(注:该方法为Container接口与Pipeline接口都具有的方法声明,因此SimpleWrapper类只要一个实现),下面我们继续分析SimplePipeline相关实现

public class SimplePipeline implements Pipeline {

  public SimplePipeline(Container container) {
setContainer(container);
} // The basic Valve (if any) associated with this Pipeline.
protected Valve basic = null;
// The Container with which this Pipeline is associated.
protected Container container = null;
// the array of Valves
protected Valve valves[] = new Valve[0]; public void setContainer(Container container) {
this.container = container;
} public Valve getBasic() {
return basic;
} public void setBasic(Valve valve) {
this.basic = valve;
((Contained) valve).setContainer(container);
} public void addValve(Valve valve) {
if (valve instanceof Contained)
((Contained) valve).setContainer(this.container); synchronized (valves) {
Valve results[] = new Valve[valves.length +1];
System.arraycopy(valves, 0, results, 0, valves.length);
results[valves.length] = valve;
valves = results;
}
} public Valve[] getValves() {
return valves;
} public void invoke(Request request, Response response)
throws IOException, ServletException {
// Invoke the first Valve in this pipeline for this request
(new SimplePipelineValveContext()).invokeNext(request, response);
} public void removeValve(Valve valve) {
} // this class is copied from org.apache.catalina.core.StandardPipeline class's
// StandardPipelineValveContext inner class.
protected class SimplePipelineValveContext implements ValveContext { protected int stage = 0; public String getInfo() {
return null;
} 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("No valve");
}
}
} // end of inner class }

invoke()方法里面调用内部类SimplePipelineValveContext(实现了ValveContext接口),遍历执行各个阀的invoke()方法

wrapper容器执行的基本流程如上所述,下面我们来进一步分析相关辅助类及实现类等

在应用初始化servlet容器时,我们需要为其指定一个载入器,下面是一个简单的载入器,实现了Loader接口

public class SimpleLoader implements Loader {

  public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webroot"; ClassLoader classLoader = null;
Container container = null; public SimpleLoader() {
try {
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(WEB_ROOT);
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
urls[0] = new URL(null, repository, streamHandler);
classLoader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
} } public ClassLoader getClassLoader() {
return classLoader;
} public Container getContainer() {
return container;
}
//这里省略其余代码
}

基础阀是干嘛的呢,具体来说是调用具体servlet的service()方法(管道持有对基础阀的引用)

public class SimpleWrapperValve implements Valve, Contained {

  protected Container container;

  public void invoke(Request request, Response response, ValveContext valveContext)
throws IOException, ServletException { SimpleWrapper wrapper = (SimpleWrapper) getContainer();
ServletRequest sreq = request.getRequest();
ServletResponse sres = response.getResponse();
Servlet servlet = null;
HttpServletRequest hreq = null;
if (sreq instanceof HttpServletRequest)
hreq = (HttpServletRequest) sreq;
HttpServletResponse hres = null;
if (sres instanceof HttpServletResponse)
hres = (HttpServletResponse) sres; // Allocate a servlet instance to process this request
try {
servlet = wrapper.allocate();
if (hres!=null && hreq!=null) {
servlet.service(hreq, hres);
}
else {
servlet.service(sreq, sres);
}
}
catch (ServletException e) {
}
} public String getInfo() {
return null;
} public Container getContainer() {
return container;
} public void setContainer(Container container) {
this.container = container;
}
}

其他额外添加的阀本人就不在具体描述了,至此SimpleWrapper容器分析完毕!

---------------------------------------------------------------------------

本系列How Tomcat Works系本人原创

转载请注明出处 博客园 刺猬的温驯

本人邮箱: chenying998179#163.com (#改为@)

本文链接 http://www.cnblogs.com/chenying99/p/3235544.html

How Tomcat Works(七)的更多相关文章

  1. How Tomcat works — 七、tomcat发布webapp

    目录 什么叫发布 webapp发布方式 reload 总结 什么叫发布 发布就是让tomcat知道我们的程序在哪里,并根据我们的配置创建Context,进行初始化.启动,如下: 程序所在的位置 创建C ...

  2. 攻城狮在路上(肆)How tomcat works(零) 前言说明

    最近几篇是关于How tomcat works一书的读书笔记. 通过数个章节逐渐实现一个tomcat的功能. 源码下载地址:http://zhidao.baidu.com/share/7007af0f ...

  3. How Tomcat works — 四、tomcat启动(3)

    上一节说到StandardService负责启动其子组件:container和connector,不过注意,是有先后顺序的,先启动container,再启动connector,这一节先来看看conta ...

  4. How Tomcat Works(十四)补充

    在How Tomcat Works(十四)中,本人并没有对javax.servlet.Filter及javax.servlet.FilterChain做详细的描述,本文在这里做一下补充 FilterC ...

  5. How Tomcat Works(十八)

    在前面的文章中,如果我们要启动tomcat容器,我们需要使用Bootstrap类来实例化连接器.servlet容器.Wrapper实例和其他组件,然后调用各个对象的set方法将它们关联起来:这种配置应 ...

  6. How Tomcat Works(十七)

    在前面的文章中,已经学会了如何通过实例化一个连接器和容器来获得一个servlet容器,并将连接器和容器相关联:但在前面的文章中只有一个连接器可用,该连接器服务8080端口上的HTTP请求,无法添加另一 ...

  7. How Tomcat Works(十六)

    本文接下来会介绍Host容器和Engine容器,在tomcat的实际部署中,总是会使用一个Host容器:本文介绍Host接口和Engine接口及其相关类 Host容器是org.apache.catal ...

  8. How Tomcat Works(十五)

    本文接下来分析Context容器,Context容器实例表示一个具体的Web应用程序,其中包括一个或多个Wrapper实例:不过Context容器还需要其他的组件支持,典型的如载入器和Session管 ...

  9. How Tomcat Works(十四)

    我们已经知道,在tomcat中有四种类型的servlet容器,分别为Engine.Host.Context 和Wrapper,本文接下来对tomcat中Wrapper接口的标准实现进行说明. 对于每个 ...

随机推荐

  1. web前端调试工具

    1.firebug入门指南 http://www.ruanyifeng.com/blog/2008/06/firebug_tutorial.html 2. Console命令详解,让调试js代码变得更 ...

  2. 对于fmri的设计矩阵构造的一个很直观的解释-by 西南大学xulei教授

    本程序意在解释这样几个问题:完整版代码在本文的最后. 1.实验的设计如何转换成设计矩阵? 2.设计矩阵的每列表示一个刺激条件,如何确定它们? 3.如何根据设计矩阵和每个体素的信号求得该体素对刺激的敏感 ...

  3. ubuntu下Rhythmbox播放器乱码问题解决方案

    (注:本文部分内容转自互联网)<a href="http://riden001.com/wp-content/uploads/2014/11/45.jpg"><i ...

  4. VPN协议PPTP/L2TP/OpenVPN及SSH的区别与详解

    大家在使用VPN的时候都会看到商家有提供PPTP VPN.L2TP  VPN.OpenVPN.SSH代理等多种协议选择,但是许多朋友却不知道它们之间有什么区别,也不知道该如何选择,今天整理了一些日常收 ...

  5. 为什么Jquery对input file控件的onchange事件只生效一次

    今天在做jquery对input file控件的onchange事件进行监听,就一直只生效一次,不知道Jquery为什么对file控件没有做到每次改变触发onchange事件的效果,但是还是有好几种解 ...

  6. PHP 截取字符串专题

    1. 截取GB2312中文字符串 < ?php//截取中文字符串function mysubstr($str, $start, $len) {    $tmpstr = "" ...

  7. FZU 2171(线段树的延迟标记)

    题意:容易理解. 分析:时隔很久,再一次写了一道线段树的代码,之前线段树的题也做了不少,包括各种延迟标记,但是在组队分任务之后,我们队的线段树就交给了另外一个队友在搞, 然后我就一直没去碰线段树的题了 ...

  8. C#中实现对Excel特定文本的搜索

    打开Excel的VBA帮助,查看Excel的对象模型,很容易找到完成这个功能需要的几个集合和对象: Application.Workbooks. Workbook.Worksheets还有Worksh ...

  9. MSSQL 查询分组前N条记录

    sql语句中,查询分组中前n条记录的sql语句如下 第一种方法 select * from consultingfeebill awhere n>(select count(*) from co ...

  10. LINQ to SQL使用教程

    前些时间用LINQ to SQL做了一些项目,现在打算总结一下,帮助新手快速入门,并写一些别的教程没提到的东西. 一.LINQ to SQL和别的LINQ to XXX有什么关系?二.延迟执行(Def ...