我們已經(jīng)知道,在tomcat中有四種類型的servlet容器,分別為Engine、Host、Context 和Wrapper,本文接下來(lái)對(duì)tomcat中Wrapper接口的標(biāo)準(zhǔn)實(shí)現(xiàn)進(jìn)行說(shuō)明。
對(duì)于每個(gè)引入的HTTP請(qǐng)求,連接器都會(huì)調(diào)用與其關(guān)聯(lián)的servlet容器的invoke()方法;然后,servlet容器會(huì)調(diào)用所有子容器的invoke()方法
這里面的流程通常是servlet容器調(diào)用其管道對(duì)象的invoke()方法,其管道對(duì)象的invoke()方法最后調(diào)用其基礎(chǔ)閥,管道對(duì)象的基礎(chǔ)閥里面會(huì)調(diào)用子容器的invoke()方法;子容器的invoke()方法的調(diào)用序列與之相同。
在tomcat中,servlet類可以實(shí)現(xiàn)javax.servlet.SingleThreadModel接口,這樣的servlet類也稱為SingleThreadModel(STM)servlet類;根據(jù)Servlet規(guī)范,實(shí)現(xiàn)此接口的目的是保證servlet實(shí)例一次只處理一個(gè)請(qǐng)求。
StandardWrapper對(duì)象的主要任務(wù)是載入它所代表的servlet類,并進(jìn)行實(shí)例化;不過StandardWrapper類并不調(diào)用servlet的service方法,該任務(wù)由StandardWrapperValve對(duì)象(StandardWrapper實(shí)例的管道對(duì)象中的基礎(chǔ)閥)完成;StandardWrapperValve對(duì)象通過調(diào)用StandardWrapper實(shí)例的allocate()方法獲取servlet實(shí)例,在獲取實(shí)例后,StandardWrapperValve實(shí)例就會(huì)調(diào)用servlet實(shí)例的service()方法
對(duì)于STM 類型的servlet類與非STM 類型的servlet類,StandardWrapper實(shí)例的載入方式是不一樣的;對(duì)于非STM 類型的servlet類,StandardWrapper實(shí)例只會(huì)載入一次,對(duì)于隨后的請(qǐng)求都會(huì)返回servlet的同一個(gè)實(shí)例,它假設(shè)該servlet類的service()方法在多線程環(huán)境中是線程安全的。
而對(duì)于STM 類型的servlet類,StandardWrapper實(shí)例必須保證每一時(shí)刻只能有一個(gè)線程在執(zhí)行STM servlet類的service()方法;StandardWrapper實(shí)例通過將STM 類型的servlet實(shí)例保存在一個(gè)java.util.Stack類型的棧中
public Servlet allocate() throws ServletException { if (debug >= 1 ) log( "Allocating an instance" ); // If we are currently unloading this servlet, throw an exception if (unloading) throw new ServletException (sm.getString( "standardWrapper.unloading" , getName())); // If not SingleThreadedModel, return the same instance every time if (! singleThreadModel) { // Load and initialize our instance if necessary if (instance == null ) { synchronized ( this ) { if (instance == null ) { try { instance = loadServlet(); } catch (ServletException e) { throw e; } catch (Throwable e) { throw new ServletException (sm.getString( "standardWrapper.allocate" ), e); } } } } if (! singleThreadModel) { if (debug >= 2 ) log( " Returning non-STM instance" ); countAllocated ++ ; return (instance); } } synchronized (instancePool) { while (countAllocated >= nInstances) { // Allocate a new instance if possible, or else wait if (nInstances < maxInstances) { try { instancePool.push(loadServlet()); nInstances ++ ; } catch (ServletException e) { throw e; } catch (Throwable e) { throw new ServletException (sm.getString( "standardWrapper.allocate" ), e); } } else { try { instancePool.wait(); } catch (InterruptedException e) { ; } } } if (debug >= 2 ) log( " Returning allocated STM instance" ); countAllocated ++ ; return (Servlet) instancePool.pop(); } }
而在loadServlet()方法中,首先獲取要加載的servlet的完整類名及類加載器,然后通過類加載器加載該servlet類,然后實(shí)例化該servlet類,最后調(diào)用該servlet實(shí)例的init()方法初始化(傳入javax.servlet.ServletConfig對(duì)象)
public synchronized Servlet loadServlet() throws ServletException { // Nothing to do if we already have an instance or an instance pool if (!singleThreadModel && (instance != null )) return instance; PrintStream out = System.out; SystemLogHandler.startCapture(); Servlet servlet = null ; try { // If this "servlet" is really a JSP file, get the right class. // HOLD YOUR NOSE - this is a kludge that avoids having to do special // case Catalina-specific code in Jasper - it also requires that the // servlet path be replaced by the <jsp-file> element content in // order to be completely effective String actualClass = servletClass; if ((actualClass == null ) && (jspFile != null )) { Wrapper jspWrapper = (Wrapper) ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME); if (jspWrapper != null ) actualClass = jspWrapper.getServletClass(); } // Complain if no servlet class has been specified if (actualClass == null ) { unavailable( null ); throw new ServletException (sm.getString( "standardWrapper.notClass" , getName())); } // Acquire an instance of the class loader to be used Loader loader = getLoader(); if (loader == null ) { unavailable( null ); throw new ServletException (sm.getString( "standardWrapper.missingLoader" , getName())); } ClassLoader classLoader = loader.getClassLoader(); // Special case class loader for a container provided servlet if (isContainerProvidedServlet(actualClass)) { classLoader = this .getClass().getClassLoader(); log(sm.getString ( "standardWrapper.containerServlet" , getName())); } // Load the specified servlet class from the appropriate class loader Class classClass = null ; try { if (classLoader != null ) { System.out.println( "Using classLoader.loadClass" ); classClass = classLoader.loadClass(actualClass); } else { System.out.println( "Using forName" ); classClass = Class.forName(actualClass); } } catch (ClassNotFoundException e) { unavailable( null ); throw new ServletException (sm.getString( "standardWrapper.missingClass" , actualClass), e); } if (classClass == null ) { unavailable( null ); throw new ServletException (sm.getString( "standardWrapper.missingClass" , actualClass)); } // Instantiate and initialize an instance of the servlet class itself try { servlet = (Servlet) classClass.newInstance(); } catch (ClassCastException e) { unavailable( null ); // Restore the context ClassLoader throw new ServletException (sm.getString( "standardWrapper.notServlet" , actualClass), e); } catch (Throwable e) { unavailable( null ); // Restore the context ClassLoader throw new ServletException (sm.getString( "standardWrapper.instantiate" , actualClass), e); } // Check if loading the servlet in this web application should be // allowed if (! isServletAllowed(servlet)) { throw new SecurityException (sm.getString( "standardWrapper.privilegedServlet" , actualClass)); } // Special handling for ContainerServlet instances if ((servlet instanceof ContainerServlet) && isContainerProvidedServlet(actualClass)) { System.out.println( "calling setWrapper" ); ((ContainerServlet) servlet).setWrapper( this ); System.out.println( "after calling setWrapper" ); } // Call the initialization method of this servlet try { instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, servlet); servlet.init(facade); // Invoke jspInit on JSP pages if ((loadOnStartup > 0) && (jspFile != null )) { // Invoking jspInit HttpRequestBase req = new HttpRequestBase(); HttpResponseBase res = new HttpResponseBase(); req.setServletPath(jspFile); req.setQueryString( "jsp_precompile=true" ); servlet.service(req, res); } instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet); } catch (UnavailableException f) { instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet, f); unavailable(f); throw f; } catch (ServletException f) { instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet, f); // If the servlet wanted to be unavailable it would have // said so, so do not call unavailable(null). throw f; } catch (Throwable f) { instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet, f); // If the servlet wanted to be unavailable it would have // said so, so do not call unavailable(null). throw new ServletException (sm.getString( "standardWrapper.initException" , getName()), f); } // Register our newly initialized instance singleThreadModel = servlet instanceof SingleThreadModel; if (singleThreadModel) { if (instancePool == null ) instancePool = new Stack(); } fireContainerEvent( "load", this ); } finally { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0 ) { if (getServletContext() != null ) { getServletContext().log(log); } else { out.println(log); } } } return servlet; }
?StandardWrapper類的loadServlet()方法在載入servlet類后,會(huì)調(diào)用該servlet實(shí)例的init()方法,該方法需要傳入一個(gè)javax.servlet.ServletConfig實(shí)例作為參數(shù);那么,?StandardWrapper對(duì)象是如何獲取ServletConfig對(duì)象的呢
答案就在StandardWrapper類本身,該類不僅實(shí)現(xiàn)了Wrapper接口,還實(shí)現(xiàn)了javax.servlet.ServletConfig接口,下面是相關(guān)方法實(shí)現(xiàn)
/** * Return the initialization parameter value for the specified name, * if any; otherwise return <code>null</code>. * * @param name Name of the initialization parameter to retrieve */ public String getInitParameter(String name) { return (findInitParameter(name)); } /** * Return the set of initialization parameter names defined for this * servlet. If none are defined, an empty Enumeration is returned. */ public Enumeration getInitParameterNames() { synchronized (parameters) { return ( new Enumerator(parameters.keySet())); } } /** * Return the servlet context with which this servlet is associated. */ public ServletContext getServletContext() { if (parent == null ) return ( null ); else if (!(parent instanceof Context)) return ( null ); else return (((Context) parent).getServletContext()); } /** * Return the name of this servlet. */ public String getServletName() { return (getName()); }
StandardWrapper實(shí)例在它的loadServlet()方法里面調(diào)用它所載入的servlet類的實(shí)例的init()方法,該方法需要一個(gè)javax.servlet.ServletConfig類型參數(shù),理論上StandardWrapper對(duì)象可以將自身傳入init()方法,不過為了類型安全,StandardWrapper類將自身實(shí)例包裝成StandardWrapperFacade類的一個(gè)實(shí)例,然后再傳給init()方法
public final class StandardWrapperFacade implements ServletConfig { /** * Create a new facede around a StandardWrapper. */ public StandardWrapperFacade(StandardWrapper config) { super (); this .config = (ServletConfig) config; } /** * Wrapped config. */ private ServletConfig config = null ; public String getServletName() { return config.getServletName(); } public ServletContext getServletContext() { ServletContext theContext = config.getServletContext(); if ((theContext != null ) && (theContext instanceof ApplicationContext)) theContext = ((ApplicationContext) theContext).getFacade(); return (theContext); } public String getInitParameter(String name) { return config.getInitParameter(name); } public Enumeration getInitParameterNames() { return config.getInitParameterNames(); } }
StandardWrapperValve類是StandardWrapper實(shí)例中的基礎(chǔ)閥,要完成兩個(gè)操作
(1)執(zhí)行與該servlet相關(guān)聯(lián)的全部過濾器
(2)調(diào)用servlet實(shí)例的service()方法
public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { // Initialize local variables we may need boolean unavailable = false ; Throwable throwable = null ; StandardWrapper wrapper = (StandardWrapper) 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; // Check for the application being marked unavailable if (! ((Context) wrapper.getParent()).getAvailable()) { hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString( "standardContext.isUnavailable" )); unavailable = true ; } // Check for the servlet being marked unavailable if (!unavailable && wrapper.isUnavailable()) { log(sm.getString( "standardWrapper.isUnavailable" , wrapper.getName())); if (hres == null ) { ; // NOTE - Not much we can do generically } else { long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) hres.setDateHeader( "Retry-After" , available); hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString( "standardWrapper.isUnavailable" , wrapper.getName())); } unavailable = true ; } // Allocate a servlet instance to process this request try { if (! unavailable) { servlet = wrapper.allocate(); } } catch (ServletException e) { log(sm.getString( "standardWrapper.allocateException" , wrapper.getName()), e); throwable = e; exception(request, response, e); servlet = null ; } catch (Throwable e) { log(sm.getString( "standardWrapper.allocateException" , wrapper.getName()), e); throwable = e; exception(request, response, e); servlet = null ; } // Acknowlege the request try { response.sendAcknowledgement(); } catch (IOException e) { sreq.removeAttribute(Globals.JSP_FILE_ATTR); log(sm.getString( "standardWrapper.acknowledgeException" , wrapper.getName()), e); throwable = e; exception(request, response, e); } catch (Throwable e) { log(sm.getString( "standardWrapper.acknowledgeException" , wrapper.getName()), e); throwable = e; exception(request, response, e); servlet = null ; } // Create the filter chain for this request ApplicationFilterChain filterChain = createFilterChain(request, servlet); // Call the filter chain for this request // NOTE: This also calls the servlet's service() method try { String jspFile = wrapper.getJspFile(); if (jspFile != null ) sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile); else sreq.removeAttribute(Globals.JSP_FILE_ATTR); if ((servlet != null ) && (filterChain != null )) { filterChain.doFilter(sreq, sres); } sreq.removeAttribute(Globals.JSP_FILE_ATTR); } catch (IOException e) { sreq.removeAttribute(Globals.JSP_FILE_ATTR); log(sm.getString( "standardWrapper.serviceException" , wrapper.getName()), e); throwable = e; exception(request, response, e); } catch (UnavailableException e) { sreq.removeAttribute(Globals.JSP_FILE_ATTR); log(sm.getString( "standardWrapper.serviceException" , wrapper.getName()), e); // throwable = e; // exception(request, response, e); wrapper.unavailable(e); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) hres.setDateHeader( "Retry-After" , available); hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString( "standardWrapper.isUnavailable" , wrapper.getName())); // Do not save exception in 'throwable', because we // do not want to do exception(request, response, e) processing } catch (ServletException e) { sreq.removeAttribute(Globals.JSP_FILE_ATTR); log(sm.getString( "standardWrapper.serviceException" , wrapper.getName()), e); throwable = e; exception(request, response, e); } catch (Throwable e) { sreq.removeAttribute(Globals.JSP_FILE_ATTR); log(sm.getString( "standardWrapper.serviceException" , wrapper.getName()), e); throwable = e; exception(request, response, e); } // Release the filter chain (if any) for this request try { if (filterChain != null ) filterChain.release(); } catch (Throwable e) { log(sm.getString( "standardWrapper.releaseFilters" , wrapper.getName()), e); if (throwable == null ) { throwable = e; exception(request, response, e); } } // Deallocate the allocated servlet instance try { if (servlet != null ) { wrapper.deallocate(servlet); } } catch (Throwable e) { log(sm.getString( "standardWrapper.deallocateException" , wrapper.getName()), e); if (throwable == null ) { throwable = e; exception(request, response, e); } } // If this servlet has been marked permanently unavailable, // unload it and release this instance try { if ((servlet != null ) && (wrapper.getAvailable() == Long.MAX_VALUE)) { wrapper.unload(); } } catch (Throwable e) { log(sm.getString( "standardWrapper.unloadException" , wrapper.getName()), e); if (throwable == null ) { throwable = e; exception(request, response, e); } } }
上述創(chuàng)建過濾器鏈的過程本人后來(lái)作了補(bǔ)充: How Tomcat Works(十四)補(bǔ)充
---------------------------------------------------------------------------?
本系列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)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
