1、首先,什么是类加载器:负责动态加载Java类到Java虚拟机的内存空间中。
2、作用:众所周知,我们写完的代码,大多数都是.java结尾的java文件,打开target文件,你会看到一个个class结尾的class文件,这些都是java文件编译后的。而类加载器的作用就是把这些class文件进行加载,获取一个个可以运行的类。
自定义类加载器GZYClassLoader
使用了自定义加密工具,使编译出来的class类不能被默认的类加载器加载(把加密解密的代码都去掉,加载器也能运行,只是这样,默认的加载器也能加载,实现不了加密效果。ps:可以自己写加密代码,也可以问私信我,我的加密也很简单)
代码
Test类:
import java.lang.reflect.Method;
public class GZYClassLoaderTest {
// 定义一个主方法
public static void main(String[] args) throws Exception {
//测试类的路径
String javaName = "D:/work.workspace.TestIdea.src.classloader.Hello";
String className = "D:/work.workspace.TestIdea.target.classes/classloader.Hello";
//进行自定义的编译
GZYClassLoader.writeClass(javaName, className, true);
//测试自定义类加载
doClass(className, new String[]{"123", "456"});
}
public static void doClass(String className, String[] args) throws Exception {
GZYClassLoader ccl = new GZYClassLoader();
// 加载需要运行的类
Class<?> clazz = ccl.loadClass(className);
System.out.println(clazz.getClassLoader());
// 获取需要运行的类的方法
Method main = clazz.getMethod("main", (new String[0]).getClass());
Object[] argsArray = {args};
//传参
main.invoke(null, argsArray);
}
}
GZYClassLoader类
import java.io.*;
public class GZYClassLoader extends ClassLoader {
// 读取一个文件的内容
private byte[] getBytes(String filename)
throws IOException {
File file = new File(filename);
long len = file.length();
byte[] raw = new byte[(int) len];
try (
FileInputStream fin = new FileInputStream(file)) {
// 一次读取class文件的全部二进制数据
int r = fin.read(raw);
//如果class没有加密,下一行可以去掉
raw = Mi.jieMi(raw, 7, 7);
if (r != len)
throw new IOException("无法读取全部文件:"
+ r + " != " + len);
return raw;
}
}
// 定义编译指定Java文件的方法
private static boolean compile(String javaFilename, String classFilename)
throws IOException {
System.out.println("CompileClassLoader:正在编译 "
+ javaFilename + ".java...");
// 调用系统的javac命令
Process p = Runtime.getRuntime().exec("javac -encoding utf8 " + javaFilename + ".java");
try {
// 其他线程都等待这个线程完成
p.waitFor();
} catch (InterruptedException ie) {
System.out.println(ie);
}
//下面是进行拷贝操作
if (!classFilename.equals(javaFilename + ".class")) {
File javaFile = new File(javaFilename + ".class");
File classFile = new File(classFilename);
try (BufferedInputStream inBuff = new BufferedInputStream(new FileInputStream(javaFile));
// 新建文件输出流并对它进行缓冲
BufferedOutputStream outBuff = new BufferedOutputStream(new FileOutputStream(classFile));) {
// 缓冲数组
byte[] b = new byte[(int) javaFile.length()];
int len;
while ((len = inBuff.read(b)) != -1) {
//加密操作,可以删除
b = Mi.jiaMi(b, 7, 7);
outBuff.write(b, 0, len);
}
// 刷新此缓冲的输出流
outBuff.flush();
}
}
// 获取javac线程的退出值
int ret = p.exitValue();
// 返回编译是否成功
return ret == 0;
}
// 重写ClassLoader的findClass方法
protected Class<?> findClass(String name)
throws ClassNotFoundException {
Class clazz = null;
// 将包路径中的点(.)替换成斜线(/)。
String[] names = name.split("/");
//className必须是包内路径
String className = names[names.length - 1];
String fileStub = name.replace(".", "/");
String classFilename = fileStub + ".class";
File classFile = new File(classFilename);
// 如果class文件存在,系统负责将该文件转换成Class对象
if (classFile.exists()) {
try {
// 将class文件的二进制数据读入数组
byte[] raw = getBytes(classFilename);
System.out.println("使用了自定义的----");
// 调用ClassLoader的defineClass方法将二进制数据转换成Class对象
clazz = defineClass(className, raw, 0, raw.length);
} catch (IOException ie) {
ie.printStackTrace();
}
}
// 如果clazz为null,表明加载失败,则抛出异常
if (clazz == null) {
throw new ClassNotFoundException(name);
}
return clazz;
}
/**
* @param javaName
* @param className
* @param must 是否一定要重新编译
* @throws Exception
*/
public static void writeClass(String javaName, String className, boolean must) throws Exception {
Class clazz = null;
// 将包路径中的点(.)替换成斜线(/)。
String javaStub = javaName.replace(".", "/");
String classStub = className.replace(".", "/");
String javaFilename = javaStub + ".java";
String classFilename = classStub + ".class";
File javaFile = new File(javaFilename);
File classFile = new File(classFilename);
if (!javaFile.exists()) {
throw new Exception("找不到指定java类");
}
// 当指定Java源文件存在,且class文件不存在、或者Java源文件
// 的修改时间比class文件修改时间更晚,重新编译
if (javaFile.exists() && (!classFile.exists()
|| javaFile.lastModified() > classFile.lastModified() || must)) {
try {
// 如果编译失败,或者该Class文件不存在
if (!compile(javaStub, classFilename) || !classFile.exists()) {
throw new ClassNotFoundException(
"ClassNotFoundExcetpion:" + javaFilename);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}