Classloader 二 自定义类加载器

一 为什么需要自定义类加载器

1.假设需要加载的类需要保密,那么java自带的app类加载器就无法完成加载的任务,这个时候就需要通过自定义类加载器先对类文件进行解密,然后再进行加载。

2.加载指定路径的类文件。比如类文件放在磁盘的某个文件夹、或者来自网络。下面的例子会展示加载指定磁盘目录下的一个类文件的方法。

二 示例

自定义一个类加载器MyClassLoader,继承自ClassLoader,重写findClass方法(前一篇讲到了为什么重写findClass而不是loadClass,主要是为了遵循双亲委托模型)。代码如下:

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

public class MyClassLoader extends ClassLoader {

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // TODO Auto-generated method stub
        try {
            File location = new File("D:\\bkp");
            byte[] b = loadClassData(name, location);
            return defineClass(name, b, 0, b.length);
        } catch (Exception e) {
            throw new ClassNotFoundException(name);
        }
    }



    @Override

    public Class<?> loadClass(String arg0) throws ClassNotFoundException {
        // TODO Auto-generated method stub
        return super.loadClass(arg0);
    }



    protected byte[] loadClassData(String name, File location) {
        FileInputStream fis = null;
        byte[] datas = null;
        try {
            File classFile = new File(location, name + ".class");
            fis = new FileInputStream(classFile);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int b;
            while ((b = fis.read()) != -1) {
                bos.write(b);
            }
            datas = bos.toByteArray();
            bos.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fis != null)
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
        return datas;

    }

}

然后使用这个类加载器尝试加载一些不同类型的类文件,我们来看一下结果:

import java.lang.reflect.Method;


public class ClassLoaderTest {
        public static void main(String[] args) throws Exception {
            ClassLoader myLoader = new MyClassLoader();
            Class<?> clazz = myLoader.loadClass("ClassLoaderTest");
            Object obj = clazz.newInstance();
            System.out.println(obj.getClass().getClassLoader());
        }
}

第一次

传入参数ClassLoaderTest

结果:sun.misc.Launcher$AppClassLoader@73d16e93

第二次
传入参数java.lang.String

结果:null
null代表使用的是bootstrap类加载器进行的加载。因为这次传入参数是java.lang.String,根据双亲委派原则,最终会请求bootstrap类加载器加载java.lang.String类,而java.lang.String类是属于bootstrap类加载器加载的内容范围。

第三次

传入参数Demo,这个类不在应用程序classpath目录,而是放在d:\bkp目录

结果:MyClassLoader@6d06d69c。这次才是通过自定义类加载器完成了类加载。

三 总结

上例通过写一个类加载器说明了自定义类加载器的基本流程,为了遵循双亲委托模型,我们在自定义类加载器中重写了findClass方法。如果重写loadClass同样可以实现自定义类加载器,但是我们上例中得到的结果就会不同,因为破坏了双亲加载模型,最后得到的结果都是自定义类加载器。

猜你喜欢

转载自blog.csdn.net/rambomatrix/article/details/78492010