Tomcat學習之Wrapper

Wrapper 代表一個 Servlet,它負責管理一個 Servlet,包括的 Servlet 的裝載、初始化、執行以及資源回收。它的父容器一般是Context,Wrapper 是最底層的容器,它沒有子容器了,所以調用它的 addChild 將會拋illegalargumentexception。Wrapper 的實現類是 StandardWrapper,StandardWrapper 還實現了擁有一個 Servlet 初始化信息的 ServletConfig,由此看出 StandardWrapper 將直接和 Servlet 的各種信息打交道。

在StandardContext啓動時,讀取web.xml配置文件,配置Context之後,緊接着啓動Context的一些附屬組件,除此以外還加載了那些標記爲"load on start"的那些wrapper

            // Load and initialize all "load on startup" servlets
            if (ok) {
                loadOnStartup(findChildren());
            }
 public void loadOnStartup(Container children[]) {

        // Collect "load on startup" servlets that need to be initialized
        TreeMap<Integer, ArrayList<Wrapper>> map =
            new TreeMap<Integer, ArrayList<Wrapper>>();
        for (int i = 0; i < children.length; i++) {
            Wrapper wrapper = (Wrapper) children[i];
            int loadOnStartup = wrapper.getLoadOnStartup();
            if (loadOnStartup < 0)
                continue;
            Integer key = Integer.valueOf(loadOnStartup);
            ArrayList<Wrapper> list = map.get(key);
            if (list == null) {
                list = new ArrayList<Wrapper>();
                map.put(key, list);
            }
            list.add(wrapper);
        }

        // Load the collected "load on startup" servlets
        for (ArrayList<Wrapper> list : map.values()) {
            for (Wrapper wrapper : list) {
                try {
                    wrapper.load();
                } catch (ServletException e) {
                    getLogger().error(sm.getString("standardWrapper.loadException",
                                      getName()), StandardWrapper.getRootCause(e));
                    // NOTE: load errors (including a servlet that throws
                    // UnavailableException from tht init() method) are NOT
                    // fatal to application startup
                }
            }
        }

    }

這個方法做了兩件事:

1、遍歷這些wrapper,將其放入一個map中。key爲啓動順序,value是同一啓動順序的servlet list,爲了保護數字小的先啓動,這裏用了treemap這種數據結構來存儲;

2、遍歷這個map,依次加載對應list中的各個wrapper。由於採用的是arrayList,所以相同"load on start"值靠前的先加載

下面來看看StandardWrapper的load方法,直接調用了loadServlet方法來初始化
 public synchronized void load() throws ServletException {
        instance = loadServlet();
        
        if (!instanceInitialized) {
            initServlet(instance);
        }

        if (isJspServlet) {
            StringBuilder oname =
                new StringBuilder(MBeanUtils.getDomain(getParent()));
            
            oname.append(":type=JspMonitor,name=");
            oname.append(getName());
            
            oname.append(getWebModuleKeyProperties());
            
            try {
                jspMonitorON = new ObjectName(oname.toString());
                Registry.getRegistry(null, null)
                    .registerComponent(instance, jspMonitorON, null);
            } catch( Exception ex ) {
                log.info("Error registering JSP monitoring with jmx " +
                         instance);
            }
        }
    }
    private synchronized void initServlet(Servlet servlet)throws ServletException {
        if (instanceInitialized && !singleThreadModel) return;
        // Call the initialization method of this servlet
        try {
          	instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, servlet);
			if (Globals.IS_SECURITY_ENABLED) {
				Object[] args = new Object[] { (facade) };
				SecurityUtil.doAsPrivilege("init", servlet, classType, args);
				args = null;
			} else {
				servlet.init(facade);
			}

			instanceInitialized = true;
			instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet);
        } catch (UnavailableException f) {
			//handle exception
        }
    }
實際上是調用了servlet的init方法,這已經是servlet的代碼了,這裏不再分析。

前面提到的servlet的加載只是被標識爲“load on start”的那些servlet,那麼其他servlet是在什麼時候被加載的呢?選中StandardWrapper的initServlet方法,在eclipse中查看其調用層次如下:

會發現有個allocate方法間接調用了它,這裏給出請求進入wrapper之後的方法調用時序圖:

可以看出在請求進入wrapper之後,通過allocate方法從實例池棧中彈出一個servlet實例來處理這個請求,servlet實例被封裝成filterChain對象,緊接着通過一系列的過濾器過濾到達servlet.service()方法,這是singleThreadModel模式的做法。在非singleThreadModel模式的情況下首次加載並初始始化servlet賦給instance字段,下次直接從這個字段中獲取servlet實例,因此在非singleThreadModel模式下每次返回的是同一個servlet實例。有關singleThreadModel的具體介紹參考:http://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/SingleThreadModel.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章