how tomcat works 五 servlet容器 上

來源:互聯網
上載者:User

標籤:servlet   tomcat   源碼   .重構   

servlet容器是用來處理請求servlet資源,並為Web用戶端填充response對象的模組。在上一篇文章(也就是書的第四章)我們設計了SimpleContainer類,讓他實現Container介面,也基本完成了容器的作用。但是我們得知道在實際的tomcat中有4類容器:
Engine: 表示整個Catalina servlet引擎;
Host: 包含一個或多個Context容器的虛擬機器主機;
Context:表示一個Web應用程式,一個Context可以包含多個Wrapper
Wrapper: 表示一個獨立的Servlet;
UML圖如下:



Container容器中包含addChild(Container con),removeChild(Container con),findChild(String name),findChildren()四個方法,方法的作用就不解釋了,Wrapper就表示一個獨立的Servlet所以,它的addChild方法體為空白。
在本節,我們主要展示Wrapper的特性,其他的容器以後再說。

首先我們看看時序圖(第一次畫 可能在時序圖規則上有些問題)


管道任務這節主要說明檔連接器調用了servlet容器的invoke方法後發生的事情。
管道包含了一個servlet容器要執行的任務(和本身的servlet無關,是指額外執行的任務)
一個Wrapper會包含一個管道,而一個管道會包含若干個Valve(閥)
public class SimpleWrapper implements Wrapper, Pipeline {  // the servlet instance  private Servlet instance = null;  private String servletClass;  private Loader loader;  private String name;  private SimplePipeline pipeline = new SimplePipeline(this);  protected Container parent = null;  ...}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];            //所有的閥}


在閥中,請求會像水一樣經過管道中的每一個閥,中的閥n會調用我們請求的servlet類。
再調用容器的invoke方法後,會到如下的方法
invokeNext是StandardPipelineValveContext類中的方法(StandardPipelineValveContext是管道的一個內部類,因此可以訪問管道的所有成員) 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


ok?如水般流過。下面這行代碼值得我們仔細看看

valves[subscript].invoke(request, response, this);

也可參閱拙作

<<說說struts2中攔截器的請求流程一(類比大致流程)>>
http://blog.csdn.net/dlf123321/article/details/40078583


public class HeaderLoggerValve implements Valve, Contained {  protected Container container;  public void invoke(Request request, Response response, ValveContext valveContext)    throws IOException, ServletException {    // Pass this request on to the next valve in our pipeline    valveContext.invokeNext(request, response);    System.out.println("Header Logger Valve");    ServletRequest sreq = request.getRequest();    if (sreq instanceof HttpServletRequest) {      HttpServletRequest hreq = (HttpServletRequest) sreq;      Enumeration<?> headerNames = hreq.getHeaderNames();      while (headerNames.hasMoreElements()) {        String headerName = headerNames.nextElement().toString();        String headerValue = hreq.getHeader(headerName);        System.out.println(headerName + ":" + headerValue);      }    }    else      System.out.println("Not an HTTP Request");    System.out.println("------------------------------------");  }}



這行代碼 valveContext.invokeNext(request, response); 看懂了吧
另一方面 大家應該能看出來,所有的閥都是逆向執行的,每次執行一個閥的時候,閥都會先傳遞給後面的閥,等到後面的執行完畢後,才執行自己的商務邏輯。
當通道裡的處了最後一個基礎閥外,都走了一遍的時候自然就執行如下代碼
 basic.invoke(request, response, this);
 問題是basic是誰?
這個咱們等會再說。

Pipeline介面我們在這裡使用的是SimplePipeline,它實現了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);}
Pipeline裡面有個基礎閥,是最後調用的,負責處理request對象魚response對象。因而這裡有getset方法
通道裡面存放的是閥,所以剩下的方法就不用多說了吧。
另外tomcat4中,還存在一個StandardPipelineValveContext,上面已經說它是SimplePipeline的一個內部類。

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


很清晰,不是嗎?

Contained介面
public interface Contained {public Container getContainer();public void setContainer(Container container);}


實現這個介面的類,至多與一個servlet容器相關聯。

Wrapper介面首先我們在文章開始的時候就說了,Wrapper封裝的是一個最基礎的servlet
這個介面裡面有兩個方法比較重要
public javax.servlet.Servlet allocate() throws javax.servlet.ServletException;
public void load() throws javax.servlet.ServletException;
allocate會分配一個已經初始化的servlet執行個體,
load會載入它。


Wrapper應用程式

類圖如下:



SimpleLoader類
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() );    }  }}


建構函式會初始會類載入器,供SimpleWrapper使用

SimpleWrapper類該類實現了 org.apache.catalina.Wrapper 介面並且實現了 allocate 方法和load 方法。
器建構函式如下
  public SimpleWrapper() {
    pipeline.setBasic(new SimpleWrapperValve());
  }
上文我們曾問過,basic從哪裡來,從這裡來!

SimpleWrapperValve類通過pipeline.setBasic(new SimpleWrapperValve())我們知道SimpleWrapperValve類是一個基礎閥,用來處理傳遞Servlet的參數,如下:
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) {    }  }


 應該沒有什麼問題。

ClientIPLoggerValve類是用來顯示用戶端的ip地址的,代碼從略,另外還有HeaderLoggerValve,用來顯示http的要求標頭,代碼從略。


Bootstrap1啟動類
public final class Bootstrap1 {  @SuppressWarnings("deprecation")public static void main(String[] args) {/* call by using http://localhost:8080/ModernServlet,   but could be invoked by any name */    HttpConnector connector = new HttpConnector();    Wrapper wrapper = new SimpleWrapper();    wrapper.setServletClass("ModernServlet");       Loader loader = new SimpleLoader();        Valve valve1 = new HeaderLoggerValve();    Valve valve2 = new ClientIPLoggerValve();    wrapper.setLoader(loader);    ((Pipeline) wrapper).addValve(valve1);    ((Pipeline) wrapper).addValve(valve2);    connector.setContainer(wrapper);    try {      connector.initialize();      connector.start();      // make the application wait until we press a key.      System.in.read();    }    catch (Exception e) {      e.printStackTrace();    }  }}


運行結果如下:

看到了,其實在部分裡,不管你請求那個服務,返回的結果都一樣,為什麼?這個還用我來解釋嗎?


這一章東西比較多,關於context容器的使用,我們放在下一節中。

how tomcat works 五 servlet容器 上

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.