Tomcat學習筆記(十)
StandardWrapper容器
Context容器包含一個或者多個Wrapper實例,每個Wrapper實例表示一個具體的servlet定義。
方法調用序列
具體過程
(1)連接器創建request和response對象
(2)連接器調用StandardContext實例的invoke()方法
(3)接著,StandardContext實例的invoke方法調用其管道對象的invoke方法。StandardContext中管道對象的基礎閥是StandContextValve類的實例,因此,StandardContext的管道對象會調用StandardContextValve實例的invoke方法。
(4)StandardContextValve實例的invoke方法獲取相應的Wrapper實例處理HTTP請求,調用Wrapper實例的invoke的方法。
(5)StandardWrapper類是Wrapper接口的標準實現,StandardWrapper實例的invoke方法會調用其管道對象的invoke方法。
(6)StandardWrapper流水線的基本閥門時StandardWrapperValve。因此StandardWrapperValve的invoke方法會被調用。StandardWrapperValve的invoke方法會調用包裝器的allocate方法獲得一個servlet的實例。
(7)當一個servlet需要被加載的時候,方法allocate調用方法load來加載一個servlet。
(8)方法load會調用servlet的init方法。
(9)StandardWrapperValve調用servlet實例的service方法。
1. StandardWrapper對象主要任務加載servlet類,並進行實例。但是StandardWrapper並不調用servlet的service方法。StandardWrapperValve對象通過調用allocate()方法從StandardWrapper實例中獲取servlet實例。在獲取得了servlet實例後,StandardWrapperValve實例就會調用servlet實例的service方法。
粗略流程如下:
分配Servlet實例
StandardWrapperValve實例的invoke方法調用Wrapper實例的allocate方法獲取請求的servlet的一個實例,因此StandardWrapper類的實現allocate方法。
StandardWrapperValve#invoke
public final void invoke(Request request, Response response) throws IOException, ServletException { //...省略 // Allocate a servlet instance to process this request try { if (!unavailable) { servlet = wrapper.allocate(); } }catch (UnavailableException e) { //...省略
StandardWrapper#allocate
/** * Stack containing the STM instances. */ protected Stack<Servlet> instancePool = null; public Servlet allocate() throws ServletException { // If we are currently unloading this servlet, throw an exception if (unloading) throw new ServletException (sm.getString("standardWrapper.unloading", getName())); boolean newInstance = false; // 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 { if (log.isDebugEnabled()) log.debug("Allocating non-STM instance"); instance = loadServlet();//加載servlet實例 if (!singleThreadModel) { // For non-STM, increment here to prevent a race // condition with unload. Bug 43683, test case // #3 newInstance = true; //分配計數 countAllocated.incrementAndGet(); } } catch (ServletException e) { throw e; } catch (Throwable e) { ExceptionUtils.handleThrowable(e); throw new ServletException (sm.getString("standardWrapper.allocate"), e); } } } } if (!instanceInitialized) { initServlet(instance);//初始化servlet->servlet.init() } if (singleThreadModel) { if (newInstance) { // Have to do this outside of the sync above to prevent a // possible deadlock synchronized (instancePool) { instancePool.push(instance); nInstances++; } } } else { if (log.isTraceEnabled()) log.trace(" Returning non-STM instance"); // For new instances, count will have been incremented at the // time of creation if (!newInstance) { countAllocated.incrementAndGet(); } return (instance); } } synchronized (instancePool) { while (countAllocated.get() >= 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) { ExceptionUtils.handleThrowable(e); throw new ServletException (sm.getString("standardWrapper.allocate"), e); } } else { try { instancePool.wait(); } catch (InterruptedException e) { // Ignore } } } if (log.isTraceEnabled()) log.trace(" Returning allocated STM instance"); countAllocated.incrementAndGet(); return instancePool.pop();//彈出棧頂servlet } }
加載Servlet
在StandardWrapper#loadServlet()方法,先檢查是否已經加載過servlet
if (!singleThreadModel && (instance != null)) return instance;
StandardContext獲取實例管理器,並通過實例管理器獲取servlet實例
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
servlet = (Servlet) instanceManager.newInstance(servletClass);
初始化servlet,並觸發容器加載監聽事件。
initServlet(servlet); fireContainerEvent("load", this);
最後,返回servlet
return servlet;
2.ServletConfig類
在servlet調用init方法,要傳入ServletConfig參數,ServletConfig參數的來源,由於StandardWrapper不僅實現了Wrapper接口,還實現了javax.servlet.ServletConfig接口,在ServletConfig接口中的方法如下:
public interface ServletConfig { public String getServletName();//返回servlet名字 public ServletContext getServletContext();//返回servlet容器 public String getInitParameter(String name);//返回初始化參數 public Enumeration<String> getInitParameterNames(); }
StandardWrapper類並不是將自身傳遞給servlet#init方法,它會在一個StandardWrapperFacade實例中包裝自生,將其大部分的公共方法對servlet程序員隱藏。
/** * The facade associated with this wrapper. */ protected StandardWrapperFacade facade = new StandardWrapperFacade(this);
在StandardWrapper中方法實現
public ServletContext getServletContext() { if (parent == null) return (null); else if (!(parent instanceof Context)) return (null); else return (((Context) parent).getServletContext()); } protected HashMap<String, String> parameters = new HashMap<String, String>(); /** * Return the name of this servlet. */ @Override public String getServletName() { return (getName()); } 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. */ @Override public Enumeration<String> getInitParameterNames() { try { parametersLock.readLock().lock(); return Collections.enumeration(parameters.keySet()); } finally { parametersLock.readLock().unlock(); } } //初始化參數 public void addInitParameter(String name, String value) { try { parametersLock.writeLock().lock(); parameters.put(name, value); } finally { parametersLock.writeLock().unlock(); } fireContainerEvent("addInitParameter", name); }
3.Servlet容器的父子關系。
Wrapper實例代表一個servlet實例,因此Wrapper實例不在有子容器,不應該在調用其addChild方法,否則拋出IllegalStateException異常。下面是addChild方法。
public void addChild(Container child) { throw new IllegalStateException (sm.getString("standardWrapper.notChild")); }
Wrapper的父容器只能是Context類的實現,若是在調用Wrapper實例的setParent方法時,傳入了一個非Context類型的容器,則會拋出IllegalStateException異常。
public void setParent(Container container) { if ((container != null) && !(container instanceof Context)) throw new IllegalArgumentException (sm.getString("standardWrapper.notContext")); if (container instanceof StandardContext) { swallowOutput = ((StandardContext)container).getSwallowOutput(); unloadDelay = ((StandardContext)container).getUnloadDelay(); } super.setParent(container); }
4.StandardWrapperFacade類
StandardWrapper實例會調用它所載入的servlet類的實例的init()方法。init()方法需要一個javax.servlet.ServletConfig實例,而StandardWrapper類本身實現了javax.servlet.ServletConfig接口,所以,理論上StandardWrapper對象可以將自己傳入init()方法。但是StandardWrapper需要將其大部分公共方法對servlet程序員隱藏起來。為了實現在這個目的,StandWrapper類將自身包裝成StandardWrapperFacade類的一個實例。關系如下。
(1)在StandardWrapper類中,將自身包裝成StandardWrapperFacade。
/** * The facade associated with this wrapper. */ protected StandardWrapperFacade facade = new StandardWrapperFacade(this);
(2)StandardWrapperFacade中的構造方法。
public StandardWrapperFacade(StandardWrapper config) { super(); this.config = config; } //通過包裝類型調用方法 public String getInitParameter(String name) { return config.getInitParameter(name); } public ServletContext getServletContext() { if (context == null) { context = config.getServletContext(); if ((context != null) && (context instanceof ApplicationContext)) context = ((ApplicationContext) context).getFacade(); } return (context); } public String getServletName() { return config.getServletName(); }
這裏采用裝飾模式。
5.StandardWrapperValve類
StandardWrapperValve是StandardWrapper中的基礎閥門,主要完成2個操作。
(1)執行與該servlet實例關聯的全部過濾器,
(2)調用servlet實例的service方法。
在StandardWrapperValve中invoke方法執行的步驟。
(1)調用StandardWrapper實例的allocate方法獲取該StandardWrapper實例所表示的servlet的實例;
(2)調用私有方法createFilterChain,創建過濾器鏈;
(3)調用過濾鏈的doFilter方法,其中包括調用servlet實例的service方法;
(4)釋放過濾鏈;
(5)調用Wrapper實例的deallocate方法;
(6)若該servlet類再也不會用到,將會調用Wrapper實例的unload方法。
6.ApplicationFilterChain類
ApplicationFilterChain類實現了FilterChain接口,StandardWrapperValve類的invoke方法會創建ApplicationFilterChain類的一個實例,並調用其doFilter方法,doFilter方法會調用第一個過濾器的doFilter方法。直到最後一個過濾器,則會調用被請求的servlet類的service方法。
總結:
通過StandWrapper的學習,大致的了解了Servlet的從加載,初始化,service方法,到卸載的整個流程。另外還有就是單例模式,監聽事件,裝飾模式的設計思想的了解。
Tomcat學習筆記(十)