一、概念
ClassLoader即类加载器,Java中的类是动态加载的,当需要使用类A时,ClassLoader会将A.class字节码文件动态加载到jvm的方法区内存中,然后生成一个对应的java.lang.Class对象的实例保存该类的信息,并通过该实例的newInstance()来产生对象。
ClassLoader是一个抽象类,通常给定类的名称,然后转换成.class文件名,然后查找相应的类文件进行加载。ClassLoader的实现类图如下:
二、常用的ClassLoader
JVM主要有三个类加载器
1) BootstrapClassLoader:是JVM虚拟机的一部分,采用native代码编写,没有继承ClassLoader类,在虚拟机启动时它负责加载位于$JAVA_HOME/jre/lib/rt.jar等包中的类,包括java.lang.*/java.util.*等,还有java.net.Launcher,这个类内部包含两个静态子类,ExtClassLoader和AppClassLoader。
BoostrapClassLoader加载类的路径为:
System.out.println(System.getProperty("sun.boot.class.path"));
输出:
C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\resources.jar;
C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\rt.jar;
C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\sunrsasign.jar;
C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\jsse.jar;
C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\jce.jar;
C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\charsets.jar;
C:\ProgramFiles\Java\jdk1.8.0_92\jre\lib\jfr.jar;
C:\ProgramFiles\Java\jdk1.8.0_92\jre\classes
2) ExtClassLoader :ClassLoader的实现类,用于加载扩展类,加载类的路径为
System.out.println(System.getProperty("java.ext.dirs"));
输出:
C:\Program Files\Java\jdk1.8.0_92\jre\lib\ext;
C:\Windows\Sun\Java\lib\ext
3) AppClassLoader :ClassLoader的实现类,用于加载应用程序的主函数类和classpath下的包
System.out.println(System.getProperty("java.class.path"));
三、源码解析
1.首先看一下ClassLoader部分源码
1)ClassLoader中定义了父加载器变量parent,用户自定义类加载器时可以通过参数指定父加载器,默认采用AppLoaderClassLoader作为默认父加载器;
2)loadClass()方法用来加载类,首先调用父加载器的loadClass()方法,若父加载器为空,在调用BoostrapClassLoader加载。当父加载器加载失败时,才调用本身的findClass()方法来加载,这个方法在各加载器类中分别实现。
public class ClassLoader {
//实现ClassLoader的类都有一个父加载器
private final ClassLoader parent;
//定义时由外部类指定父加载器
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
...
}
//定义时由外部类指定父加载器
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
//默认的父加载器是AppClassLoader
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
...
return scl;
}
private static synchronized void initSystemClassLoader() {
if (!sclSet) {
if (scl != null)
throw new IllegalStateException("recursive invocation");
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
//Launcher获取的是AppClassLoader
scl = l.getClassLoader();
...
}
sclSet = true;
}
}
//该方法由loadClass()调用,用于加载指定名字的类,由子类去实现,
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
//加载类的方法
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();
//首先由父加载去加载类,如果不存在父加载器,则调用BootstrapClassLoader加载器
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
}
//如果父加载器找不到类,则调用自己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();
}
}
//如果resolve为true,则解析类
if (resolve) {
resolveClass(c);
}
return c;
}
}
}
2. Laucher的部分源码
1)Launcher是在JVM启动时由BoostrapClassLoader加载的,在构造函数中定义了AppClassLoader类实例 loader和ExtClassLoader类实例var2;
2)Launcher类定义了静态子类AppClassLoader,其构造函数中传入了var2,因此ExtClassLoader是其父加载器;
3)还定义了静态子类ExtClassLoader,其构造函数中parent传入null值,因此ExtClassLoader没有父加载器,但是ClassLoader默认的父加载器是BoostrapClassLoader,因此其父加载器为BoostrapClassLoader。
public class Launcher {
private ClassLoader loader;
public Launcher() {
sun.misc.Launcher.ExtClassLoader var1;
try {
//创建ExtClassLoader的实例
var1 = sun.misc.Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//创建AppClassLoader的实例作为默认的ClassLoader,构造函数中传入了ExtClassLoader的实例
this.loader = sun.misc.Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
...
}
public ClassLoader getClassLoader() {
return this.loader;
}
//AppClassLoader是Launcher静态内部类
static class AppClassLoader extends URLClassLoader {
AppClassLoader(URL[] var1, ClassLoader var2) {
//调用UrlClassLoader的构造函数,其中var2将作为parent加载器,因此AppClassLoader的父加载器是ExtClassLoader
super(var1, var2, sun.misc.Launcher.factory);
this.ucp.initLookupCache(this);
}
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : sun.misc.Launcher.getClassPath(var1);
return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction<sun.misc.Launcher.AppClassLoader>() {
public sun.misc.Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : sun.misc.Launcher.pathToURLs(var2);
//调用AppClassLoader的构造函数
return new sun.misc.Launcher.AppClassLoader(var1x, var0);
}
});
}
//其中重写了loadClass方法
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
int var3 = var1.lastIndexOf(46);
if (var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if (var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
}
if (this.ucp.knownToNotExist(var1)) {
Class var5 = this.findLoadedClass(var1);
if (var5 != null) {
if (var2) {
this.resolveClass(var5);
}
return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
return super.loadClass(var1, var2);
}
}
}
static class ExtClassLoader extends URLClassLoader {
//调用URLClassLoader的构造函数,传入的父加载器为null,因此ExtClassLoader的父加载器为空
public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, sun.misc.Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
}
}
上面AppClassLoader和ExtClassLoader中调用了父类URLClassLoader的构造方法,其源码如下
public class URLCLassLoader {
URLClassLoader(URL[] urls, ClassLoader parent,
AccessControlContext acc) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls);
this.acc = acc;
}
}
由以上分析得知,JVM的这种类加载顺序为Parent Delegation Model,其加载顺序如下图所示(借用其他人的图片)
四、自定义类加载器
1.步骤
1)自定义ClassLoader需要继承ClassLoader抽象类,可以在构造函数中指定其父加载器,如果不指定,默认是AppClassLoader;
2)复写findClass()方法;
3)在findClass方法中调用defineClass方法,defineClass方法ClassLoader方法已经提供了实现;
package com.lpq;
public class Test {
public void say(){
System.out.println("Say Hello");
}
}
将Test.class放在E:/test文件夹下
public class MyClassLoader extends ClassLoader{
private String myLibPath;
public MyClassLoader(String myLibPath){
this.myLibPath=myLibPath;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
String fileName =name.substring(name.lastIndexOf(".")+1)+".class";
FileInputStream is = new FileInputStream(new File(myLibPath,fileName));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int b=0;
try {
while((b=is.read())!=-1){
bos.write(b);
}
} catch (IOException e) {
e.printStackTrace();
}
byte[] data = bos.toByteArray();
is.close();
bos.close();
return defineClass(name,data,0,data.length);
} catch (IOException e) {
// TODO Auto-generated catch block
throw new ClassNotFoundException();
}
}
}
public class MainTest {
public static void main(String[] args){
MyClassLoader myClassLoader = new MyClassLoader("E:\\test");
try {
//加载class文件
Class c = myClassLoader.loadClass("com.lpq.Test");
if(c != null){
try {
Object obj = c.newInstance();
Method method = c.getDeclaredMethod("say",null);
//通过反射调用Test类的say方法
method.invoke(obj, null);
System.out.println(obj.getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出:
Say Hello
com.lpq.MyClassLoader@3fb4f649