JAVA通过自定义类加载器加载外部目标类
双亲委派模型下类加载流程
流程示意图,具体源码如下:
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 {//父类加载器优先在自己管辖范围内寻找并加载目标名称类
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
}
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;
}
}
双亲委派模型好处就是保证了JAVA的核心类不会被用户自己定义的同名类所覆盖!所以根据规则我们自定义的类加载器只需要继承Classloader类并重写findClass()方法
自定义类加载器
import java.io.*;
public class MyCalssLoader extends ClassLoader {
private String classPath;
public MyCalssLoader(String classPath) {
this.classPath = classPath;
}
/**
* 通过文件字节流的方式将类的字节码文件读入程序并转换成字节数组
* @return
* @throws IOException
*/
private byte[] getClassByteArray() throws IOException {//class文件读入
File file=new File(classPath);
if (file.exists()){
FileInputStream inputStream=null;
ByteArrayOutputStream outputStream=null;
byte[] classArray=new byte[66];
try {
inputStream=new FileInputStream(file);
outputStream=new ByteArrayOutputStream();
int size=0;
while ((size=inputStream.read(classArray))!=-1)outputStream.write(classArray,0,size);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
inputStream.close();
}
return outputStream.toByteArray();
}else {
System.out.println(
"类字节码不存在"
);
return null;
}
}
/**
*通过将getClassByteArray()得到的字节数组还原为目标类以供类加载器的loadClass()方法将类加载入JVM中
* @param name 类的字节码文件名 !注意包含包名但不带后缀名
* @return 返回目标类
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class target=null;
try {
byte[] classArray=getClassByteArray();
if(classArray!=null){
target= defineClass(name,classArray,0,classArray.length);
}
} catch (IOException e) {
e.printStackTrace();
}
return target;
}
}
测试
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;
public class RunTest {
public static void main(String[] args) {
String path="C:\\Users\\张永新\\Desktop\\people.class";
MyCalssLoader myCalssLoader=new MyCalssLoader(path);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ClassLoadingMXBean mxBean= ManagementFactory.getClassLoadingMXBean();//ManagementFactory是一个为我们提供各种获取JVM信息的工厂类
System.out.println(mxBean.getTotalLoadedClassCount());//获取当前JVM自本次启动后的历史类加载总数
try {
Class t=myCalssLoader.loadClass("people");
System.out.println("加载到了"+t);
System.out.println(mxBean.getTotalLoadedClassCount());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
执行结果
加载成功