自定义类加载器
2020-09-20 本文已影响0人
M_lear
相关类:ClassLoader
相关方法:findClass;defineClass(将字节数组变成Class对象);loadClass;
【双亲委派模型】使用模板模式实现在 loadClass 方法中。
loadClass 的源码:
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;
}
}
自定义类加载器两种实现:
- 遵守双亲委派模型:继承 ClassLoader,重写 findClass 方法。
- 不遵守双亲委派模型:继承 ClassLoader,重写 loadClass 方法。
一、遵守双亲委派模型
1.1 自定义类加载器
代码:
package classloader;
import java.io.*;
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String path) {
classPath = path;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
// 参数 name 为类的全限定名,作为 defineClass 方法的参数
// 第一步:将 class 文件读入字节数组
FileInputStream in = null;
ByteArrayOutputStream out = null;
byte[] buffer = new byte[1024];
int len;
try {
in = new FileInputStream(classPath);
out = new ByteArrayOutputStream();
while((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] classByteArray = out.toByteArray();
// 第二步:调用 defineClass 将字节数组变成 Class 对象
return this.defineClass(name, classByteArray, 0, classByteArray.length);
}
}
1.2 测试
待加载类:
package code;
public class Reverse{
public static void main(String[] args){
int i = 0, j = args.length-1;
while(i < j){
String temp = args[i];
args[i] = args[j];
args[j] = temp;
i++;
j--;
}
for(i = 0; i < args.length; ++i){
System.out.print(args[i]+" ");
}
}
}
实现了一个数组反转。
测试类:
package test;
import java.lang.reflect.InvocationTargetException;
import classloader.MyClassLoader;
public class Main{
public static void main(String[] args) throws Exception {
MyClassLoader mcl = new MyClassLoader("C:\\Users\\sunjian\\Desktop\\code\\Reverse.class");
Class<?> r = mcl.loadClass("code.Reverse");
System.out.println(r.getClassLoader());
System.out.println(r.getName());
r.getMethod("main", String[].class).invoke(null, new Object[] {new String[] {"1", "2", "3"}});
}
}
首先测试是否遵守双亲委派模型:
当前工程和桌面都有 Reverse.class。
测试结果:
image.png
可以看到,应用类加载器将当前工程下的 Reverse.class 加载进来。
符合预期,遵守了双亲委派模型。
删除当前工程下的 Reverse.class,看是否能加载桌面上的 Reverse.class。
运行结果:
image.png
当所有父加载器都加载失败后,我们的自定义类加载器起作用了,成功将桌面上的 Reverse.class 加载进来。
二、不遵守双亲委派模型
当我们需要同时把当前工程和桌面的 Reverse.class 都加载进内存时,就需要定义一个不遵守双亲委派模型的类加载器。
package classloader;
import java.io.*;
public class MyClassLoader1 extends ClassLoader {
private String classPath, className;
public MyClassLoader1(String path, String name) {
classPath = path;
className = name;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
// 依赖类调用 super 的方法加载
if(!name.equals(className)) return super.loadClass(name);
// 将 class 文件读入字节数组
FileInputStream in = null;
ByteArrayOutputStream out = null;
byte[] buffer = new byte[1024];
int len;
try {
in = new FileInputStream(classPath);
out = new ByteArrayOutputStream();
while((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] classByteArray = out.toByteArray();
// 调用 defineClass 将字节数组变成 Class 对象
return this.defineClass(name, classByteArray, 0, classByteArray.length);
}
}
测试类:
package test;
import classloader.MyClassLoader;
import classloader.MyClassLoader1;
public class Main{
public static void main(String[] args) throws Exception{
MyClassLoader mcl = new MyClassLoader("C:\\Users\\sunjian\\Desktop\\code\\Reverse.class");
Class<?> r = mcl.loadClass("code.Reverse");
System.out.println(r.getClassLoader());
System.out.println(r.getName());
r.getMethod("main", String[].class).invoke(null, new Object[] {new String[] {"1", "2", "3"}});
System.out.println();
System.out.println("--------------------------");
MyClassLoader1 mcl1 = new MyClassLoader1("C:\\Users\\sunjian\\Desktop\\code\\Reverse.class", "code.Reverse");
Class<?> r1 = mcl1.loadClass("code.Reverse");
System.out.println(r1.getClassLoader());
System.out.println(r1.getName());
r1.getMethod("main", String[].class).invoke(null, new Object[] {new String[] {"1", "2", "3"}});
System.out.println();
System.out.println("--------------------------");
System.out.println(r.newInstance() instanceof code.Reverse);
System.out.println(r1.newInstance() instanceof code.Reverse);
}
}
运行结果:
image.png