Tomcat源碼學習(五)-- Tomcat_7.0.70 類加載體系分析

一、前言

Tomcat遵循J2EE規範,實現了Web容器。Java虛擬機有本身的一套類加載體系,一樣Tomcat也有本身的一套類加載體系。java

二、概述

首先簡單介紹下Java虛擬機的主要的類加載器:web

  1. 啓動類加載器(bootstrap classloader)apache

    它用來加載 Java 的核心庫,是用原生代碼(本地代碼,與平臺有關)來實現的,並不繼承自java.lang.ClassLoader。這個類加載器負責將存放在<JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath參數所指定的路徑中的,而且是虛擬機識加的(僅按照文件名識別,如rt.jar,名字不符合的類庫即便放在lib目錄中也不會被加載)類庫加載到虛擬機內存中。啓動類加載器沒法被Java程序直接引用。bootstrap

  2. 擴展類加載器(extensions classloader)session

    擴展類加載器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 實現的。它負責將 < Java_Runtime_Home >/lib/ext 或者由系統變量java.ext.dir 指定位置中的類庫加載到內存中app

  3. 應用程序類加載器(application classloader)webapp

    系統類加載器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現的,因爲這個類加載器是ClassLoader中getSystemClassLoader()方法的返回值,因此通常也稱它爲系統類加載器。它根據 Java 應用的類路徑(CLASSPATH)來加載 Java 類。開發者能夠直接使用這個類加載器,若是應用程序中沒有自定義過本身的類加載器,通常狀況下這個就是程序默認的類加載器。工具

  4. 用戶自定義的類裝載器this

    用戶自定義的類裝載器是普通的Java對象,它的類必須派生自java.lang.ClassLoader類。ClassLoader中定義的方法爲程序爲程序提供了訪問類裝載器機制的接口。此外,對於每個被裝載的類型,Java虛擬機都會爲它建立一個java.lang.Class類的實例來表明該類型。和全部其它對象同樣,用戶自定義的類裝載器以有Class類的實例都放在內存中的堆區,而裝載的類型信息則都放在方法區。url

而後在來一張圖簡要說明Tomcat的類加載體系(圖畫的很差):

  • ClassLoader:Java提供的類加載器抽象類,用戶自定義的類加載器須要繼承實現
  • commonLoader:Tomcat最基本的類加載器,加載路徑中的class能夠被Tomcat容器自己以及各個Webapp訪問
  • catalinaLoader:Tomcat容器私有的類加載器,加載路徑中的class對於Webapp不可見
  • sharedLoader:各個Webapp共享的類加載器,加載路徑中的class對於全部Webapp可見,可是對於Tomcat容器不可見
  • WebappClassLoader:各個Webapp私有的類加載器,加載路徑中的class只對當前Webapp可見

三、分析

commonLoader、catalinaLoader和sharedLoader在Tomcat容器初始化的一開始,即調用Bootstrap的init方法時建立。catalinaLoader會被設置爲Tomcat主線程的線程上下文類加載器,而且使用catalinaLoader加載Tomcat容器自身容器下的class。Bootstrap的init方法的部分代碼以下:

/**
 * Initialize daemon.
 */
public void init()
    throws Exception
{
	setCatalinaHome();
    setCatalinaBase();

    initClassLoaders();

    Thread.currentThread().setContextClassLoader(catalinaLoader);

    SecurityClassLoad.securityClassLoad(catalinaLoader);
	.....
}

initClassLoaders方法:

private void initClassLoaders() {
    try {
        commonLoader = createClassLoader("common", null);
        if( commonLoader == null ) {
            // no config file, default to this loader - we might be in a 'single' env.
            commonLoader=this.getClass().getClassLoader();
        }
        catalinaLoader = createClassLoader("server", commonLoader);
        sharedLoader = createClassLoader("shared", commonLoader);
    } catch (Throwable t) {
        handleThrowable(t);
        log.error("Class loader creation threw exception", t);
        System.exit(1);
    }
}

建立類加載器的createClassLoader方法的實現:

private ClassLoader createClassLoader(String name, ClassLoader parent)
    throws Exception {

    String value = CatalinaProperties.getProperty(name + ".loader");
    if ((value == null) || (value.equals("")))
        return parent;

    value = replace(value);

    List<Repository> repositories = new ArrayList<Repository>();

    StringTokenizer tokenizer = new StringTokenizer(value, ",");
    while (tokenizer.hasMoreElements()) {
        String repository = tokenizer.nextToken().trim();
        if (repository.length() == 0) {
            continue;
        }

        // Check for a JAR URL repository
        try {
            @SuppressWarnings("unused")
            URL url = new URL(repository);
            repositories.add(
                    new Repository(repository, RepositoryType.URL));
            continue;
        } catch (MalformedURLException e) {
            // Ignore
        }

        // Local repository
        if (repository.endsWith("*.jar")) {
            repository = repository.substring
                (0, repository.length() - "*.jar".length());
            repositories.add(
                    new Repository(repository, RepositoryType.GLOB));
        } else if (repository.endsWith(".jar")) {
            repositories.add(
                    new Repository(repository, RepositoryType.JAR));
        } else {
            repositories.add(
                    new Repository(repository, RepositoryType.DIR));
        }
    }

    return ClassLoaderFactory.createClassLoader(repositories, parent);
}

createClassLoader最終使用ClassLoaderFactory.createClassLoader(locations, types, parent)方法建立ClassLoader。

咱們在看SecurityClassLoad.securityClassLoad(catalinaLoader);

public static void securityClassLoad(ClassLoader loader)
    throws Exception {

    if( System.getSecurityManager() == null ){
        return;
    }

    loadCorePackage(loader);
    loadCoyotePackage(loader);
    loadLoaderPackage(loader);
    loadRealmPackage(loader);
    loadServletsPackage(loader);
    loadSessionPackage(loader);
    loadUtilPackage(loader);
    loadValvesPackage(loader);
    loadJavaxPackage(loader);
    loadConnectorPackage(loader);
    loadTomcatPackage(loader);
}

securityClassLoad方法主要加載Tomcat容器所需的class,包括:

  • Tomcat核心class,即org.apache.catalina.core路徑下的class;
  • org.apache.catalina.loader.WebappClassLoader$PrivilegedFindResourceByName
  • Tomcat有關session的class,即org.apache.catalina.session路徑下的class
  • Tomcat工具類的class,即org.apache.catalina.util路徑下的class
  • javax.servlet.http.Cookie
  • Tomcat處理請求的class,即org.apache.catalina.connector路徑下的class
  • Tomcat其它工具類的class,也是org.apache.catalina.util路徑下的class

咱們以加載Tomcat核心class的loadCorePackage方法爲例,查看其實現:

private static final void loadCorePackage(ClassLoader loader)
    throws Exception {
    final String basePackage = "org.apache.catalina.core.";
    loader.loadClass
        (basePackage +
         "AccessLogAdapter");
    loader.loadClass
        (basePackage +
         "ApplicationContextFacade$1");
    loader.loadClass
        (basePackage +
         "ApplicationDispatcher$PrivilegedForward");
    loader.loadClass
        (basePackage +
         "ApplicationDispatcher$PrivilegedInclude");
    loader.loadClass
        (basePackage +
        "AsyncContextImpl");
    loader.loadClass
        (basePackage +
        "AsyncContextImpl$DebugException");
    loader.loadClass
        (basePackage +
        "AsyncContextImpl$1");
    loader.loadClass
        (basePackage +
        "AsyncListenerWrapper");
    loader.loadClass
        (basePackage +
         "ContainerBase$PrivilegedAddChild");
    loader.loadClass
        (basePackage +
         "DefaultInstanceManager$1");
    loader.loadClass
        (basePackage +
         "DefaultInstanceManager$2");
    loader.loadClass
        (basePackage +
         "DefaultInstanceManager$3");
    loader.loadClass
        (basePackage +
         "DefaultInstanceManager$AnnotationCacheEntry");
    loader.loadClass
        (basePackage +
         "DefaultInstanceManager$AnnotationCacheEntryType");
    loader.loadClass
        (basePackage +
         "ApplicationHttpRequest$AttributeNamesEnumerator");
}

至此爲止,咱們尚未看到WebappClassLoader。啓動StandardContext的時候會建立WebappLoader,StandardContext的方法startInternal的部分代碼以下:

protected synchronized void startInternal() throws LifecycleException {  
  
    ......
  
    if (getLoader() == null) {  
        WebappLoader webappLoader = new WebappLoader(getParentClassLoader());  
        webappLoader.setDelegate(getDelegate());  
        setLoader(webappLoader);  
    }  
   ......  
   if ((loader != null) && (loader instanceof Lifecycle))  
        ((Lifecycle) loader).start();   
   // 省略後邊的代碼   
}

從上面代碼看到最後會調用WebappLoader的start方法:

public final synchronized void start() throws LifecycleException {
    
    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {
        
        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
        }
        
        return;
    }
    
    if (state.equals(LifecycleState.NEW)) {
        init();
    } else if (state.equals(LifecycleState.FAILED)) {
        stop();
    } else if (!state.equals(LifecycleState.INITIALIZED) &&
            !state.equals(LifecycleState.STOPPED)) {
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }

    setStateInternal(LifecycleState.STARTING_PREP, null, false);

    try {
        startInternal();//start再次調用了startInternal方法(WebappLoader中的方法)
    } catch (Throwable t) {
        // This is an 'uncontrolled' failure so put the component into the
        // FAILED state and throw an exception.
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
    }

    if (state.equals(LifecycleState.FAILED)) {
        // This is a 'controlled' failure. The component put itself into the
        // FAILED state so call stop() to complete the clean-up.
        stop();
    } else if (!state.equals(LifecycleState.STARTING)) {
        // Shouldn't be necessary but acts as a check that sub-classes are
        // doing what they are supposed to.
        invalidTransition(Lifecycle.AFTER_START_EVENT);
    } else {
        setStateInternal(LifecycleState.STARTED, null, false);
    }
}

start又調用了startInternal方法,startInternal的實現以下:

protected void startInternal() throws LifecycleException {

    if (log.isDebugEnabled())
        log.debug(sm.getString("webappLoader.starting"));

    if (container.getResources() == null) {
        log.info("No resources for " + container);
        setState(LifecycleState.STARTING);
        return;
    }

    // Register a stream handler factory for the JNDI protocol
    URLStreamHandlerFactory streamHandlerFactory =
            DirContextURLStreamHandlerFactory.getInstance();
    if (first) {
        first = false;
        try {
            URL.setURLStreamHandlerFactory(streamHandlerFactory);
        } catch (Exception e) {
            // Log and continue anyway, this is not critical
            log.error("Error registering jndi stream handler", e);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            // This is likely a dual registration
            log.info("Dual registration of jndi stream handler: "
                     + t.getMessage());
        }
    }

    // Construct a class loader based on our current repositories list
    try {

        classLoader = createClassLoader();
        classLoader.setResources(container.getResources());
        classLoader.setDelegate(this.delegate);
        classLoader.setSearchExternalFirst(searchExternalFirst);
        if (container instanceof StandardContext) {
            classLoader.setAntiJARLocking(
                    ((StandardContext) container).getAntiJARLocking());
            classLoader.setClearReferencesRmiTargets(
                    ((StandardContext) container).getClearReferencesRmiTargets());
            classLoader.setClearReferencesStatic(
                    ((StandardContext) container).getClearReferencesStatic());
            classLoader.setClearReferencesStopThreads(
                    ((StandardContext) container).getClearReferencesStopThreads());
            classLoader.setClearReferencesStopTimerThreads(
                    ((StandardContext) container).getClearReferencesStopTimerThreads());
            classLoader.setClearReferencesHttpClientKeepAliveThread(
                    ((StandardContext) container).getClearReferencesHttpClientKeepAliveThread());
        }

        for (int i = 0; i < repositories.length; i++) {
            classLoader.addRepository(repositories[i]);
        }

        // Configure our repositories
        setRepositories();
        setClassPath();

        setPermissions();

        ((Lifecycle) classLoader).start();

        // Binding the Webapp class loader to the directory context
        DirContextURLStreamHandler.bind(classLoader,
                this.container.getResources());

        StandardContext ctx=(StandardContext)container;
        String contextName = ctx.getName();
        if (!contextName.startsWith("/")) {
            contextName = "/" + contextName;
        }
        ObjectName cloname = new ObjectName
            (MBeanUtils.getDomain(ctx) + ":type=WebappClassLoader,context="
             + contextName + ",host=" + ctx.getParent().getName());
        Registry.getRegistry(null, null)
            .registerComponent(classLoader, cloname, null);

    } catch (Throwable t) {
        t = ExceptionUtils.unwrapInvocationTargetException(t);
        ExceptionUtils.handleThrowable(t);
        log.error( "LifecycleException ", t );
        throw new LifecycleException("start: ", t);
    }

    setState(LifecycleState.STARTING);
}

最後咱們看看createClassLoader的實現:

private WebappClassLoaderBase createClassLoader()
    throws Exception {

    Class<?> clazz = Class.forName(loaderClass);
    WebappClassLoaderBase classLoader = null;

    if (parentClassLoader == null) {
        parentClassLoader = container.getParentClassLoader();
    }
    Class<?>[] argTypes = { ClassLoader.class };
    Object[] args = { parentClassLoader };
    Constructor<?> constr = clazz.getConstructor(argTypes);
    classLoader = (WebappClassLoaderBase) constr.newInstance(args);

    return classLoader;

}

至此Tomcat類加載完畢。

相關文章
相關標籤/搜索