轉自http://blog.csdn.net/vking_wang/article/details/17162327
層次結構和類圖
ClassLoader層次結構:
UML類圖:
- sun.misc.Launcher.ExtClassLoader
- sun.misc.Launcher.AppClassLoader
顯式加載類
在代碼中顯式加載某個類,有三種方法:
- this.getClass().getClassLoader().loadClass()
- Class.forName()
- MyClassLoader.findClass()
ClassLoader.loadClass()
ClassLoader.loadClass()的加載步驟爲:
- 調用
findLoadedClass(String)
來檢查是否已經加載類。 - 在父類加載器上調用
loadClass
方法。如果父類加載器爲 null,則使用虛擬機的內置類加載器。 - 調用
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()的功能是找到class文件並把字節碼加載到內存中。
自定義的ClassLoader一般覆蓋這個方法。——以便使用不同的加載路徑。
在其中調用defineClass()解析字節碼。
自定義的加載器可以覆蓋該方法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。
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)