自定义类加载器ClassLoader

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();
            }
        }
    }
}
发布了127 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_33321609/article/details/103957050