本文接下来介绍并分析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 (#改为@)