posts - 78, comments - 34, trackbacks - 0, articles - 1
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2009-12-09休息 JAVA类加载器

Posted on 2009-12-10 01:09 長城 阅读(247) 评论(0)  编辑  收藏

                从张老师的基础增强课上,我就想深入了解一下类加载器。然后听懂了张老师的课,知道了JAVA类加载的方式,以及实现自己的类加载器的应用。但一直没有自己去编写类加载器,也对class文件的具体处理方式不了解。今日休息,有时间深入了解一下。

 

一、JAVA有三个类加载器:

1.bootstrap class loader,负责加载系统类,比如jdkrt.jar包里的类。

2.extension class loader,负责加载jre/lib/ext目录下的所有类。

3.system class loader,负责加载环境变量classpath指向目录下的所有类。

 

他们三个依次是“父子关系”,因为bootstrap class loader一般是使用C语言编写的,所以用户无法获取它的对象。

 

当在classpath环境变量中的一个class文件被加载时,system class loader会将class交给父类extension class loader加载,extension class loader会将class交给bootstrap class loader加载。如果bootstrap class loader加载不了,则返回给extension class loader加载,如果extension class loader加载不了,则返回给system class loader加载。

 

被其中一个加载器成功加载后,便解析class文件。如果class文件中有使用到其他类对象,则继续调用类加载器加载。

 

有些情况下不希望被用户看到Class文件的明文,比如为了保护软件的安全、防止破解等。《JAVA核心技术第2卷》也有提到编写自己的类加载器,实现加载被加了密的class文件。

 

下面实现一个自己的类加载器,加载被加密了的class文件:

 

EasyTest.java用于被自定义类加载器加载。

public class EasyTest {

      public void print(){

            System.out.println("EasyTest,you did it!");

      }

}

 

Mycipher.java用于加密EasyTest.class

package cn.itcast.cc.cipher;

 

import java.io.*;

import java.security.Key;

import javax.crypto.*;

 

public class MyCipher {

      private Key key = null;

      private String traninfo = null;

 

      public MyCipher(Key key, String traninfo) {

            this.key = key;

            this.traninfo = traninfo;

      }

 

      public void cipher(InputStream ins, OutputStream ous) throws IOException {

            // 创建加密输出流

            Cipher cip = null;

            CipherOutputStream cos = null;

            try {

                  cip = Cipher.getInstance(this.traninfo);

                  cip.init(Cipher.ENCRYPT_MODE, this.key);

                  cos = new CipherOutputStream(ous, cip);

                  // 写出到输入流

                  int len = 0;

                  byte[] buf = new byte[1024];

                  while ((len = ins.read(buf)) != -1) {

                        cos.write(buf, 0, len);

                  }

            } catch (Exception e) {

                  e.printStackTrace();

            } finally {

                  // 释放输入和输出流

                  cos.close();

                  ins.close();

                  ous.close();

            }

      }

}

 

MyClassLoader.java是自定义类加载器,用于解密被加密后的class文件。

package cn.itcast.cc.classloader;

 

import java.io.*;

import java.security.Key;

import javax.crypto.*;

 

public class MyClassLoader extends ClassLoader {

      private Key key = null;

      private String traninfo = null;

 

      public MyClassLoader(Key key, String traninfo) {

            this.key = key;

            this.traninfo = traninfo;

      }

 

      @Override

      @SuppressWarnings("unchecked")

      protected Class findClass(String name) throws ClassNotFoundException {

            // 获取class解密后的字节码

            byte[] classBytes = null;

            try {

                  classBytes = loadClassBytes(name);

            } catch (Exception e) {

                  throw new ClassNotFoundException(name);

            }

           

            // 使用字节码,实例类对象

            String clname = name.substring(name.lastIndexOf("/") + 1, name

                        .lastIndexOf("."));

            Class cl = defineClass(clname, classBytes, 0, classBytes.length);

            if (cl == null)

                  throw new ClassNotFoundException(name);

            return cl;

      }

 

      private byte[] loadClassBytes(String name) throws IOException {

            // 读入文件

            FileInputStream ins = null;

            ByteArrayOutputStream baos = null;

            CipherInputStream cis = null;

            byte[] result = null;

            try {

                  ins = new FileInputStream(name);

                  Cipher cip = Cipher.getInstance(this.traninfo);

                  cip.init(Cipher.DECRYPT_MODE, this.key);

                  // 使用密码解密class文件

                  cis = new CipherInputStream(ins, cip);

                  baos = new ByteArrayOutputStream();

                  int len = 0;

                  byte[] buf = new byte[1024];

                  while ((len = cis.read(buf)) != -1) {

                        baos.write(buf, 0, len);

                  }

                  result = baos.toByteArray();

            } catch (Exception e) {

                  e.printStackTrace();

            } finally {

                  // 释放流

                  baos.close();

                  cis.close();

                  ins.close();

            }

 

            return result;

      }

}

 

Test.java,用于测试自定义类加载器。

package cn.itcast.cc.testcalss;

 

import java.io.*;

import java.lang.reflect.Method;

import java.security.Key;

import javax.crypto.KeyGenerator;

import cn.itcast.cc.cipher.MyCipher;

import cn.itcast.cc.classloader.MyClassLoader;

 

public class Test {

      public static void main(String[] args) {

            KeyGenerator keyGen;

            try {

                  // 创建密钥

                  keyGen = KeyGenerator.getInstance("AES");

                  keyGen.init(128);

                  Key key = keyGen.generateKey();

                  keyGen = null;

 

                  MyCipher mc = new MyCipher(key,"AES/ECB/PKCS5Padding");

                  mc.cipher(new FileInputStream(new File("C:/EasyTestbak.class")),

                              new FileOutputStream(new File("C:/EasyTest.class")));

 

                  MyClassLoader mcl = new MyClassLoader(key,"AES/ECB/PKCS5Padding");

                 

                  Class clazz = mcl.loadClass("C:/EasyTest.class");

                  Method meth = clazz.getMethod("print", null);

                  meth.invoke(clazz.newInstance(), null);

 

            } catch (Exception e1) {

                  e1.printStackTrace();

            }

      }

}

 

注意:将EasyTest.java编译后放到C盘:C:/EasyTestbak.class

 

例子写的不干净,主要是为了演示自定义类加载器!

 

            学习了tomcat servlet以来便对它们的实现机制比较感兴趣,虽然已经弄懂了他们简单的实现原理。但今日看到《实现自己的类加载器》这篇文章时,让对有了更深入的了解。在tomcatwebapps目录下的所有WEB应用中的class文件和jar文件,都是在Tomcat启动时将全路径记录到URLClassloader中。需要什么类,直接调用URLClassloader.loaderclass()方法即可!

 

            我原本想自己找到class文件的全路径,然后调用class.forname的方法不也可以吗?但如果有JAR包,我还需要使用JARInputstream读入,然后再搜索吗?这样太麻烦,所以还是使用URLClassloader比较好,它全处理了。

 

            它的实现原理,请看《实现自己的类加载器》 http://ajava.org/course/java/14990.html

 

 

参考文献:《实现自己的类加载器》 http://ajava.org/course/java/14990.html


只有注册用户登录后才能发表评论。


网站导航: