Java自定义类加载器的编写步骤

Java自定义类加载器的编写步骤

两个问题

  1. 为什么要使用自定义类加载器呢?

    Java的class很容易就被反编译,那么我们需要做加密,那么我们加载这个类的时候就需要用到自定义加载器。并且如果需要加载的类不在classPath下,而是在硬盘其他地方或者是网络上,那么同样也需要用到自定也需要用到自定义类加载器

  2. 什么情况下使用自定义类加载器?

    1. 加密:Java代码容易被反编译,那么不想要人家看到源代码的时候就需要进行加密。加密之后自带的类加载器不能使用,那么就需要使用到自定义类加载器
    2. 非标准来源加载代码:比如硬盘其他路径上的class文件,或者网络传过来的class文件

如何自定义类加载器

我们先来看 sun.misc.Launcher 类中的源码,以 AppClassloader 为例,首先人家继承了 java.net.URLClassLoader 并且实现了加载类的 loadClass 方法,如图

AppClassLoader

然而我们查看 java.net.URLClassLoader 时我们可以看到,最终人家继承自 java.lang.ClassLoader,如:

public class URLClassLoader extends SecureClassLoader implements Closeable // 继承自 SecureClassLoader
public class SecureClassLoader extends ClassLoader // 然后 SecureClassLoader 继承自 ClassLoader

注意看 AppClassloader 中的 loadClass 人家在调用父类的 loadClass 方法,父类的 loadClass 方法中调用了 findClass 方法

调用findClass方法

findClass 方法会抛出一个没有找到类的异常,并表示需要子类从写这个方法

需要子类从写findClass方法

那么我们根据这个思路新增一个自定义类加载器并继承 java.lang.ClassLoader

package com.xiaohh.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * 自定义类加载器
 */
public class XiaoHHClassLoader extends ClassLoader {

    /**
     * 查找 class
     * @param name class 的名字
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // Class 类的存放路径
        String resource = "C:/test/";

        // 按照路径找到需要加载的 class 文件
        File source = new File(resource, name.replace('.', '/').concat(".class"));
        // 判断文件是否存在
        if (!source.exists()) {
            throw new ClassNotFoundException(name + "is not found. ");
        }

        FileInputStream fis = null;

        try {
            // 将文件以流的方式加载到内存中
            fis = new FileInputStream(source);

            // 将文件转换为字节数组
            byte[] bytes = new byte[fis.available()];
            fis.read(bytes);

            // 定义类并返回,此步骤没有抛出异常表示完成加载
            return super.defineClass(name, bytes, 0, bytes.length);
        } catch (Exception e) {
            // 处理方式可以更加严谨,请自行处理
            throw new ClassNotFoundException(name + "is not found. ");
        } finally {
            // 关闭资源
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

这段代码定义了一个名为 XiaoHHClassLoader 的自定义类加载器,我们注意一下这段代码

return super.defineClass(name, bytes, 0, bytes.length);

这段代码调用了 ClassLoader 中的 defineClass 方法,作用是将字节数组解析成一个 Class 实例

在这里插入图片描述

到此为止我们的自定义类加载器就编写完成了,我们现在编写一个类来测试一下

package com.xiaohh.customer;

public class Hello {
    public void sayHello() {
        System.out.println("Hello");
    }
}

将这个文件编译之后放在 C:\test\com\xiaohh\customer 的文件夹中

测试的Class文件

注意在自己的项目中不要出现这个class!!!

项目中不要出现这个

然后我们写一个测试方法

package com.xiaohh.test;

import java.lang.reflect.InvocationTargetException;

public class TestMain {
    public static void main(String[] args)
            throws ClassNotFoundException, IllegalAccessException,
            InstantiationException, InvocationTargetException, NoSuchMethodException {
        // 获取自定的类加载器
        ClassLoader loader = new XiaoHHClassLoader();

        // 加载硬盘上的 class 文件
        Class<?> helloClass = loader.loadClass("com.xiaohh.customer.Hello");

        // 获得这个类的对象,因为只有一个默认的无参构造
        Object hello = helloClass.getConstructors()[0].newInstance();

        // 查看类的信息
        System.out.println("Class name: " + hello.getClass().getName());
        System.out.println("Class loader: " + hello.getClass().getClassLoader()); // 查看使用的是哪个类加载器

        // 调用这个类的方法
        // helloClass.getMethods()[0].invoke(hello);
        helloClass.getMethod("sayHello").invoke(hello);
    }
}

然后查看测试结果

方法调用成功

可以看到编写成功了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值