分析这些类加载器,就绕不开java.lang.ClassLoader这个抽象类。我们先看下这个抽象类。
// The parent class loader for delegation
// Note: VM hardcoded the offset of this field, thus all new fields
// must be added *after* it.
private final ClassLoader parent;
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 先检查这个class是否已经被加载到内存中
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//双亲委派机制的实现原理,如果当前类加载器的父类不为空,就让父类去加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//如果父类加载器为空,则委托给引导类加载器,而该方法最终为native方法private native Class findBootstrapClass(String name);,实际上就是调用openjdk中BootStrap ClassLoader的实现去加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// 如果仍然没有找到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) {
//是否解析当前加载的class
resolveClass(c);
}
return c;
}
}
当前ClassLoader去加载class时,首先判断其“parent”属性的类加载器,如果不为null,则首先让“parent”类加载器去加载,这样按照“类加载委派关系图”一层层往上推;如果,其委派层次上面的“parent”类加载器加载失败,最后由当前的类加载器去加载。这里需要注意的是,虽然用户自定义ClassLoader的“parent”属性指向AppClassLoder,AppClassLoder的“parent”属性指向ExtClassLoder,但是ExtClassLoder的“parent”属性并不是指向Bootstrap ClassLoder,而是为null,当然Bootstrap ClassLoder的“parent”也为null。
static class ExtClassLoader extends URLClassLoader {
private static volatile Launcher.ExtClassLoader instance;
//获取ExtClassLoader
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
if (instance == null) {
Class var0 = Launcher.ExtClassLoader.class;
synchronized(Launcher.ExtClassLoader.class) {
if (instance == null) {
instance = createExtClassLoader();
}
}
}
return instance;
}
//创建ExtClassLoader
private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
try {
return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
public Launcher.ExtClassLoader run() throws IOException {
//获取ExtClassLoader加载器加载路径下的所有class文件
File[] var1 = Launcher.ExtClassLoader.getExtDirs();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
MetaIndex.registerDirectory(var1[var3]);
}
//这里是真正生成ExtClassLoader的地方
return new Launcher.ExtClassLoader(var1);
}
});
} catch (PrivilegedActionException var1) {
throw (IOException)var1.getException();
}
}
void addExtURL(URL var1) {
super.addURL(var1);
}
//ExtClassLoader的构造函数,通过跟踪父类源码可以看到第二个参数即为ClassLoader中的parent属性,这里为空,说明ExtClassLoader的父类加载器为空
public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
//加载指定路径的class文件
private static File[] getExtDirs() {
//可以看到ExtClassLoader加载路径为java.ext.dirs
String var0 = System.getProperty("java.ext.dirs");
File[] var1;
if (var0 != null) {
StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
int var3 = var2.countTokens();
var1 = new File[var3];
for(int var4 = 0; var4 < var3; ++var4) {
var1[var4] = new File(var2.nextToken());
}
} else {
var1 = new File[0];
}
return var1;
}
private static URL[] getExtURLs(File[] var0) throws IOException {
Vector var1 = new Vector();
for(int var2 = 0; var2 < var0.length; ++var2) {
String[] var3 = var0[var2].list();
if (var3 != null) {
for(int var4 = 0; var4 < var3.length; ++var4) {
if (!var3[var4].equals("meta-index")) {
File var5 = new File(var0[var2], var3[var4]);
var1.add(Launcher.getFileURL(var5));
}
}
}
}
URL[] var6 = new URL[var1.size()];
var1.copyInto(var6);
return var6;
}
}
static class AppClassLoader extends URLClassLoader {
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
//获取AppClassLoader 类加载器
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
//可以看到加载路径为java.class.path
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
//AppClassLoader构造器,var2为他的父类加载器,在Launcher初始化AppClassLoader时可以看到var2为Extension ClassLoader
AppClassLoader(URL[] var1, ClassLoader var2) {
super(var1, var2, Launcher.factory);
this.ucp.initLookupCache(this);
}
//重写了ClassLoader的loadClass方法,加载当前目录的class
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);
}
}
}
ExtClassLoader和AppClassLoader均为sun.misc.Launcher.class中的两个静态内部类,初始化均在Launcher中实现。源码如下:
public Launcher() {
Launcher.ExtClassLoader var1;
try {
//先初始化ExtClassLoader
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//然后初始化AppClassLoader,并将ExtClassLoader实例传入进去作为AppClassLoader的parent加载器,与上文AppClassLoader源码对应
//初始化AppClassLoader完成,并将this.loader指向它,这样每次加载都会从AppClassLoader开始
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
//线程上下文加载器
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
;
} catch (InstantiationException var6) {
;
} catch (ClassNotFoundException var7) {
;
} catch (ClassCastException var8) {
;
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
类加载器一共有四种,其中前三种都是系统定义好的,最后一种用户自定义类加载器就是我们需要自己实现的。
实现自定义类加载器其实很简单,我们阅读ClassLoader的源码的loadClass方法就会看到,真正加载的方法是findClass方法。我们可以看一下这个方法,没有具体实现,只抛了一个异常,而且是protected的,这充分证明了:这个方法就是给开发者重写用的。源码如下:
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
自己实现只需要三步。
(1)继承ClassLoader (2)重写findClass()方法 (3)调用defineClass()方法
代码如下:
import java.io.*;
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println("---------------------");
System.out.println(System.getProperty("java.ext.dirs"));//输出ExtClassLoader的加载目录
System.out.println(System.getProperty("java.class.path"));//输出AppClassLoader的加载目录
System.out.println(ClassLoader.getSystemClassLoader());//输出AppClassLoader
System.out.println(ClassLoader.getSystemClassLoader().getParent());//输出AppClassLoader的父类加载器ExtClassLoader
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());//此处为空,证明ExtClassLoader的父类加载器为空
System.out.println("---------------------");
MyClassLoader myClassLoader = new MyClassLoader("E:\\project\\common\\util\\target\\classes");
try {
Class c = myClassLoader.findClass("com.kyw.common.domain.SelectDomain");
if(c!=null){
Object obj=c.newInstance();
System.out.println(obj.toString());
System.out.println(c.getClassLoader().toString());
}
System.out.println("---------------------");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("找不到类");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
class MyClassLoader extends ClassLoader
{
private String classPath ;
public MyClassLoader() { }
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
public MyClassLoader(ClassLoader parent)
{
super(parent);
}
//重写findClass方法
protected Class<?> findClass(String name) throws ClassNotFoundException
{
try {
byte[] bytes = getClassBytes(name);
//defineClass方法将字节码转化为类
Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
return c;
}
catch (Exception e)
{
e.printStackTrace();
}
return super.findClass(name);
}
//获取类的字节码
private byte[] getClassBytes(String className) throws Exception
{
InputStream in = null;
ByteArrayOutputStream out = null;
try {
String path=classPath + File.separatorChar +
className.replace('.',File.separatorChar)+".class";
File file = new File(path);
// 这里要读入.class的字节,因此要使用字节流
in = new FileInputStream(file);
out = new ByteArrayOutputStream();
byte[] buffer=new byte[2048];
int len=0;
while((len=in.read(buffer))!=-1){
out.write(buffer,0,len);
}
return out.toByteArray();
}catch (FileNotFoundException e) {
e.printStackTrace();
}
finally{
in.close();
out.close();
}
return null;
}
}
输出结果为:
---------------------
C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\project\common\util\target\test-classes;E:\project\common\util\target\classes;E:\localRepository\junit\junit\4.11\junit-4.11.jar;E:\localRepository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;E:\localRepository\redis\clients\jedis\2.7.3\jedis-2.7.3.jar;E:\localRepository\org\apache\commons\commons-pool2\2.3\commons-pool2-2.3.jar;E:\localRepository\org\springframework\spring-beans\4.0.5.RELEASE\spring-beans-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-context\4.0.5.RELEASE\spring-context-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-aop\4.0.5.RELEASE\spring-aop-4.0.5.RELEASE.jar;E:\localRepository\aopalliance\aopalliance\1.0\aopalliance-1.0.jar;E:\localRepository\log4j\log4j\1.2.17\log4j-1.2.17.jar;E:\localRepository\org\slf4j\slf4j-api\1.7.2\slf4j-api-1.7.2.jar;E:\localRepository\org\slf4j\slf4j-log4j12\1.7.2\slf4j-log4j12-1.7.2.jar;E:\localRepository\com\fasterxml\jackson\core\jackson-core\2.4.3\jackson-core-2.4.3.jar;E:\localRepository\com\fasterxml\jackson\core\jackson-annotations\2.4.3\jackson-annotations-2.4.3.jar;E:\localRepository\com\fasterxml\jackson\core\jackson-databind\2.4.3\jackson-databind-2.4.3.jar;E:\localRepository\mysql\mysql-connector-java\8.0.12\mysql-connector-java-8.0.12.jar;E:\localRepository\com\google\protobuf\protobuf-java\2.6.0\protobuf-java-2.6.0.jar;E:\localRepository\org\springframework\spring-core\4.0.5.RELEASE\spring-core-4.0.5.RELEASE.jar;E:\localRepository\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar;E:\localRepository\org\springframework\spring-tx\4.0.5.RELEASE\spring-tx-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-jdbc\4.0.5.RELEASE\spring-jdbc-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-context-support\4.0.5.RELEASE\spring-context-support-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-web\4.0.5.RELEASE\spring-web-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-webmvc\4.0.5.RELEASE\spring-webmvc-4.0.5.RELEASE.jar;E:\localRepository\org\springframework\spring-expression\4.0.5.RELEASE\spring-expression-4.0.5.RELEASE.jar;E:\localRepository\javax\servlet\javax.servlet-api\3.1.0\javax.servlet-api-3.1.0.jar;E:\localRepository\org\mybatis\mybatis\3.3.1\mybatis-3.3.1.jar;E:\localRepository\org\mybatis\mybatis-spring\1.2.5\mybatis-spring-1.2.5.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar;C:\Users\Administrator\.IntelliJIdea2018.2\system\captureAgent\debugger-agent.jar
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@b065c63
null
---------------------
com.kyw.common.domain.SelectDomain@5479e3f
com.kyw.MyClassLoader@490d6c15
---------------------
注意类加载器的体系并不是“继承”体系,而是一个“委派”体系。大多数类加载器首先会到自己的parent中查找类或者资源,如果找不到,才会在自己 的本地进行查找。事实上,类加载器被定义加载哪些在parent中无法加载到的类,这样在较高层级的类加载器上的类型能够被“赋值”为较低类加载器加载的类型。