自定义类加载器

The default implementation of this method searches for classes in the following order:

  1. Invoke findLoadedClass(String) to check if the class has already been loaded.
  2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
  3. Invoke the findClass(String) method to find the class.

稍微理解下:

  1. 首先调用findLoadedClass(String),如果被加载过直接返回true,没有执行下一个步骤
  2. 调用父级类加载器的loaderClass方法,如果父级类加载器为空,就使用JVM默认的类加载器
  3. 调用findClass方法

这个是模版方法设计模式的运用。

编写自定义类加载器

需要继承ClassLoader类,重写findClass方法。

package lorenzo;
public class MyClassLoader extends ClassLoader {

    private String rootDir;

    public MyClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    private byte[] getClassData(String className) {
        String path = classNameToPath(className);
        try {
            InputStream ins = new FileInputStream(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int bufferSize = 4096;
            byte[] buffer = new byte[bufferSize];
            int bytesNumRead = 0;
            while ((bytesNumRead = ins.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesNumRead);
            }

            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    private String classNameToPath(String className) {
        return rootDir + File.separatorChar
                + className.replace('.', File.separatorChar) + ".class";
    }

    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        System.out.println("自定义类加载器");
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }
}
编写需要加载的类
package lorenzo;
public class MyClass {
    @Override
    public String toString() {
        return "Hello Lorenzo!";
    }
}
编写测试类
package lorenzo;
public class Client {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader myClassLoader = new MyClassLoader("/data");
        Class clazz = myClassLoader.loadClass("lorenzo.MyClass");
        System.out.println(clazz.newInstance().toString());
        System.out.println(myClassLoader.getClass().getClassLoader());
    }
}

注意:以上三个类都在/data/lorenzo文件夹下面
mac控制台输入:

cd /data/lorenzo
javac MyClassLoader.java
javac MyClass.java
javac Client.java
cd ..
java ava lorenzo/Client

output:

Hello Lorenzo!
sun.misc.Launcher$AppClassLoader@2a139a55

分析结果:
根据类加载器树形图可知

自定义类加载器_第1张图片
类加载器树.png

MyClassLoader.java和Client.java以及MyClass.java都在classpath目录下,他们由AppClassLoader类加载器加载。根据loadClass(String)的模版方法可知,MyClassLoader的父级类加载器AppClassLoader可以加载binary name 为lorenzo.MyClass的类,程序不会执行到loadClass的第三步,所以是AppClassLoader而不是MyClassLoader加载MyClass。

修改代码

把MyClass.class移动到data1/lorenzo的文件夹下
修改Client中代码:

  MyClassLoader myClassLoader = new MyClassLoader("/data");

修改后的代码为

MyClassLoader myClassLoader = new MyClassLoader("/data1");

mac控制台输入:

cd /data/lorenzo
javac Client.java
cd ..
java lorenzo/Client

output:

自定义类加载器
Hello Lorenzo!
sun.misc.Launcher$AppClassLoader@2a139a55

注意确保/data/lorenzo目录没有MyClass.class。

把自定义的类加载器挂载到ExtClassLoader
  1. 把MyClassLoader.class打成jar包

cd /data
jar cvf lorenzo.jar lorenzo/MyClassLoader.class

  1. 把lorenzo.jar复制到../jre/lib/ext/目录下

java lorenzo/Client

output:

自定义类加载器
Hello Lorenzo!
sun.misc.Launcher$ExtClassLoader@75b84c92

写在最后

加密的类加载器思路,首先编译生成class文件,加密class文件,在自定义的类加载器解密。

你可能感兴趣的:(自定义类加载器)