Java:類加載器(ClassLoader)

聽上去很高端,其實通常自定義類加載器不須要用戶去實現解析的過程,只要負責實現獲取類對應的.class字節流部分就ok了,摘錄深刻理解Java虛擬機的一段話java

虛擬機設計團隊把類加載階段中的「經過一個類的全限定名來獲取描述此類的二進制字節流」這個動做放到Java虛擬機外部去實現,以便讓應用程序本身決定如何去獲取所須要的類。實現這個動做的代碼模塊稱爲「類加載器」web

實現類加載器須要繼承ClassLoader這個抽象類,用戶只要實現其中的findClass方法便可。在該類的javadoc中給出了這樣一個示例:apache

      class NetworkClassLoader extends ClassLoader {
          String host;
          int port;
 
          public Class findClass(String name) {
              byte[] b = loadClassData(name);
              return defineClass(name, b, 0, b.length);
          }
 
          private byte[] loadClassData(String name) {
              // load the class data from the connection
          }
      }

即用戶這個自定義的類加載器的類實現文件在網絡的某個位置而不是本地的文件。defineClass是ClassLoader類中的一個函數,底層調用了本地方法用來作真正的類解析工做。用戶定義的findClass方法會在已有加載器沒法加載指定名稱的類時被調用。不一樣的類加載器即便加載(defineClass執行者不一樣,若是一個自定義的類加載器最後仍是調用了系統的類加載器加載那麼和直接使用系統加載器加載的效果是一致的)相同的類在JVM看來也屬於不同。具體在loadClass方法中:bootstrap

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

咱們繼承的ClassLoader中含有一個parent ClassLoader的成員,若是它不爲null,那麼類先會讓它去嘗試着加載,當它找不到指定的類時再調用用戶定義的findClass過程來加載。服務器

Parents Delegation Model(雙親委派模型)

上述先讓類加載器中存在parent類加載器,而且先讓parent類加載器嘗試類加載的過程涉及到類加載器的「雙親委派模型」。雙親其實就是一個祖先(parents對應的中文而已)即當前ClassLoader中的parent成員,委派就是先讓parent加載器代理類加載過程,不行再用本身的類加載過程。按照深刻理解JVM的說法有系統提供的三種類加載器它們的層次以下:websocket

|———————————————|網絡

|    Bootstrap ClassLoader       |app

|———————————————|eclipse

                  |webapp

|----------------------------------|

|   Extension ClassLoder        |

|----------------------------------|

                  |

|----------------------------------|

|   Application ClassLoder      |

|----------------------------------|

 

Bootstrap ClassLoader: 負責加載存放在JAVA_HOME/lib目錄中的或者被-Xbootclasspath參數所指定的,而且是按名稱被虛擬機識別的(如rt.jar,名字不符不會識別)

Extension ClassLoader: 負責加載JAVA_HOME/lib/ext目錄中的或者被java.ext.dirs系統變量指定的路徑中的全部類庫

Application ClassLoader: 負責加載用戶路徑(classpath)上所指定的類庫,通常狀況下這個就是程序中默認的類加載器

 bootstrap classloader不能被直接獲取,以下代碼將輸出null,表明Integer這個類是由bootstrap加載的,也能夠從ClassLoader的loadClass判斷過程看出若是parent==null就採用bootstrap去加載。

System.out.println(Integer.class.getClassLoader());

application classloader能夠經過ClassLoader.getSystemClassLoader()靜態方法獲取,以下代碼將返回true,klass是一個用戶定義的類的class屬性

System.out.println(ClassLoader.getSystemClassLoader() == klass.getClassLoader());

本身定義的類加載器默認狀況下將ApplicationClassLoader做爲parent類加載器。另外線程體也能夠設置上下文類加載器:

        Thread.currentThread().setContextClassLoader(cl);
        Thread.currentThread().getContextClassLoader();

應用

若是咱們須要將jetty嵌入到已有的Java代碼中,在初始化Jetty服務器前先實例化一個全局配置類。若是一個類在啓動Jetty前的ClassLoader的搜索目錄和Jetty的webroot/inf中都存在(可是對應的資源文件可能只有在前者對應的目錄中存在),能夠設置優先從parentClassLoader中進行載入。jetty除了碰到一些語言級別的類會從parent類加載器獲取,其餘都先從本身的類加載器獲取(不然會使得各個容器下的app能夠相互影響)。當使用嵌入式jetty時,通常就運行一個app因此,能夠把這個選項打開,以下:

appContext.setParentLoaderPriority(true);

 下面是jetty的WebAppClassLoader繼承了URLClassLoader:

    @Override
    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
    {
        Class<?> c= findLoadedClass(name);
        ClassNotFoundException ex= null;
        boolean tried_parent= false;
        
        boolean system_class=_context.isSystemClass(name);
        boolean server_class=_context.isServerClass(name);
        
        if (system_class && server_class)
        {
            return null;
        }
        
        if (c == null && _parent!=null && (_context.isParentLoaderPriority() || system_class) && !server_class)
        {
            tried_parent= true;
            try
            {
                c= _parent.loadClass(name);
                if (LOG.isDebugEnabled())
                    LOG.debug("loaded " + c);
            }
            catch (ClassNotFoundException e)
            {
                ex= e;
            }
        }

        if (c == null)
        {
            try
            {
                c= this.findClass(name);
            }
            catch (ClassNotFoundException e)
            {
                ex= e;
            }
        }

        if (c == null && _parent!=null && !tried_parent && !server_class )
            c= _parent.loadClass(name);

        if (c == null)
            throw ex;

        if (resolve)
            resolveClass(c);

        if (LOG.isDebugEnabled())
            LOG.debug("loaded " + c+ " from "+c.getClassLoader());
        
        return c;
    }

其中的serverClass和systemClass見WebAppContext:

    public final static String[] __dftSystemClasses =
    {
        "java.",                            // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
        "javax.",                           // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
        "org.xml.",                         // needed by javax.xml
        "org.w3c.",                         // needed by javax.xml
        "org.apache.commons.logging.",      // TODO: review if special case still needed
        "org.eclipse.jetty.continuation.",  // webapp cannot change continuation classes
        "org.eclipse.jetty.jndi.",          // webapp cannot change naming classes
        "org.eclipse.jetty.plus.jaas.",     // webapp cannot change jaas classes
        "org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension
        "org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension
        "org.eclipse.jetty.websocket.WebSocketServlet", // webapp cannot change WebSocketServlet
        "org.eclipse.jetty.servlet.DefaultServlet" // webapp cannot change default servlets
    } ;

    // Server classes are classes that are hidden from being
    // loaded by the web application using system classloader,
    // so if web application needs to load any of such classes,
    // it has to include them in its distribution.
    public final static String[] __dftServerClasses =
    {
        "-org.eclipse.jetty.continuation.", // don't hide continuation classes
        "-org.eclipse.jetty.jndi.",         // don't hide naming classes
        "-org.eclipse.jetty.plus.jaas.",    // don't hide jaas classes
        "-org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension
        "-org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension
        "-org.eclipse.jetty.websocket.WebSocketServlet", // don't hide WebSocketServlet
        "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
        "-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners
        "org.eclipse.jetty."                // hide other jetty classes
    } ;

 不懂啊!待續

相關文章
相關標籤/搜索