• How Tomcat Works(七)


    本文接下来介绍并分析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

  • 相关阅读:
    初学C++到底应该用什么工具比较合适——工具简析
    便携式办公套件LibreOffice Portable 4.0.1
    Hibernate和JPA之间的联系
    央视《家有妙招》整理版,共250招,值得永远收藏
    思科Vs华为:不可避免的对决
    Facebook手机刺激了谁?
    Facebook利用Home平台加速进军移动领域
    英特尔Haswell处理器已出货 预计6月推出
    苹果自建街景地图 或与谷歌针锋相对
    图片链接
  • 原文地址:https://www.cnblogs.com/chenying99/p/3235544.html
Copyright © 2020-2023  润新知