Posted on 2009-12-10 01:09
長城 閱讀(249)
評論(0) 編輯 收藏
從張老師的基礎增強課上,我就想深入了解一下類加載器。然后聽懂了張老師的課,知道了JAVA類加載的方式,以及實現自己的類加載器的應用。但一直沒有自己去編寫類加載器,也對class文件的具體處理方式不了解。今日休息,有時間深入了解一下。
一、JAVA有三個類加載器:
1.bootstrap class loader,負責加載系統類,比如jdk的rt.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文件的明文,比如為了保護軟件的安全、防止破解等?!?/span>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以來便對它們的實現機制比較感興趣,雖然已經弄懂了他們簡單的實現原理。但今日看到《實現自己的類加載器》這篇文章時,讓對有了更深入的了解。在tomcat的webapps目錄下的所有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