1. 程式人生 > >tomcat 類載入機制 —— ClassLoader

tomcat 類載入機制 —— ClassLoader

tomcat 為了做到每個host中都能載入各種不同的WEB應用而不相互影響,採用的類載入機制有所特別:


載入WEB應用中我們自己寫的類的順序也是按照圖中 標示的1243順序所示。 

 

把WebAppClassLoader.java的loadClass方法貼出來瞧瞧:

Java程式碼  收藏程式碼
  1. publicsynchronized Class loadClass(String name, boolean resolve)  
  2.        throws ClassNotFoundException {  
  3.        if (log.isDebugEnabled())  
  4.            log.debug("loadClass("
     + name + ", " + resolve + ")");  
  5.        Class clazz = null;  
  6.        // Log access to stopped classloader
  7.        if (!started) {  
  8.            try {  
  9.                thrownew IllegalStateException();  
  10.            } catch (IllegalStateException e) {  
  11.                log.info(sm.getString("webappClassLoader.stopped"
    , name), e);  
  12.            }  
  13.        }  
  14.        // (0) Check our previously loaded local class cache.從先前已經載入的本地 cache中查詢該類
  15.        clazz = findLoadedClass0(name);  
  16.        if (clazz != null) {  
  17.            if (log.isDebugEnabled())  
  18.                log.debug("  Returning class from cache");  
  19.            if (resolve)  
  20.                resolveClass(clazz);  
  21.            return (clazz);  
  22.        }  
  23.        // (0.1) Check our previously loaded class cache .從先前已經載入cache中查詢該類
  24.        clazz = findLoadedClass(name);  
  25.        if (clazz != null) {  
  26.            if (log.isDebugEnabled())  
  27.                log.debug("  Returning class from cache");  
  28.            if (resolve)  
  29.                resolveClass(clazz);  
  30.            return (clazz);  
  31.        }  
  32.        // (0.2) Try loading the class with the system class loader, to prevent
  33.        //       the webapp from overriding J2SE classes
  34.        // 嘗試從system class loader中載入,保護j2se 的核心類。
  35.        try {  
  36.            clazz = system.loadClass(name);  
  37.            if (clazz != null) {  
  38.                if (resolve)  
  39.                    resolveClass(clazz);  
  40.                return (clazz);  
  41.            }  
  42.        } catch (ClassNotFoundException e) {  
  43.            // Ignore
  44.        }  
  45.        // (0.5) Permission to access this class when using a SecurityManager
  46.        if (securityManager != null) {  
  47.            int i = name.lastIndexOf('.');  
  48.            if (i >= 0) {  
  49.                try {  
  50.                    securityManager.checkPackageAccess(name.substring(0,i));  
  51.                } catch (SecurityException se) {  
  52.                    String error = "Security Violation, attempt to use " +  
  53.                        "Restricted Class: " + name;  
  54.                    log.info(error, se);  
  55.                    thrownew ClassNotFoundException(error, se);  
  56.                }  
  57.            }  
  58.        }  
  59.        boolean delegateLoad = delegate || filter(name);  
  60.        // (1) Delegate to our parent if requested
  61.        // 如果delegate被設定為true的話委託給雙親載入
  62.        if (delegateLoad) {  
  63.            if (log.isDebugEnabled())  
  64.                log.debug("  Delegating to parent classloader1 " + parent);  
  65.            ClassLoader loader = parent;  
  66.            if (loader == null)  
  67.                loader = system;  
  68.            try {  
  69.                clazz = loader.loadClass(name);  
  70.                if (clazz != null) {  
  71.                    if (log.isDebugEnabled())  
  72.                        log.debug("  Loading class from parent");  
  73.                    if (resolve)  
  74.                        resolveClass(clazz);  
  75.                    return (clazz);  
  76.                }  
  77.            } catch (ClassNotFoundException e) {  
  78.                ;  
  79.            }  
  80.        }  
  81.        // (2)從WEB-INF/lib  ,WEB-INF/classes載入類
  82.        if (log.isDebugEnabled())  
  83.            log.debug("  Searching local repositories");  
  84.        try {  
  85.            clazz = findClass(name);  
  86.            if (clazz != null) {  
  87.                if (log.isDebugEnabled())  
  88.                    log.debug("  Loading class from local repository");  
  89.                if (resolve)  
  90.                    resolveClass(clazz);  
  91.                return (clazz);  
  92.            }  
  93.        } catch (ClassNotFoundException e) {  
  94.            ;  
  95.        }  
  96.        // (3) Delegate to parent unconditionally
  97.        // 如果本地找不到需要載入的類,則還是委託雙親去載入該類
  98.        if (!delegateLoad) {  
  99.            if (log.isDebugEnabled())  
  100.                log.debug("  Delegating to parent classloader at end: " + parent);  
  101.            ClassLoader loader = parent;  
  102.            if (loader == null)  
  103.                loader = system;  
  104.            try {  
  105.                clazz = loader.loadClass(name);  
  106.                if (clazz != null) {  
  107.                    if (log.isDebugEnabled())  
  108.                        log.debug("  Loading class from parent");  
  109.                    if (resolve)  
  110.                        resolveClass(clazz);  
  111.                    return (clazz);  
  112.                }  
  113.            } catch (ClassNotFoundException e) {  
  114.                ;  
  115.            }  
  116.        }  
  117.        thrownew ClassNotFoundException(name);  
  118.    }  

 那麼在哪裡用到了WebAppClassLoader呢,見下面的程式碼,程式碼貼的比較長。但是比較重點的:

 StandardWrapper.java

    /**
     * Load and initialize an instance of this servlet, if there is not already at least one initialized instance. This
     * can be used, for example, to load servlets that are marked in the deployment descriptor to be loaded at server
     * startup time.
     */
    public synchronized Servlet loadServlet() throws ServletException {

        // Nothing to do if we already have an instance or an instance pool
        // 初始化servlet的時候,如果發現已經初始化完畢,則不再初始化
        if (!singleThreadModel && (instance != null)) return instance;

        // 把標準輸出重定向
        PrintStream out = System.out;
        if (swallowOutput) {
            SystemLogHandler.startCapture();
        }

        Servlet servlet;
        try {
            long t1 = System.currentTimeMillis();
            // 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
            /**
             * <pre>
             * 以下程式碼為了使類似這樣的配置
             * <servlet> 
             *     <servlet-name>Test</servlet-name> 
             *     <jsp-file>/TestPage.jsp</jsp-file>
             * </servlet> 
             * 其中<jsp-file>是用來代替<servlet-class>的
             * </pre>
             */
            String actualClass = servletClass;
            if ((actualClass == null) && (jspFile != null)) {
                Wrapper jspWrapper = (Wrapper) ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
                if (jspWrapper != null) {
                    actualClass = jspWrapper.getServletClass();
                    // Merge init parameters
                    String paramNames[] = jspWrapper.findInitParameters();
                    for (int i = 0; i < paramNames.length; i++) {
                        if (parameters.get(paramNames[i]) == null) {
                            parameters.put(paramNames[i], jspWrapper.findInitParameter(paramNames[i]));
                        }
                    }
                }
            }

            // Complain if no servlet class has been specified
            // 如果配置檔案中<servlet-class>沒有指定的話,丟擲抱怨
            if (actualClass == null) {
                unavailable(null);
                throw new ServletException(sm.getString("standardWrapper.notClass", getName()));
            }

            // Acquire an instance of the class loader to be used
            // 獲取一個CLASS LOADER來搞,一般獲得的是WebAppClassLoader
            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) && !((Context) getParent()).getPrivileged()) {
                // If it is a priviledged context - using its own
                // class loader will work, since it's a child of the container
                // loader
                classLoader = this.getClass().getClassLoader();
            }

            // Load the specified servlet class from the appropriate class loader
            Class classClass = null;
            try {
                if (SecurityUtil.isPackageProtectionEnabled()) {
                    final ClassLoader fclassLoader = classLoader;
                    final String factualClass = actualClass;
                    try {
                        classClass = (Class) AccessController.doPrivileged(new PrivilegedExceptionAction() {

                            public Object run() throws Exception {
                                if (fclassLoader != null) {
                                    return fclassLoader.loadClass(factualClass);
                                } else {
                                    return Class.forName(factualClass);
                                }
                            }
                        });
                    } catch (PrivilegedActionException pax) {
                        Exception ex = pax.getException();
                        if (ex instanceof ClassNotFoundException) {
                            throw (ClassNotFoundException) ex;
                        } else {
                            getServletContext().log("Error loading " + fclassLoader + " " + factualClass, ex);
                        }
                    }
                } else {
                    if (classLoader != null) {
                        // 使用WebAppClassLoader載入實際的類
                        classClass = classLoader.loadClass(actualClass);
                    } else {
                        classClass = Class.forName(actualClass);
                    }
                }
            } catch (ClassNotFoundException e) {
                unavailable(null);
                getServletContext().log("Error loading " + classLoader + " " + actualClass, e);
                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 = (Servlet) classClass.newInstance();
                // Annotation processing
                if (!((Context) getParent()).getIgnoreAnnotations()) {
                    if (getParent() instanceof StandardContext) {
                        ((StandardContext) getParent()).getAnnotationProcessor().processAnnotations(servlet);
                        ((StandardContext) getParent()).getAnnotationProcessor().postConstruct(servlet);
                    }
                }
            } catch (ClassCastException e) {
                unavailable(null);
                // Restore the context ClassLoader
                throw new ServletException(sm.getString("standardWrapper.notServlet", actualClass), e);
            } catch (Throwable e) {
                unavailable(null);

                // Added extra log statement for Bugzilla 36630:
                // http://issues.apache.org/bugzilla/show_bug.cgi?id=36630
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("standardWrapper.instantiate", actualClass), e);
                }

                // 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) || ((Context) getParent()).getPrivileged())) {
                ((ContainerServlet) servlet).setWrapper(this);
            }

            classLoadTime = (int) (System.currentTimeMillis() - t1);
            // Call the initialization method of this servlet
            try {
                // 觸發servlet的beforeInit事件
                instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, servlet);

                if (Globals.IS_SECURITY_ENABLED) {

                    Object[] args = new Object[] { ((ServletConfig) facade) };
                    SecurityUtil.doAsPrivilege("init", servlet, classType, args);
                    args = null;
                } else {
                    // 觸發servlet初始化操作
                    servlet.init(facade);
                }

                // Invoke jspInit on JSP pages
                if ((loadOnStartup >= 0) && (jspFile != null)) {
                    // Invoking jspInit
                    DummyRequest req = new DummyRequest();
                    req.setServletPath(jspFile);
                    req.setQueryString(Constants.PRECOMPILE + "=true");
                    DummyResponse res = new DummyResponse();

                    if (Globals.IS_SECURITY_ENABLED) {
                        Object[] args = new Object[] { req, res };
                        SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args);
                        args = null;
                    } else {
                        servlet.service(req, res);
                    }
                }
                // 觸發afterInit初始化操作
                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) {
                getServletContext().log("StandardWrapper.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
            // singleTheadModel純粹沒用的東西,不用考慮了 
            singleThreadModel = servlet instanceof SingleThreadModel;
            if (singleThreadModel) {
                if (instancePool == null) instancePool = new Stack();
            }
            fireContainerEvent("load", this);

            loadTime = System.currentTimeMillis() - t1;
        } finally {
            if (swallowOutput) {
                String log = SystemLogHandler.stopCapture();
                if (log != null && log.length() > 0) {
                    if (getServletContext() != null) {
                        getServletContext().log(log);
                    } else {
                        out.println(log);
                    }
                }
            }
        }
        return servlet;

    }


轉載:http://yjhexy.iteye.com/blog/668334

http://blog.csdn.net/joeyon1985/article/details/38978251