ClassLoader源碼分析

轉自http://blog.csdn.net/vking_wang/article/details/17162327

層次結構和類圖

ClassLoader層次結構:

 
 
 
UML類圖:
 

 
  • sun.misc.Launcher.ExtClassLoader
  • sun.misc.Launcher.AppClassLoader
 

顯式加載類

在代碼中顯式加載某個類,有三種方法:
  1. this.getClass().getClassLoader().loadClass()
  2. Class.forName()
  3. MyClassLoader.findClass()

ClassLoader.loadClass()

ClassLoader.loadClass()的加載步驟爲:
  1. 調用 findLoadedClass(String) 來檢查是否已經加載類。
  2. 在父類加載器上調用 loadClass 方法。如果父類加載器爲 null,則使用虛擬機的內置類加載器。
  3. 調用 findClass(String) 方法查找類。

 

 
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    /**
     * Loads the class with the specified <a href="#name">binary name</a>.  The
     * default implementation of this method searches for classes in the
     * following order:
     *
     * <p><ol>
     *
     *   <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
     *   has already been loaded.  </p></li>
     *
     *   <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
     *   on the parent class loader.  If the parent is <tt>null</tt> the class
     *   loader built-in to the virtual machine is used, instead.  </p></li>
     *
     *   <li><p> Invoke the {@link #findClass(String)} method to find the
     *   class.  </p></li>
     *
     * </ol>
     *
     * <p> 
     */
    protected synchronized Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
    {
    // First, check if the class has already been loaded
    Class c = findLoadedClass(name);
    if (c == null) {
        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.
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
    }
 
 

URLClassLoader.findClass()

 

ClassLoader.loadClass()的最後一步是調用findClass(),這個方法在ClassLoader中並未實現,由其子類負責實現。

findClass()的功能是找到class文件並把字節碼加載到內存中。

自定義的ClassLoader一般覆蓋這個方法。——以便使用不同的加載路徑

 
    /* The search path for classes and resources */
    URLClassPath ucp;
    /* The context to be used when loading classes and resources */
    private AccessControlContext acc;
 
   /**
     * Finds and loads the class with the specified name from the URL search
     * path. Any URLs referring to JAR files are loaded and opened as needed
     * until the class is found.
     *
     * @param name the name of the class
     * @return the resulting class
     * @exception ClassNotFoundException if the class could not be found
     */
    protected Class<?> findClass(final String name)
     throws ClassNotFoundException
    {
    try {
        return (Class)
        AccessController.doPrivileged(new PrivilegedExceptionAction() {
            public Object run() throws ClassNotFoundException {
            String path = name.replace('.''/').concat(".class");
            // 1. URLClassPath ucp,幫助獲取class文件字節流
            //    URLClassPath會用FileLoader或者JarLoader去加載字節碼 
            Resource res = ucp.getResource(path, false); 
            if (res != null) {
                try {
                // 2. defineClass,創建類對象,將字節流解析成JVM能夠識別的Class對象。
                return defineClass(name, res, true);
                } catch (IOException e) {
                throw new ClassNotFoundException(name, e);
                }
            } else {
                throw new ClassNotFoundException(name);
            }
            }
        }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
    }
    }
 

ClassLoader.resolveClass()

加載完字節碼後,會根據需要進行驗證、解析。
 
    /**
     * Links the specified class.  This (misleadingly named) method may be
     * used by a class loader to link a class.  If the class <tt>c</tt> has
     * already been linked, then this method simply returns. Otherwise, the
     * class is linked as described in the "Execution" chapter of the <a
     * href="http://java.sun.com/docs/books/jls/">Java Language Specification</a>.
     * </p>
     *
     * @param  c
     *         The class to link
     *
     * @throws  NullPointerException
     *          If <tt>c</tt> is <tt>null</tt>.
     *
     * @see  #defineClass(String, byte[], int, int)
     */
    protected final void resolveClass(Class<?> c) {
    resolveClass0(c);
    }
    private native void resolveClass0(Class c);
 
 

自定義加載器

  • findClass()定義加載路徑

findClass()的功能是找到class文件並把字節碼加載到內存中。

自定義的ClassLoader一般覆蓋這個方法。——以便使用不同的加載路徑

在其中調用defineClass()解析字節碼。

  • loadClass()定義加載機制

自定義的加載器可以覆蓋該方法loadClass(),以便定義不同的加載機制

例如Servlet中的WebappClassLoader覆蓋了該方法,在WEB-INFO/classes目錄下查找類文件;在加載時,如果成功,則緩存到ResourceEntry對象。——不同的加載機制。

 

AppClassLoader覆蓋了loadClass()方法。

如果自定義的加載器僅覆蓋了findClass,而未覆蓋loadClass(即加載規則一樣,但加載路徑不同);則調用getClass().getClassLoader()返回的仍然是AppClassLoader!因爲真正load類的,還是AppClassLoader。

  • 實現類的熱部署

JVM默認不能熱部署類,因爲加載類時會去調用findLoadedClass(),如果類已被加載,就不會再次加載。

JVM判斷類是否被加載有兩個條件:完整類名是否一樣、ClassLoader是否是同一個。

所以要實現熱部署的話,只需要使用ClassLoader的不同實例來加載。


MyClassLoader cl1 = new MyClassLoader();
Class c1 = cl1.findClass("Test.class");
c1.newInstance();

MyClassLoader cl2 = new MyClassLoader();
Class c2 = cl2.findClass("Test.class");
c2.newInstance();

上例中的c1和c2就是兩個不同的實例。

如果用同一個ClassLoader實例來加載,則會拋LinkageError。

 

  • 不可覆蓋的final方法

defineClass

    /**
     * Converts an array of bytes into an instance of class <tt>Class</tt>.
     * Before the <tt>Class</tt> can be used it must be resolved.
     */
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
 

findLoadedClass

    /**
     * Returns the class with the given <a href="#name">binary name</a> if this
     * loader has been recorded by the Java virtual machine as an initiating
     * loader of a class with that <a href="#name">binary name</a>.  Otherwise
     * <tt>null</tt> is returned.  </p>
     */
protected final Class<?> findLoadedClass(String name) 
 

findSystemClass

 
    /**
     * Finds a class with the specified <a href="#name">binary name</a>,
     * loading it if necessary.
     *
     * <p> This method loads the class through the system class loader (see
     * {@link #getSystemClassLoader()}).  The <tt>Class</tt> object returned
     * might have more than one <tt>ClassLoader</tt> associated with it.
     * Subclasses of <tt>ClassLoader</tt> need not usually invoke this method,
     * because most class loaders need to override just {@link #findClass(String)}.  </p>
     */
protected final Class<?> findSystemClass(String name)
 

getParent

    /**
     * Returns the parent class loader for delegation. Some implementations may
     * use <tt>null</tt> to represent the bootstrap class loader. This method
     * will return <tt>null</tt> in such implementations if this class loader's
     * parent is the bootstrap class loader.
     */
public final ClassLoader getParent()
 

resolveClass

 /**
     * Links the specified class.  This (misleadingly named) method may be
     * used by a class loader to link a class.  If the class <tt>c</tt> has
     * already been linked, then this method simply returns. Otherwise, the
     * class is linked as described in the "Execution" chapter of the <a
     * href="http://java.sun.com/docs/books/jls/">Java Language Specification</a>.
     */
protected final void resolveClass(Class<?> c) 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章