标签:类加载器
先看例子:
public class ClassLoaderTest{
public static void main(String[] args) {
ClassLoader cl = ClassLoaderTest.class.getClassLoader();
System.out.println(cl);
System.out.println(cl.getParent());
System.out.println(cl.getParent().getParent());
/**
* List是rt.jar包下
*/
System.out.println(List.class.getClassLoader());
}
}输出结果:
sun.misc.Launcher$AppClassLoader@6fd7bd04
sun.misc.Launcher$ExtClassLoader@3cba8af9
null
null
系统默认三个类加载器
BootStrap(根加载器):java核心库(rt.jar),C/C++写,故而打印出 null
ExtClassLoader(扩展加载器):ext包下
AppClassLoader:classpath下
类加载器的委托机制:
当Java虚拟机要加载第一个类的时候,到底派出哪个类加载器去加载呢?
(1). 首先当前线程的类加载器去加载线程中的第一个类(当前线程的类加载器:Thread类中有一个get/setContextClassLoader(ClassLoader cl);方法,可以获取/指定本线程中的类加载器)
(2). 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B
(3). 还可以直接调用ClassLoader.loadClass(String className)方法来指定某个类加载器去加载某个类
每个类加载器加载类时,又先委托给其上级类加载器当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则会抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild()方法。例如:如上图所示: MyClassLoader->AppClassLoader->ExtClassLoader->BootStrap.自定定义的MyClassLoader1首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrap,这时候BootStrap就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束。
在看一个例子 :
public class MyClassLoader extends ClassLoader{
protected MyClassLoader(ClassLoader parent) {
super(parent);
}
protected MyClassLoader() {
super();
}
}
public class ClassLoaderTest2 {
public static void main(String[] args) {
MyClassLoader ml1 = new MyClassLoader();
System.out.println(ml1.getParent());
MyClassLoader ml2 = new MyClassLoader(Thread.currentThread().getContextClassLoader().getParent());
System.out.println(ml2.getParent());
}
}打印结果:
sun.misc.Launcher$AppClassLoader@2ac510e3
sun.misc.Launcher$ExtClassLoader@6fd7bd04
说明自定义类加载器的父加载器默认是 :AppClassLoader
自定义的类加载器必须继承抽象类ClassLoader
一般只需 重写findClass方法,其实他内部还有一个loadClass方法和defineClass方法;
看loadClass源码:
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
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加载,如果找不到该类,
//抛ClassNotFoundException ,让子加载器加载,直到加载成功结束
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//如果自定义加载器parent为null,直接用根加载器加载
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.
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;
}
}
//该方法直接抛出异常,就是为了重写,所以自定义加载器只需要重写findClass,如果重写
// load方法,还需要重新写容器的委托逻辑, 没有必要
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}defineClass这个方法很简单就是将class文件的字节数组编程一个class对象,这个方法肯定不能重写,内部实现是在C/C++代码中实现的
自定义类加载器:
public class MyClassLoader extends ClassLoader{
private String base_dir;
protected MyClassLoader(ClassLoader parent) {
super(parent);
}
protected MyClassLoader() {
super();
}
public MyClassLoader(ClassLoader parent,String base_dir) {
super(parent);
this.base_dir = base_dir;
}
public MyClassLoader(String base_dir) {
super();
this.base_dir = base_dir;
}
//重写findClass
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String path = this.base_dir+"\\"+name+".class";
byte[] b = new byte[0];
try {
b = toByteArray(path);
} catch (IOException e) {
e.printStackTrace();
}
if(b.length == 0){
return null;
}
//定义class信息,把class文件字节码信息组装成jvm的class信息
return defineClass(null,b,0,b.length);
}
private byte[] toByteArray(String filename) throws IOException {
File f = new File(filename);
if (!f.exists()) {
throw new FileNotFoundException(filename);
}
ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());
BufferedInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(f));
int buf_size = 1024;
byte[] buffer = new byte[buf_size];
int len = 0;
while (-1 != (len = in.read(buffer, 0, buf_size))) {
bos.write(buffer, 0, len);
}
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
bos.close();
}
}
}
public class ClassLoaderTest3 {
public static void main(String[] args) {
MyClassLoader ml = new MyClassLoader("E:\\workspace\\mytest\\temp");
try {
Class helloClass = ml.loadClass("Hello");
Object hello = helloClass.newInstance();
System.out.println(hello.getClass().getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}打印结果:
classloader.MyClassLoader@19ad0dd8
这里要声明:很容易打印出 AppClassLoader,这是由于你本地idea创建的Hello.class文件可能存在 项目class目录下,删除,从其他目录引入 即可;
现在来测试下自定义的类加载器:
public class Hello {
public String sayHello(){
return "hello classLoader";
}
}
public class ClassLoaderTest3 {
public static void main(String[] args) {
//指定根路径,可以根据实际情况引入
MyClassLoader ml = new MyClassLoader(ClassLoader.getSystemClassLoader().getParent(),"E:\\workspace\\mytest\\temp");
try {
//加载
Class helloClass = ml.loadClass("Hello");
//初始化
Object hello = helloClass.newInstance();
//调用 加载类的方法
Object info = helloClass.getMethod("sayHello").invoke(hello);
System.out.println(info);
System.out.println(hello.getClass().getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}打印结果:
hello classLoader
classloader.MyClassLoader@19ad0dd8
本文出自 “11898338” 博客,谢绝转载!
标签:类加载器
原文地址:http://11908338.blog.51cto.com/11898338/1922012