天天看點

tomcat7源碼Bootstrap

        tomcat的啟動從bootstrap的main方法開始,在main方法中主要是做了三件事,調用init方法初始化自己,調用catalinaDaemon對象

的setAwait方法設定它的await屬性為true,最後調用自己的start方法。

        首先看看init方法:

public void init()
        throws Exception
    {

        // Set Catalina path
        setCatalinaHome();
        setCatalinaBase();

        //初始化三個類加載器
        initClassLoaders();

         //線程構造後将從它的父線程中繼承相應的上下文類加載器. 如果在整個應用中你不做任何特殊設定, 
        //所有的線程将都以系統類加載器(system classloader)作為自己的線程上下文類加載器.
        //這裡從Bootstrap構造的線程都會繼承改線程的上下文類加載器
        Thread.currentThread().setContextClassLoader(catalinaLoader);

        //加載conf下的catalina.properties配置檔案下的common.loader和server.loader所指定的類。因為server.loader為空時
        //catalinaLoader和commonLoader是指向的同一對象,不為空時commonLoader是catalinaLoader的父類。
        SecurityClassLoad.securityClassLoad(catalinaLoader);

        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        Class<?> startupClass =
            catalinaLoader.loadClass
            ("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();

        // Set the shared extensions class loader
        if (log.isDebugEnabled())
            log.debug("Setting startup class properties");
        String methodName = "setParentClassLoader";
        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Class.forName("java.lang.ClassLoader");
        Object paramValues[] = new Object[1];
        paramValues[0] = sharedLoader;
        Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);

        catalinaDaemon = startupInstance;

    }
           

       這裡的主要内容就是調用了initClassLoaders();初始化了commonLoader,catalinaLoader,sharedLoader三個類加載器,它們是bootstrap的

成員變量。并通過SecurityClassLoad.securityClassLoad(catalinaLoader)這步代碼來加載conf/catalina.properties檔案下common.loader,

server.loader,shared.loade三個屬性指定的jar包和class。securityClassLoad會通過一系列環境變量替換等處理來找到相關的類并加載它們。

最後一段代碼初始化Catalina對象,再設定它的父類加載器為sharedLoader。這裡的一些疑問稍後再說。

        先看看initClassLoaders()方法:

initClassLoaders源碼:
   private void initClassLoaders() {
        try {
            commonLoader = createClassLoader("common", null);//1,建立commonLoader類加載器
            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);//2,
            sharedLoader = createClassLoader("shared", commonLoader);//3,
        } catch (Throwable t) {
            handleThrowable(t);
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }
           

      第一步是通過common關鍵字初始化了一個commonLoader對象。

      進到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);
        ........
        ClassLoader classLoader = ClassLoaderFactory.createClassLoader
            (repositories, parent);
        ........
        return classLoader;
    }
           

      這裡應該很清楚,CatalinaProperties.getProperty(name + ".loader");就是擷取配置檔案裡的屬性的方法。value = replace(value)替換環境變量

等一系列的轉換擷取了一些類路徑的url最後将這些url傳給ClassLoaderFactory.createClassLoader方法。CommonclassLoader和其他兩個類加載器都

是通過該方法擷取的,它們是一個StandardClassLoader執行個體。StandardClassLoader隻是簡單的從URLClassLoader繼承,并實作了

StandardClassLoaderMBean接口來實作JMX監控。是以可以認為這三個類加載器就是URLClassLoader對象,

createClassLoader(repositories, parent),其實就是調用了URLClassLoader的構造方法,傳入類路徑的URL集合和父加載器。如果打開

conf/catalina.properties配置檔案,server.loader,shared.loade預設為空。從createClassLoader方法中if判斷就可以知道

commonLoader == catalinaLoader == sharedLoader。

    這裡先說一點設定這三個類加載器的用意,以後再用源碼證明。commonLoader被用來設定為catalinaLoader和sharedLoader的父加載器。

catalinaLoader用來加載tomcat自身程式所需要的類。sharedLoader用來加載一些webapp目錄下的程式共享的class,并被WebappClassLoader

(使用者web程式的類加載器)設定為父類(從這裡就可以知道web程式的類是怎麼實作互相隔離,但又可以共享某些jar包的。因為類加載器在加載

類時會先讓父加載器去加載,父加載器找不到的話就自己來加載,在這裡sharedLoader和WebappClassLoader都有不同的加載路徑,所有的

WebappClassLoader都繼承自sharedLoader,是以他們共享了sharedLoader的加載路徑)。

    再來看看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);
    }
           

      可以進每個方法看下,它是加載了指定的一些類,注意這些類名是指定的,如果你放了自己的jar包在裡面,那麼它的加載就不是catalinaLoader完成的。

      最後就是初始化catalinaDaemon對象了,從最後一段代碼可以看出catalinaDaemon被指派為org.apache.catalina.startup.Catalina的執行個體,

并且通過反射調用調用了setParentClassLoader方法,參數是catalinaLoader。

    這裡設定父加載器的用意是什麼?下面是這個方法的源碼   

public void setParentClassLoader(ClassLoader parentClassLoader) {
        this.parentClassLoader = parentClassLoader;
    }
           

     方法很簡單,看來問題不在這。在來看看Catalina的另一個方法:

public ClassLoader getParentClassLoader() {
        if (parentClassLoader != null) {
            return (parentClassLoader);
        }
        return ClassLoader.getSystemClassLoader();
    }
           

         其實這裡parentClassLoader屬性不是說加載Catalina的類加載器的父類加載器,它隻是一個普通的屬性。

        在來看看哪裡調用了getParentClassLoader,在StandardServer中有這樣一個方法:

@Override
    public ClassLoader getParentClassLoader() {
        if (parentClassLoader != null)
            return (parentClassLoader);
        if (catalina != null) {
            return (catalina.getParentClassLoader());
        }
        return (ClassLoader.getSystemClassLoader());
    }
           

     然後他的下級元件StandardService:

@Override
    public ClassLoader getParentClassLoader() {
        if (parentClassLoader != null)
            return (parentClassLoader);
        if (server != null) {
            return (server.getParentClassLoader());
        }
        return (ClassLoader.getSystemClassLoader());
    }
           

    與之對應的都有一個setParentClassLoader方法。

    engin容器也有這個方法。最後在WebappLoader中有個如下的方法:

private WebappClassLoader createClassLoader()
        throws Exception {

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

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

        return classLoader;

    }
           

   在這個方法中調用了org.apache.catalina.loader.WebappClassLoader的構造方法将容器container的parentClassLoader的屬性最為classLoader的

父類加載器。這裡可以得出結論從 Server 》 service 》 engin(container) 》沒一個級别都在嘗試設定parentClassLoader的值,而parentClassLoader

将作為WebappClassLoader的父類加載器。也就是說parentClassLoader是針對WebappClassLoader而言的,而每一級别都在嘗試修改它。·

    現在已經介紹完了bootstrap的init方法,下一步是來介紹setAwait(true)方法的作用。通過反射設定了Catalina的await屬性為true。Catalina成功啟動後

會通過檢查該值來調用它的成員變量server的await方法:

public void start() {
    ........
        if (await) {
            await();
            stop();
        }
    }
 public void await() {

        getServer().await();

    }
           

Catalina的成員變量server是一個StandardServer對象,通過調用該對象的await方法,會讓他一直監控server.xml配置檔案中的這段配置:

<Server port="8005" shutdown="SHUTDOWN">,所定義的端口發送過來的指令。

      最後看看start方法:

public void start()
    throws Exception {
    if( catalinaDaemon==null ) init();

    Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
    method.invoke(catalinaDaemon, (Object [])null);

}
           

   start最終通過反射調用了catalinaDaemon(也就是Catalina)的start方法。(這裡看到的多處位置是通過反射調用catalinaDaemon的方法,

因為catalinaDaemon是一個object的引用指向的Catalina對象所有它隻能調用object對象的方法,想要調用Catalina的方法隻能通過反射。)

   總結:bootstrap的主要任務就是初始化commonLoader,catalinaLoader,sharedLoader,通過catalinaLoader加載所需要的類,

初始化catalinaDaemon對象,調用它的start方法。

    疑問:bin/bootstrap.jar 和lib/catalina.jar中都有bootstrap,腳本調用的是bootstrap.jar中的main方法,bootstrap在初始三個類加載器後,

加載類時并沒有加載org.apache.catalina.startup包下的類,也就是說雖然兩個jar包都含有bootstrap類,但隻是系統的類加載器加載了

bin/bootstrap.jar裡的bootstrap。而其他lib 目錄下的所有的類都是有bootstrap裡面的三個類加載器加載的。不清楚為什麼這樣做。

繼續閱讀