程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> How Tomcat Works 讀書筆記 八 載入器

How Tomcat Works 讀書筆記 八 載入器

編輯:C++入門知識

How Tomcat Works 讀書筆記 八 載入器



 


首先說明兩個術語
倉庫(repository),表示類加載器會在哪裡搜索要載入的類;
資源(resource),知道一個類載入器中的DirContext對象,它的文件跟路徑指的就是上下文的文件跟路徑。
在tomcat中,我們使用了自定義載入器,原因有三:
為了在載入器中指定某些規則;
為了緩存已經載入的類;
為了實現類的預載入;

Loader接口

在加載servlet及相關類的時候,需要遵守一些規則。例如,應用程序中的servelt只能引用部署在web-inf/classes目錄下及其子目錄下的類,只能訪問web-inf/lib目錄下的庫。
我們在tomcat裡說的載入器是指web應用程序載入器,而不僅僅是類載入器。(載入器裡面有個類載入器!!!)
載入器應該實現org.apache.catalina.Loader接口,類載入器默認為WebappClassLoader。
package org.apache.catalina;

import java.beans.PropertyChangeListener;

public interface Loader {
    public ClassLoader getClassLoader();
    public Container getContainer();              //載入器通常與一個context級別的容器相聯
    public void setContainer(Container container);
    public DefaultContext getDefaultContext();
    public void setDefaultContext(DefaultContext defaultContext);
    public boolean getDelegate();                 //Delegate 代表 委托
    public void setDelegate(boolean delegate);    //就是類加載器是否會把加載的任務委托給其父類加載器
    public String getInfo();
    public boolean getReloadable();               //表明是否支持載入器的自動重載
    public void setReloadable(boolean reloadable);
    public void addPropertyChangeListener(PropertyChangeListener listener);
    public void addRepository(String repository);
    public String[] findRepositories();
    public boolean modified();                     //如果容器中的一個或多個類被修改了 modified就會返回true
    public void removePropertyChangeListener(PropertyChangeListenerlistener);
}

默認情況下,在context的標准實現---org.apache.catalina.core.StandContext中是不支持自動重載的,因此要想開啟自動重載功能,就需要在server.xml文件中添加一個Context元素,如下

在我們這一節的程序中Catalina 提供了 org.apache.catalina.loader.WebappLoader 作為 Load 接口的實現。WebappLoader 對象包含一個org.apache.catalina.loader.WebappClassLoader 類的實例,該類擴展了Java.netURLClassLoader 類。
當與某個載入器相關聯的容器需要使用某個servlet時,或者說就是要調用某個servlet的某個方法時,容器首先會調用載入器的getClassLoader()方法返回類載入器,然後再調用類載入器的loadClass()方法來加載這個servlet類。
uml圖如下
\

WebAppLoader類

當webapploader類的start方法啟動時,會完成以下幾項工作:
1 創建一個類載入器
2 設置倉庫
3 設置類路徑
4 設置訪問權限
5 啟動一個新線程來支持自動重載 (在webapploader的run方法中)

創建類載入器

 private WebappClassLoader createClassLoader()
        throws Exception {

    //loadClass為字符串
    //默認為    private String loaderClass =org.apache.catalina.loader.WebappClassLoader;
    //可通過setLoadClass方法更改
        Class clazz = Class.forName(loaderClass);
        WebappClassLoader classLoader = null;

    //在構造WebAppLoader時 又構造函數的參數指定
        if (parentClassLoader == null) {
            // Will cause a ClassCast is the class does not extend WCL, but
            // this is on purpose (the exception will be caught and rethrown)
            classLoader = (WebappClassLoader) clazz.newInstance();
        } else {
            Class[] argTypes = { ClassLoader.class };
            Object[] args = { parentClassLoader };
            Constructor constr = clazz.getConstructor(argTypes);
            classLoader = (WebappClassLoader) constr.newInstance(args);
        }
        return classLoader;
    }

當然我們可以通過setLoadClass來改變類加載器的實現,不過
(WebappClassLoader) constr.newInstance(args);
所以,我們自定義的類也要繼承WebappClassLoader。

設置倉庫

調用setRepositories,設置WEB-ING/classes目錄與WEB-INF/lib目錄

設置類路徑

和jasper JSP編譯器有關

設置訪問權限

setPermissions 可以設置類載入器訪問相關路徑的權限。例如只能訪問WEB-INf/classes目錄與WEB-INF/lib目錄

開啟新線程執行類的重新載入


 public void run() {

        if (debug >= 1)
            log(BACKGROUND THREAD Starting);

        // Loop until the termination semaphore is set
    //整段代碼包含在while循環中
    //在前面threadDone已經被設置為false
    //直到程序關閉時,threadDone才會變為true
        while (!threadDone) {
            // Wait for our check interval
            threadSleep();

            if (!started)
                break;

            try {
                // Perform our modification check
                if (!classLoader.modified())
                    continue;
            } catch (Exception e) {
                log(sm.getString(webappLoader.failModifiedCheck), e);
                continue;
            }

            // Handle a need for reloading
            notifyContext();
            break;

        }

        if (debug >= 1)
            log(BACKGROUND THREAD Stopping);

    }

    private void threadSleep() {          //讓程序休眠一段時間 時間由checkInterval指定
        try {
            Thread.sleep(checkInterval * 1000L);
        } catch (InterruptedException e) {
            ;
        }
    }

checkInterval,是每隔若干秒來檢查一次容器中的類是否有更改!
一旦有更改classLoader.modified()會返回true,直接調用notifyContext();
 private void notifyContext() {
        WebappContextNotifier notifier = new WebappContextNotifier();
        (new Thread(notifier)).start();
    }
protected class WebappContextNotifier implements Runnable {

        /**
         * Perform the requested notification.
         */
        public void run() {
            ((Context) container).reload();
        }
    }
WebappContextNotifier是webapploader的內部類。
這裡重新啟了一個線程,避免了擁塞。

WebappClassLoader類

該類繼承自java.net.URLClassLoader類,後者我們在前面章節已經用過;
WebappClassLoader類的設計考慮了安全與優化兩個方面。
WebappClassLoader 類不允許一些特定的類被加載。這些類被存儲在一個 String 類型的數組中,現在僅僅有一個成員。
private static final String[] triggers = {
javax.servlet.Servlet // Servlet API
};
另外在委派給系統加載器的時候,也不允許加載某些特殊的包的類或者它的子包:
private static final String[] packageTriggers = {
    javax, // Java extensions
    org.xml.sax, // SAX 1 & 2
    org.w3c.dom, // DOM 1 & 2
    org.apache.xerces, // Xerces 1 & 2
    org.apache.xalan // Xalan
};

類緩存

為了達到更好的性能,會緩存已經載入的類,這樣一來下次在使用這個類的時候,就不用再起加載了。

緩存分兩級,一級在本地執行,由webappclassloader實例來管理。
此外,java.lang.ClassLoader類也會維護一個Vector對象,保存已經載入的類,此時緩存由父類管理。

每個由WebappClassLoader載入的類,都視為資源。是org.apache.catalina.loader.ResourceEntry類的實例,裡面包含所代表的class文件的字節流,最後一次修改時間等等:
package org.apache.catalina.loader;
import java.net.URL;
import java.security.cert.Certificate;
import java.util.jar.Manifest;
public class ResourceEntry {
    public long lastModifled = -1;
    // Binary content of the resource.public byte[] binaryContent = null;
    public Class loadedClass = null;
    // URL source from where the object was loaded.
    public URL source = null;
    // URL of the codebase from where the object was loaded.
    public URL CodeBase = null;
    public Manifest manifest = null;
    public Certificate[] certificates = null;
}


所有緩存的源被存放在一個叫做 resourceEntries 的 HashMap 中,鍵值為載入的資源名稱,所有找不到的資源都被放在一個名為 notFoundResources 的 HashMap 中。



至於真正的加載類,我們放在下一節說。


參考資料

http://my.oschina.net/xianggao/blog/70826

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved