ClassLoader是做什么的?
一个类的生命周期取决于它Class对象的生命周期,经历加载、连接、初始化、使用、和卸载五个阶段。ClassLoader负责将.class文件中的二进制数据加载到内存中,放到jvm的方法区中。
什么是双亲委派模型
一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载。
类加载器分类
类加载器可以划分成三类:
1.启动类加载器(Bootstrap ClassLoader)
这个类加载器负责将\lib目录下的类库加载到虚拟机内存中,用来加载java的核心库,此类加载器并不继承于java.lang.ClassLoader,不能被java程序直接调用,代码是使用C++编写的.是虚拟机自身的一部分.
2.扩展类加载器(Extendsion ClassLoader):
这个类加载器负责加载\lib\ext目录下的类库,用来加载java的扩展库,开发者可以直接使用这个类加载器.
3.应用程序类加载器(Application ClassLoader):
这个类加载器负责加载用户类路径(CLASSPATH)下的类库,一般我们编写的java类都是由这个类加载器加载,这个类加载器是CLassLoader中的getSystemClassLoader()方法的返回值,所以也称为系统类加载器.一般情况下这就是系统默认的类加载器.
jdk 双亲委派模型实现
ClassLoader源码剖析
所有类加载器都会继承自java.lang.ClassLoader类(除了类启动加载器是c++实现的)。双亲委派模型的实现主要是java.lang.ClassLoader#loadClass(java.lang.String, boolean)和java.lang.ClassLoader#findClass两个方法。
先来看下loadClass方法的实现:
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 {
//先判断parent是否为空
//如果不为空,先尝试用父类加载器加载
//如果为空,则先尝试用类启动加载器加载(除了拓展类加载器parent为空,一般parent都不会为空,稍后解释)
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
}
//如果父类加载器没加载成功,
//才尝试调用findClass尝试自己加载这个类
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;
}
}
再来看下findClass的实现:
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
可以看到,findClass的默认实现是抛出ClassNotFoundException异常,也就是说父类加载器加载不成功,就抛出了异常。
AppClassLoader和ExtClassLoader源码剖析
先看下AppClassLoader和ExtClassLoader的继承关系:
可以看出AppClassLoader和ExtClassLoader继承自URLClassLoader。URLClassLoader主要实现通过url路径加载类的功能。从另一个侧面也反映出。jdk的双亲委派模型是基于组合而非继承来实现的。
AppClassLoader和ExtClassLoader的实现都在sun.misc.Launcher类上。Launcher主要是在程序启动的时候加载主要的类加载器(Ext和App类加载器)。我们看下Launcher加载的代码:
public Launcher() {
// Create the extension class loader
ClassLoader extcl;
try {
extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
throw new InternalError(
"Could not create extension class loader", e);
}
// Now create the class loader to use to launch the application
try {
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(
"Could not create application class loader", e);
}
// Also set the context class loader for the primordial thread.
Thread.currentThread().setContextClassLoader(loader);
......
}
//ExtClassLoader的构造函数
public ExtClassLoader(File[] dirs) throws IOException {
super(getExtURLs(dirs), null, factory);
SharedSecrets.getJavaNetAccess().
getURLClassPath(this).initLookupCache(this);
}
//AppClassLoader构造函数
AppClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent, factory);
ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
ucp.initLookupCache(this);
}
Launcher构造函数会创建ExtClassLoader实例和AppClassLoader实例,ExtClassLoader的创建也是调用父类的构造方法,但parent传的是null(parent是null代表父类是类启动加载器),AppClassLoader的构造则传入刚才创建的ExtClassLoader实例,因此他的parent是ExtClassLoader。
我们再来看下如果使用没有parent的构造函数创建类加载器是什么情况:
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
可以看到如果没有使用带parent的构造函数,parent会指定为getSystemClassLoader方法返回的对象,实际上就是AppClassLoader。双亲委派模型的递归调用链就这样形成了。
回头再看loadClass和findClass方法
如果我们想自定义类加载器呢。如果我们不想破坏双亲委派模型,我们可以重写findClass方法。如果我们要破坏双亲委派模型,则需要重写loadClass方法。
做以下实验:
package classLoader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
/**
* 测试自定义
*/
public class UserClassLoaderTest {
/**
* 重写findClass方法
*/
static class UClassLoader1 extends ClassLoader {
String path = "/Users/zhaozhenrong/Codes/workspace/my-test-project/";
/**
* 重写findClass
*
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File file = new File( path+name+".class");
if (!file.exists()) {
throw new ClassNotFoundException(name);
}
byte[] bytes = new byte[(int) file.length()];
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
fis.read(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return defineClass(name, bytes, 0, bytes.length);
}
}
/**
* 重写loadClass
*/
static class UClassLoader2 extends ClassLoader {
String path = "/Users/zhaozhenrong/Codes/workspace/my-test-project/";
/**
* 重写loadClass,破坏双亲委派模型
*
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
File file = new File(path + name + ".class");
if (!file.exists()) {
throw new ClassNotFoundException(name);
}
byte[] bytes = new byte[(int) file.length()];
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
fis.read(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return defineClass(name, bytes, 0, bytes.length);
}catch (ClassNotFoundException ex){
//如果加载不成功才调用父加载器
return super.loadClass(name);
}
}
}
public static void main(String[] args) throws ClassNotFoundException {
UClassLoader1 cl1 = new UClassLoader1();
UClassLoader1 cl2 = new UClassLoader1();
UClassLoader2 cl3 = new UClassLoader2();
UClassLoader2 cl4 = new UClassLoader2();
//1,重写defineClass,classpath中有对应classname
Class<?> c1 = cl1.loadClass("CommomClass1");
Class<?> c2 = cl2.loadClass("CommomClass1");
//classpath有对应的classname,所以是由父classloader加载
System.out.println(c1.equals(c2));
//2,重写defineClass,classpath中没有对应classname
Class<?> c3 = cl1.loadClass("CommomClass");
Class<?> c4 = cl2.loadClass("CommomClass");
//classpath没有对应的classname,所以最终由UClassLoader1加载
//虽然加载同一个类,但是不同的加载起加载,也不相同
System.out.println(c3.equals(c4));
//3,重写loadclass
Class<?> c5 = cl3.loadClass("CommomClass1");
Class<?> c6 = cl4.loadClass("CommomClass1");
//classpath有对应的classname,但重写loadclass破坏了双亲委派模型
//所以由UClassLoader2加载
System.out.println(c5.equals(c6));
}
}
//结果
//true
//false
//false