1. 自定义类加载器要求
实现目标:自定义一个类加载器,用来加载指定目录下的class文件。
2. 代码实现
2.1 自定义类加载器
package cn.ordinary.test.cl;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
/**
* 自定义类加载器:用来加载指定目录下的类
*
* @author sh
**/
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
/**
* 自定义类加载器,继承 java.lang.ClassLoader 并重写 findClass() 方法 即可。
* 这个类并没有重写 loadClass()方法,也就是说,并没有破坏 java默认的类加载机制(双亲委派模型)。
*
* @param name class文件的全限定名(包名 + 类名)
* @return java.lang.Class<?>
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] classData = this.loadClassData(name);
// 调用 defineClass()方法 将 二进制字节码 转换为 Class对象。
return super.defineClass(name, classData, 0, classData.length);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
}
/**
* 读取 class文件,将其转换为 二进制字节码
*
* @param className class文件的全限定名(包名 + 类名)
* @return byte[] 返回二进制字节码
*/
private byte[] loadClassData(String className) throws IOException {
// 将包结构转换为文件路径结构
className = className.replace('.', File.separatorChar).concat(".class");
// 从 文件系统 中读取 类的字节码
return Files.toByteArray(new File(classPath, className));
}
}
2.2 如何使用自定义类加载器
package cn.ordinary.test.cl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Date 2024/3/12
* @Author sh
**/
public class ClassLoaderTest {
public static void main(String[] args) {
// 这里的目录由使用者自己去决定
String classPath = "D:/99-test/cl";
String className = "cn.o.Emperor";
// 创建自定义类加载器
MyClassLoader myClassLoader = new MyClassLoader(classPath);
try {
/**
* 使用 自定义类加载器 加载 指定目录下的 class文件。
*
* 注意:这里调用的是 loadClass()方法,也就是说,这里也同样遵循了 java默认的类加载机制(双亲委派模型)。
*/
Class<?> clazz = myClassLoader.loadClass(className);
System.out.println("加载[" + className + "]类的类加载器是: " + clazz.getClassLoader());
// 通过反射创建对象,调用方法等
Object instance = clazz.newInstance();
Method sayMethod = clazz.getMethod("say", String.class);
sayMethod.invoke(instance, "秦始皇");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
cn.o.Emperor类如下:我们需要使用 javac 编译该 Emperor.java 文件,将其编译成 Emperor.class文件。
package cn.o;
/**
* @Date 2024/3/13
* @Author sh
**/
public class Emperor {
public static void say(String name) {
System.out.println(name + ": “额滴额滴,都是额滴!”。");
}
}
javac命令如下:
javac -encoding utf8 Emperor
执行ClassLoaderTest类,控制台输入内容如下:
加载[cn.o.Emperor]类的类加载器是: cn.ordinary.test.cl.MyClassLoader@de0a01f
秦始皇: “额滴额滴,都是额滴!”。