本文接下來會(huì)介紹Host容器和Engine容器,在tomcat的實(shí)際部署中,總是會(huì)使用一個(gè)Host容器;本文介紹Host接口和Engine接口及其相關(guān)類
Host容器是org.apache.catalina.Host接口的實(shí)例,Host接口繼承自Container接口, 其定義如下
public interface Host extends Container { public static final String ADD_ALIAS_EVENT = "addAlias" ; public static final String REMOVE_ALIAS_EVENT = "removeAlias" ; public String getAppBase(); public void setAppBase(String appBase); public boolean getAutoDeploy(); public void setAutoDeploy( boolean autoDeploy); public void addDefaultContext(DefaultContext defaultContext); public DefaultContext getDefaultContext(); public String getName(); public void setName(String name); public void importDefaultContext(Context context); public void addAlias(String alias); public String[] findAliases(); public Context map(String uri); public void removeAlias(String alias); }
該接口中比較重要的方法是map()方法,該方法返回一個(gè)用來處理引入的HTTP請(qǐng)求的Context容器的實(shí)例,該方法的具體實(shí)現(xiàn)在StandardHost類中
在tomcat中org.apache.catalina.core.StandardHost類是Host接口的標(biāo)準(zhǔn)實(shí)現(xiàn),該類繼承自org.apache.catalina.core.ContainerBase類,實(shí)現(xiàn)了Host和Deployer接口
與StandardContext類和StandardWrapper類相似,StandardHost類的構(gòu)造函數(shù)會(huì)將一個(gè)基礎(chǔ)閥的實(shí)例添加到其管道對(duì)象中:
public StandardHost() { super (); pipeline.setBasic( new StandardHostValve()); }
基礎(chǔ)閥是org.apache.catalina.core.StandardHostValve類的實(shí)例
當(dāng)調(diào)用其start()方法時(shí),StandardHost實(shí)例會(huì)添加兩個(gè)閥,分別為ErrorReportValve類和ErrorDispatcherValve類的實(shí)例,這兩個(gè)閥均位于org.apache.catalina.valves包下
public synchronized void start() throws LifecycleException { // Set error report valve if ((errorReportValveClass != null ) && (!errorReportValveClass.equals("" ))) { try { Valve valve = (Valve) Class.forName(errorReportValveClass) .newInstance(); addValve(valve); } catch (Throwable t) { log(sm.getString ( "standardHost.invalidErrorReportValveClass" , errorReportValveClass)); } } // Set dispatcher valve addValve( new ErrorDispatcherValve()); super .start(); }
每當(dāng)引入一個(gè)HTTP請(qǐng)求,都會(huì)調(diào)用Host實(shí)例的invoke()方法,這里是StandardHost的父類ContainerBase類的invoke()方法,而ContainerBase類的invoke()方法會(huì)調(diào)用StandardHost實(shí)例的基礎(chǔ)閥StandardHostValve實(shí)例的invoke()方法;StandardHostValve類的invoke()方法會(huì)調(diào)用StandardHost類的map()方法來獲取相應(yīng)的Context實(shí)例來處理HTTP請(qǐng)求
public Context map(String uri) { if (debug > 0 ) log( "Mapping request URI '" + uri + "'" ); if (uri == null ) return ( null ); // Match on the longest possible context path prefix if (debug > 1 ) log( " Trying the longest context path prefix" ); Context context = null ; String mapuri = uri; while ( true ) { context = (Context) findChild(mapuri); if (context != null ) break ; int slash = mapuri.lastIndexOf('/' ); if (slash < 0 ) break ; mapuri = mapuri.substring(0 , slash); } // If no Context matches, select the default Context if (context == null ) { if (debug > 1 ) log( " Trying the default context" ); context = (Context) findChild("" ); } // Complain if no Context has been selected if (context == null ) { log(sm.getString( "standardHost.mappingError" , uri)); return ( null ); } // Return the mapped Context (if any) if (debug > 0 ) log( " Mapped to context '" + context.getPath() + "'" ); return (context); }
在tomcat4中, StandardHost的父類ContainerBase類會(huì)調(diào)用其addDefaultMapper()方法創(chuàng)建一個(gè)默認(rèn)映射器,默認(rèn)映射器的類型由mapperClass屬性的值決定
protected void addDefaultMapper(String mapperClass) { // Do we need a default Mapper? if (mapperClass == null ) return ; if (mappers.size() >= 1 ) return ; // Instantiate and add a default Mapper try { Class clazz = Class.forName(mapperClass); Mapper mapper = (Mapper) clazz.newInstance(); mapper.setProtocol( "http" ); addMapper(mapper); } catch (Exception e) { log(sm.getString( "containerBase.addDefaultMapper" , mapperClass), e); } }
變量mapperClass的值定義在StandardHst類中
private String mapperClass =
??????? "org.apache.catalina.core.StandardHostMapper";
在tomcat4中,StandardHost類的start()方法會(huì)在方法末尾調(diào)用addDefaultMapper()方法,確保默認(rèn)映射器的創(chuàng)建完成
當(dāng)然,StandardHostMapper類中最重要的方法是map()方法,下面是map()方法的實(shí)現(xiàn)
public Container map(Request request, boolean update) { // Has this request already been mapped? if (update && (request.getContext() != null )) return (request.getContext()); // Perform mapping on our request URI String uri = ((HttpRequest) request).getDecodedRequestURI(); Context context = host.map(uri); // Update the request (if requested) and return the selected Context if (update) { request.setContext(context); if (context != null ) ((HttpRequest) request).setContextPath(context.getPath()); else ((HttpRequest) request).setContextPath( null ); } return (context); }
注意,這里map()方法僅僅是簡單地調(diào)用了Host實(shí)例的map()方法
org.apache.catalina.core.StandardHostValve類是StandardHost實(shí)例的基礎(chǔ)閥,當(dāng)有引入的HTTP請(qǐng)求時(shí),會(huì)調(diào)用StandardHostValve類的invoke()方法對(duì)其進(jìn)行處理
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 } // Select the Context to be used for this Request StandardHost host = (StandardHost) getContainer(); Context context = (Context) host.map(request, true ); if (context == null ) { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString( "standardHost.noContext" )); return ; } // Bind the context CL to the current thread Thread.currentThread().setContextClassLoader (context.getLoader().getClassLoader()); // Update the session last access time for our session (if any) HttpServletRequest hreq = (HttpServletRequest) request.getRequest(); String sessionId = hreq.getRequestedSessionId(); if (sessionId != null ) { Manager manager = context.getManager(); if (manager != null ) { Session session = manager.findSession(sessionId); if ((session != null ) && session.isValid()) session.access(); } } // Ask this Context to process this request context.invoke(request, response); }
在tomcat4中,invoke()方法會(huì)調(diào)用StandardHost實(shí)例的map()方法獲取一個(gè)相應(yīng)的Context實(shí)例;然后獲取與該request對(duì)象相關(guān)聯(lián)的session對(duì)象,并調(diào)用其access()方法,access()方法會(huì)修改session對(duì)象的最后訪問時(shí)間;最后調(diào)用Context實(shí)例的invoke()來處理HTTP請(qǐng)求
接下來描述Engine容器,Engine容器是org.apache.catalina.Engine接口的實(shí)例,Engine容器也就是Tomcat的servlet引擎
public interface Engine extends Container { public String getDefaultHost(); public void setDefaultHost(String defaultHost); public String getJvmRoute(); public void setJvmRoute(String jvmRouteId); public Service getService(); public void setService(Service service); public void addDefaultContext(DefaultContext defaultContext); public DefaultContext getDefaultContext(); public void importDefaultContext(Context context); }
在Engine容器中,可以設(shè)置一個(gè)默認(rèn)的Host容器或一個(gè)默認(rèn)的Context容器,注意,Engine容器可以與一個(gè)服務(wù)實(shí)例相關(guān)聯(lián)
org.apache.catalina.core.StandardEngine類是Engine接口的標(biāo)準(zhǔn)實(shí)現(xiàn),在實(shí)例化的時(shí)候,StandardEngine類會(huì)添加一個(gè)基礎(chǔ)閥
public StandardEngine() { super (); pipeline.setBasic( new StandardEngineValve()); }
org.apache.catalina.core.StandardEngineValve類是StandardEngine容器的基礎(chǔ)閥,下面是它的invoke()方法的實(shí)現(xiàn)代碼
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 } // Validate that any HTTP/1.1 request included a host header HttpServletRequest hrequest = (HttpServletRequest) request; if ("HTTP/1.1".equals(hrequest.getProtocol()) && (hrequest.getServerName() == null )) { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_BAD_REQUEST, sm.getString( "standardEngine.noHostHeader" , request.getRequest().getServerName())); return ; } // Select the Host to be used for this Request StandardEngine engine = (StandardEngine) getContainer(); Host host = (Host) engine.map(request, true ); if (host == null ) { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_BAD_REQUEST, sm.getString( "standardEngine.noHost" , request.getRequest().getServerName())); return ; } // Ask this Host to process this request host.invoke(request, response); }
在驗(yàn)證了request和response對(duì)象的類型后,invoke()方法會(huì)通過調(diào)用Engine實(shí)例的map()方法獲取Host對(duì)象;得到Host對(duì)象以后,調(diào)用其invoke()方法處理請(qǐng)求
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創(chuàng)?
轉(zhuǎn)載請(qǐng)注明出處 博客園 刺猬的溫馴?
本人郵箱: ? chenying998179 # 163.com ( #改為@ )
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
