层次结构和类图
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);
}
/**
* default implementation of this method searches for classes in the
* following order:
*
*
* Invoke {@link #findLoadedClass(String)} to check if the class
* has already been loaded.
*
* Invoke the {@link #loadClass(String) loadClass} method
* on the parent class loader. If the parent is null the class
* loader built-in to the virtual machine is used, instead.
*
* Invoke the {@link #findClass(String)} method to find the
* class.
*
*
*
*/
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 c has
* already been linked, then this method simply returns. Otherwise, the
* class is linked as described in the "Execution" chapter of the
* href="http://java.sun.com/docs/books/jls/">Java Language
Specification.
*
*
* @param c
* The class to link
*
* @throws NullPointerException
* If c is null.
*
* @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 Class.
* Before the Class can be used it must be resolved.
*/
protected final Class> defineClass(String name, byte[] b, int off, int len)
findLoadedClass
/**
* loader has been recorded by the Java virtual machine as an initiating
* null is returned.
*/
protected final Class> findLoadedClass(String name)
findSystemClass
/**
* loading it if necessary.
*
* This method loads the class through the system class loader (see
* {@link #getSystemClassLoader()}). The Class object returned
* might have more than one ClassLoader associated with it.
* Subclasses of ClassLoader need not usually invoke this method,
* because most class loaders need to override just {@link
#findClass(String)}.
*/
protected final Class> findSystemClass(String name)
getParent
/**
* Returns the parent class loader for delegation. Some implementations may
* use null to represent the bootstrap class loader. This method
* will return null 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 c has
* already been linked, then this method simply returns. Otherwise, the
* class is linked as described in the "Execution" chapter of the
* href="http://java.sun.com/docs/books/jls/">Java Language
Specification.
*/
protected final void resolveClass(Class> c)