首页 > 技术文章 > 自定义类加载器和父委托机制

zheaven 2020-01-16 14:21 原文

自定义类加载器MyClassLoader

package com.dwz.classLoader.chapter2;

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

public class MyClassLoader extends ClassLoader{
    private final static String DEFAULT_DIR = "C:\\Users\\Administrator\\Desktop\\classloader1";
    
    private String dir = DEFAULT_DIR;
    
    private String classLoaderName;

    public MyClassLoader() {
        super();
    }
    
    public MyClassLoader(String classLoaderName) {
        super();
        this.classLoaderName = classLoaderName;
    }

    public MyClassLoader(String classLoaderName, ClassLoader parent) {
        //父委托
        super(parent);
        this.classLoaderName = classLoaderName;
    }
    
    /**
     * @param name (xxx.xxx.xxx.xxx)
     *     xxx/xxx/xxx/xxx.class
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String classPath = name.replaceAll("\\.", "/");
        File classFile = new File(dir, (classPath + ".class"));
        if(!classFile.exists()) {
            throw new ClassNotFoundException("The class " + name + " not found under " + dir);
        }
        
        byte[] classBytes = loadClassBytes(classFile);
        if(null == classBytes || classBytes.length == 0) {
            throw new ClassNotFoundException("load the class " + name + " failed");
        }
        
        return this.defineClass(name, classBytes, 0, classBytes.length);
    }
    
    private byte[] loadClassBytes(File classFile) {
        try(ByteArrayOutputStream baos = new ByteArrayOutputStream();
                FileInputStream fis = new FileInputStream(classFile)) {
            byte[] buffer = new byte[1024];
            int len;
            while((len = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            baos.flush();
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public String getDir() {
        return dir;
    }

    public void setDir(String dir) {
        this.dir = dir;
    }

    public String getClassLoaderName() {
        return classLoaderName;
    }
}

需要加载的目标类

package com.dwz.classLoader.chapter2;

public class MyObject {
    static {
        System.out.println("My Object static block.");
    }
    
    public String hello() {
        return "Hello World!";
    }
}

测试一:

1.类加载器的委托是优先交给父加载器先去尝试加载
2.父加载器和子加载器其实是一种包装关系,或者包含关系

     MyClassLoader classLoader1 = new MyClassLoader("MyClassLoader-1");
        Class<?> loadClass = classLoader1.loadClass("com.dwz.classLoader.chapter2.MyObject");
        System.out.println(loadClass);
        System.out.println(loadClass.getClassLoader());
        
        Object obj = loadClass.newInstance();
        Method method = loadClass.getMethod("hello", new Class<?>[] {});
        Object result = method.invoke(obj, new Object[] {});
        System.out.println(result);

1.先将自动编译好的MyObject.class复制到我们自定义的类加载器文件 DEFAULT_DIR 中

2.删除自动编译好的MyObject.class文件

测试二:

package com.dwz.classLoader.chapter2;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 *    1.类加载器的委托是优先交给父加载器先去尝试加载
 *    2.父加载器和子加载器其实是一种包装关系,或者包含关系
 */
public class MyClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        MyClassLoader classLoader1 = new MyClassLoader("MyClassLoader-1");
        MyClassLoader classLoader2 = new MyClassLoader("MyClassLoader-2", classLoader1);
        Class<?> loadClass = classLoader2.loadClass("com.dwz.classLoader.chapter2.MyObject");
        System.out.println(loadClass);
        System.out.println(((MyClassLoader)loadClass.getClassLoader()).getClassLoaderName());
    }
}

父委托机制:
1.定义类加载器
2.初始化类加载器
3.父子类加载器之间的真实关系--包装关系(包含关系)
4.父委托机制的优点是能够提高系统的安全性,在此机制下,用户自定义的类加载器不可能加载应该有父加载器加载的可靠类,
因此可以防止恶意的代码代替父加载器的可靠代码

推荐阅读