tomcat4容器之wrapper和context

1计维、類圖

image.png

2财异、處理過程(以Wrapper為例)

在連接器部分了解到,對于客戶端的處理婉称,連接器是在解析相關(guān)的協(xié)議后踩寇,最終調(diào)用了Container的invoke方法來處理的,這里看一下Wrapper的處理過程,Wrapper的標準實現(xiàn)為StandardWrapper:

public StandardWrapper() {
        super();
        // 設置基本的 Value
        pipeline.setBasic(new StandardWrapperValve());
    }

根據(jù)類圖可知,invoke方法在ContainerBase中實現(xiàn):

protected Pipeline pipeline = new StandardPipeline(this);

public void invoke(Request request, Response response)
        throws IOException, ServletException {
        pipeline.invoke(request, response);
    }

其調(diào)用了Pipeline的invoke方法:

public void invoke(Request request, Response response)
        throws IOException, ServletException {
        // Invoke the first Valve in this pipeline for this request
        (new StandardPipelineValveContext()).invokeNext(request, response);
    }
protected class StandardPipelineValveContext implements ValveContext {

        protected int stage = 0;

        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
                    (sm.getString("standardPipeline.noValve"));
            }
        }
}

以ClientIPLoggerValve為例:

public void invoke(Request request, Response response, ValveContext valveContext)
    throws IOException, ServletException {
    // Pass this request on to the next valve in our pipeline
// 這里先調(diào)用了ValveContext的invokeNext方法
    valveContext.invokeNext(request, response);
// 然后才是處理自己的邏輯
    System.out.println("Client IP Logger Valve");
    ServletRequest sreq = request.getRequest();
    System.out.println(sreq.getRemoteAddr());
    System.out.println("------------------------------------");
  }

每一個Pipeline維持著一個基本的Valve和一個Valve[]草丧,Valve通過addValve()方法加入到pipeline中,當Container的invoke被Connector調(diào)用的時候莹桅,Container內(nèi)部調(diào)用了pipeline的invoke方法昌执,而pipeline內(nèi)部則是通過ValveContext的invokeNext方法來實現(xiàn)Valve的依次執(zhí)行,根據(jù)每個Valve的內(nèi)部實現(xiàn)统翩,這里的執(zhí)行順序為basic valve會被優(yōu)先執(zhí)行,然后其他的Valve是按照后進先出的順序執(zhí)行此洲,這個從Valve中的invoke方法實現(xiàn)也可以驗證厂汗,每個Valve在執(zhí)行前都是先執(zhí)行了ValveContext的invokeNext方法。

// 構(gòu)建一個包裝容器呜师,一個包裝容器代表一個具體的servlet
    Wrapper wrapper = new SimpleWrapper();
    wrapper.setServletClass("ModernServlet");
    // 構(gòu)建兩個Valve
    Valve valve1 = new HeaderLoggerValve();
    Valve valve2 = new ClientIPLoggerValve();
    // 添加閥門(任務)
    ((Pipeline) wrapper).addValve(valve1);
    ((Pipeline) wrapper).addValve(valve2);

Basic Valve:

public void invoke(Request request, Response response, ValveContext valveContext)
    throws IOException, ServletException {
    System.out.println("00000000000 SimpleWrapperValve Basic Valve ");
    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) {
    }
  }
#######Basic Valve被優(yōu)先執(zhí)行
00000000000 SimpleWrapperValve Basic Valve 
ModernServlet -- init
#######Client IP Logger Valve被執(zhí)行
00000000000 Client IP Logger Valve
0:0:0:0:0:0:0:1
------------------------------------
#######Header Logger Valve被執(zhí)行
00000000000 Header Logger Valve
host:localhost:8080
connection:keep-alive
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Windows NT 10.0; WOW64) ...
accept:text/html,application/xhtml+xml,application/xml;...
accept-encoding:gzip, deflate, br
accept-language:zh-CN,zh;q=0.9,en;q=0.8
------------------------------------

3娶桦、多Wrapper容器處理

在tomcat4中,如果包含多個wrapper容器汁汗,這時需要一個mapper來處理這些wrapper

HttpConnector connector = new HttpConnector();
    // wrapper 1
    Wrapper wrapper1 = new SimpleWrapper();
    wrapper1.setName("Primitive");
    wrapper1.setServletClass("PrimitiveServlet");
    // wrapper 2
    Wrapper wrapper2 = new SimpleWrapper();
    wrapper2.setName("Modern");
    wrapper2.setServletClass("ModernServlet");
    // context
    Context context = new SimpleContext();
    // wrapper add to context
    context.addChild(wrapper1);
    context.addChild(wrapper2);
    // create 2 Valve
    Valve valve1 = new HeaderLoggerValve();
    Valve valve2 = new ClientIPLoggerValve();
    // 閥門加入到了Context容器中
    ((Pipeline) context).addValve(valve1);
    ((Pipeline) context).addValve(valve2);
    // create a mapper
    Mapper mapper = new SimpleContextMapper();
    // set protocol
    mapper.setProtocol("http");
    // add mapper to context
    context.addMapper(mapper);
    Loader loader = new SimpleLoader();
    context.setLoader(loader);
    // context.addServletMapping(pattern, name);
    context.addServletMapping("/Primitive", "Primitive");
    context.addServletMapping("/Modern", "Modern");
    connector.setContainer(context);

Basic Valve:

public void invoke(Request request, Response response, ValveContext valveContext)
    throws IOException, ServletException {
    // Validate the request and response object types
    if (!(request.getRequest() instanceof HttpServletRequest) ||
      !(response.getResponse() instanceof HttpServletResponse)) {
      return;     // NOTE - Not much else we can do generically
    }

    // Disallow any direct access to resources under WEB-INF or META-INF
    HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
    String contextPath = hreq.getContextPath();
    String requestURI = ((HttpRequest) request).getDecodedRequestURI();
    String relativeURI =
      requestURI.substring(contextPath.length()).toUpperCase();

    Context context = (Context) getContainer();
    // Select the Wrapper to be used for this Request
    Wrapper wrapper = null;
    try {
// ##############1衷畦、從context中獲取一個wrapper
      wrapper = (Wrapper) context.map(request, true);
    }
    catch (IllegalArgumentException e) {
      badRequest(requestURI, (HttpServletResponse) response.getResponse());
      return;
    }
    if (wrapper == null) {
      notFound(requestURI, (HttpServletResponse) response.getResponse());
      return;
    }
    // Ask this Wrapper to process this Request
    response.setContext(context);
//  ##############2、調(diào)用wrapper的invoke方法
    wrapper.invoke(request, response);
  }

看一下Context是如何找wrapper的:

public Container map(Request request, boolean update) {
    //this method is taken from the map method in org.apache.cataline.core.ContainerBase
    //the findMapper method always returns the default mapper, if any, regardless the
    //request's protocol
// 根據(jù)協(xié)議來找到對應的Mapper
    Mapper mapper = findMapper(request.getRequest().getProtocol());
    if (mapper == null)
      return (null);
    // Use this Mapper to perform this mapping
// 然后從Mapper中來找到wrapper
    return (mapper.map(request, update));
  }

Mapper查找wrapper過程

public Container map(Request request, boolean update) {
    // Identify the context-relative URI to be mapped
    String contextPath =
      ((HttpServletRequest) request.getRequest()).getContextPath();
    String requestURI = ((HttpRequest) request).getDecodedRequestURI();
    String relativeURI = requestURI.substring(contextPath.length());
    // Apply the standard request URI mapping rules from the specification
    Wrapper wrapper = null;
    String servletPath = relativeURI;
    String pathInfo = null;
// 找到servlet的名稱
    String name = context.findServletMapping(relativeURI);
    if (name != null)
// 根據(jù)名稱來找wrapper知牌,在tomcat中祈争,一個wrapper對應一個servlet
      wrapper = (Wrapper) context.findChild(name);
    return (wrapper);
  }

總的過程就是根據(jù)協(xié)議先找到Mapper,然后再根據(jù)servlet name找到wrapper角寸。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末菩混,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扁藕,更是在濱河造成了極大的恐慌沮峡,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亿柑,死亡現(xiàn)場離奇詭異邢疙,居然都是意外死亡,警方通過查閱死者的電腦和手機望薄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門疟游,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人痕支,你說我怎么就攤上這事乡摹。” “怎么了采转?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵聪廉,是天一觀的道長瞬痘。 經(jīng)常有香客問我,道長板熊,這世上最難降的妖魔是什么框全? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮干签,結(jié)果婚禮上津辩,老公的妹妹穿的比我還像新娘。我一直安慰自己容劳,他們只是感情好喘沿,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著竭贩,像睡著了一般蚜印。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上留量,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天窄赋,我揣著相機與錄音,去河邊找鬼楼熄。 笑死忆绰,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的可岂。 我是一名探鬼主播错敢,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼缕粹!你這毒婦竟也來了伐债?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤致开,失蹤者是張志新(化名)和其女友劉穎峰锁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體双戳,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡虹蒋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了飒货。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魄衅。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖塘辅,靈堂內(nèi)的尸體忽然破棺而出晃虫,到底是詐尸還是另有隱情,我是刑警寧澤扣墩,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布哲银,位于F島的核電站扛吞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏荆责。R本人自食惡果不足惜滥比,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望做院。 院中可真熱鬧盲泛,春花似錦、人聲如沸键耕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屈雄。三九已至村视,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棚亩,已是汗流浹背蓖议。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工虏杰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留讥蟆,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓纺阔,卻偏偏與公主長得像瘸彤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子笛钝,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容