类加载器这个名字自始至终一直接触,今天趁着公司空调记录下吧。
一,首先是JVM的类加载器,包含以下三种:
(1):BootStrap 主要加载%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar等。用 System.getProperty("sun.boot.class.path")查看加载类文件的路径;
(2):Extention ClassLoader:主要加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。也可以通过System.getProperty("java.ext.dirs")查看加载类文件的路径。
(3) AppClassLoader:主要加载当前应用下的classpath路径下的类。
其中,这是三个的关系为:AppClassLoder继承URLClassLoader,而URLClassLoader继承ClassLoader,BoopStrap ClassLoder,因为它是由C/C++编写的,它本身是虚拟机的一部分。
二:JVM加载类的规则如下:
(1)首先会到自定义加载器中查找,看是否已经加载过,如果已经加载过,则返回字节码。
(2)如果自定义加载器没有加载过,则询问上一层加载器(即AppClassLoader)是否已经加载过Test.class。
(3)如果没有加载过,则询问上一层加载器(ExtClassLoader)是否已经加载过。
(4)如果没有加载过,则继续询问上一层加载(BoopStrap ClassLoader)是否已经加载过。
(5)如果BoopStrap ClassLoader依然没有加载过,则到自己指定类加载路径下("sun.boot.class.path")查看是否有Test.class字节码,有则返回,没有通知下一层加载器ExtClassLoader到自己指定的类加载路径下(java.ext.dirs)查看。
(6)依次类推,最后到自定义类加载器指定的路径还没有找到Test.class字节码,则抛出异常ClassNotFoundException.
三:自定义类加载器实战
(1)在指定一个目录,用javac生产加载类的class文件,本文的类(com.test.Test)为如下:
package com.test;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
public String dateToStr(Date date) {
if (date == null) {
return "";
}
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
}
}
(2) 代码中自定义自己的类加载器,继承ClassLoader,其中重写其findClass()方法:如下:
public class MyClassLoader extends ClassLoader {
private String classpath;
public MyClassLoader(String classpath) {
this.classpath = classpath;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
byte[] bytes = getBytesCodeFormPath(name);
if (bytes != null) {
return super.defineClass(name, bytes, 0, bytes.length);
}
return super.findClass(name);
}
private byte[] getBytesCodeFormPath(String className) {
String path = classpath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
try (InputStream in = new FileInputStream(path)) {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
byte[] buffers = new byte[2048];
int len = 0;
while ((len = in.read(buffers)) != -1) {
out.write(buffers, 0, len);
}
return out.toByteArray();
}
} catch (Exception ex) {
//NOOP
}
return null;
}
(3)通过自定义的类加载器,加载类,然后获得Test对象,然后反射调用器方法:
public static void main(String[] args) throws Exception {
MyClassLoader myClassLoader = new MyClassLoader("D:\\file");
Class clazz = myClassLoader.loadClass("com.test.Test");
if (clazz != null) {
Object obj = clazz.newInstance();
Method method = clazz.getMethod("dateToStr", new Class[]{Date.class});
String dateStr = (String) method.invoke(obj, new Date());
System.out.println(dateStr);
System.out.println(clazz.getClassLoader().toString());
}
}
(4)验证输入结果:
2018-08-04 16:33:49
com.XXXX.com20180719.classloader.MyClassLoader@ae4cf81