自定义类加载器

自定义类加载器一般步骤大概为

1. 继承 ClassLoader 类

2. 复写 findClass方法

在findClass方法中传入一个String类型参数

一般为类所在的路径可以为网络url,或者本地路径。加上类的名称。

先检查类所在的包是否存在getPackage方法

如果不存在通过父类的definePackage方法定义包

通过路径找到类所在位置,再将对应的类l加载到内存中转换为byte数组

再通过父类的 defineClass生成一个类的对象返回

protected Class findClass(String name) throws ClassNotFoundException {
        byte[] classfile;
        try {
            if (this.source != null) {
                if (this.translator != null) {
                    this.translator.onLoad(this.source, name);
                }

                try {
                    classfile = this.source.get(name).toBytecode();
                } catch (NotFoundException var7) {
                    return null;
                }
            } else {
                String jarname = "/" + name.replace('.', '/') + ".class";
                InputStream in = this.getClass().getResourceAsStream(jarname);
                if (in == null) {
                    return null;
                }

                classfile = ClassPoolTail.readStream(in);
            }
        } catch (Exception var8) {
            throw new ClassNotFoundException("caught an exception while obtaining a class file for " + name, var8);
        }

        int i = name.lastIndexOf(46);
        if (i != -1) {
            String pname = name.substring(0, i);
            if (this.getPackage(pname) == null) {
                try {
                    this.definePackage(pname, (String)null, (String)null, (String)null, (String)null, (String)null, (String)null, (URL)null);
                } catch (IllegalArgumentException var6) {
                    ;
                }
            }
        }

        return this.domain == null ? this.defineClass(name, classfile, 0, classfile.length) : this.defineClass(name, classfile, 0, classfile.length, this.domain);
    }

3. 还可复写loadClass方法

一般先通过 findLoadedClass确定类是否已经被加载过,加载过则可以不用重新加载

还可以将一些类交给父加载器加载,比如jdk自己的类

还可调用父类的resolveClass方法,对类进行解析,这个没大看懂

protected Class loadClass(String name, boolean resolve) throws ClassFormatError, ClassNotFoundException {
        name = name.intern();
        synchronized(name) {
            Class c = this.findLoadedClass(name);
            if (c == null) {
                c = this.loadClassByDelegation(name);
            }

            if (c == null) {
                c = this.findClass(name);
            }

            if (c == null) {
                c = this.delegateToParent(name);
            }

            if (resolve) {
                this.resolveClass(c);
            }

            return c;
        }
    }

4. 类加载器定义好了之后就可以通过自定义的类加载器对类进行加载了

public void testClassLoader5(){
        Loader myClassLoader = new Loader();
        for(int i=0;i<10;i++){
            try {
                Class clz = Class.forName("tools.Dump ",true,myClassLoader);
                Field[] declaredFields = clz.getDeclaredFields();
                LOGGER.info(clz.newInstance().getClass().getClassLoader().toString());
            }catch (Exception e){
                LOGGER.error("class loader error",e);
            }
        }
    }

5. 例子中加载了tools.Dump 这个类,并通可以通过反射的方式生成一个这个类的对象,

但是却不能直接将这个对象直接赋值给tools.Dump 类型的变量,因为这个类已经被系统加载器加载过了。赋值的时候会报类型转换异常的错误,以为虽然包名、类名都相同但是类加载器不同。

原文地址:https://www.cnblogs.com/liguangming/p/9452913.html