標籤:
Tomcat學習之Wrapper分類: WEB伺服器2012-08-30 22:16 1547人閱讀 評論(0) 收藏 舉報tomcatservletwrapperservletslistexception
Wrapper 代表一個 Servlet,它負責管理一個 Servlet,包括的 Servlet 的裝載、初始化、執行以及資源回收。它的父容器一般是Context,Wrapper 是最底層的容器,它沒有子容器了,所以調用它的 addChild 將會拋illegalargumentexception。Wrapper 的實作類別是 StandardWrapper,StandardWrapper 還實現了擁有一個 Servlet 初始化資訊的 ServletConfig,由此看出 StandardWrapper 將直接和 Servlet 的各種資訊打交道。
在StandardContext啟動時,讀取web.xml設定檔,配置Context之後,緊接著啟動Context的一些附屬組件,除此以外還載入了那些標記為"load on start"的那些wrapper
[java] view plaincopyprint?
- // Load and initialize all "load on startup" servlets
- if (ok) {
- loadOnStartup(findChildren());
- }
[java] view plaincopyprint?
- 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方法來初始化
[java] view plaincopyprint?
- 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);
- }
- }
- }
[java] view plaincopyprint?
- 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
Tomcat學習之Wrapper