自定义Java类加载器的实现

来源:互联网 发布:windows网络上有重名 编辑:程序博客网 时间:2024/06/05 20:35

             既然类加载器也不过是一个将需要的class文件加载在内存里的普通Java类,(具体参见上一篇博客) 那么我们是否可以通过编写实现自定义Java类加载器呢?答案是肯定的。

     我们参照java.lang包下的ClassLoader来编写自定义加载器。

      首先定义一个Student类,这里是通过对Student这个类生成的class文件加密(在项目的bin目录下)实现绕过系统的ApplicationClassLoader,而是靠自定义的加载器加载,类里没有定义方法(因为可能自己定义的加载器有问题,类无法加载,这样没办法调用该方法),而是重写toString方法,代码如下:

public class Student {public String toString(){        return "学习中";    }}
     然后开始新建一个类加载器MyClassLoader 继承自ClassLoader,这里直接定义main方法进行加密,在里面定义原来class文件所在路径,和加密后的class文件路径(我这里是在项目下新建一个文件夹test_lib,作为自己的class目录)代码如下:

public class MyClassLoder extends ClassLoader {public static void main(String[] args) {String srcPath="D:\\workbases\\java\\bin\\com\\huaxin\\classLoader\\Student.class";      String destPath="test_lib\\Studet.class";    //加密 encrypt(srcPath,destPath);} }   
       接着编写加密算法(不是重点,所以写的很简单比较简单,也方便解密)构建文件输入流 ,在读的时候对其加一,代码如下:

//加密处理 破坏原class,使得ApplicationClassLoader无法加载    public static void encrypt(String srcPath,String destPath){try {InputStream in=new FileInputStream(srcPath);OutputStream out=new FileOutputStream(destPath);int i;while((i=in.read())!=-1){out.write(i+1);}in.close();out.close();} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}
    然后运行项目,刷新可以看见在新建的文件夹test_lib下,有编译后生成的Student.class文件

将这个文件复制拷贝到系统class文件的目录下,(就是上面的srcPath,将原来的Student.class覆盖掉)这时我们在新建一个Test类,让它去引入Student类,发现程序报错,可见系统的ApplicationClassLoader加载器已经没办法加载这个破坏后的class文件,需要使用自己的加载器进行解密,再加载,继续写自定义的加载器(注意,此时不要再动Student.java文件,不然系统很快重新编译,就无法达到使用自己的加载器的目的)

   然后继续在类加载器MyClassLoader中定义解密算法

//解密处理public static void desencrypt(String srcPath,OutputStream out){try {InputStream in=new FileInputStream(srcPath);int i;while((i=in.read())!=-1){out.write(i-1);   //还原}} catch (Exception e) {// TODO: handle exception}}

      借鉴ClassLoader的API,我们知道自定义的加载器需要重写findClass方法,代码如下:

@Overrideprotected Class<?>  findClass(String name) throws ClassNotFoundException{//检验是否触发 findClass,将name值传入System.out.println("name="+name);//拼接传入的路径值String srcPath="test_lib\\"+name;//将本地文件读到内存中 向内存中输出的流ByteArrayOutputStream  baos=new ByteArrayOutputStream();  //读到内存后进行还原desencrypt(srcPath,baos);//得到Student.class在内存中的字节数组byte[] buf = baos.toByteArray();//将字节数组得到对应的Class实例return defineClass(buf,0,buf.length);  //return super.findClass(name);  }

然后我们在定义的Test文件中再次加载Student类,


public class Test {public static void main(String[] args) throws Exception {//创建自定义加载器对象(最终返回类的字节码)Class c=new MyClassLoder ().loadClass("Studet.class");         //直接实例化对象输出  调用toString方法System.out.println(c.newInstance().toString());}}

后台输出:

至此,使用自己的类加载器加载到了Student类

    最后说一个尴尬的地方由于我在第一次加密时将保存路径destPath中的“student.class"错写成"Studet.class",解密的时候一直报类找不到异常,我还在纳闷,看了半天发现自己写错了,这真是瞎呀,为了警戒自己,也没有改,直接留下来了(其实我是懒得再截图什么的,O(∩_∩)O),经常犯这样低级的错误,下次要睁大眼睛了。



0 0
原创粉丝点击