【Java类加载机制】深入类加载器(二)自定义加密、解密类加载器
類加載器原理
將class文件字節碼內容加載到內存中,并將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個代表這個類的java.lang.Class對象,作為方法區類數據的訪問入口。
類加載器樹狀結構、雙親委托(代理)機制
1、引導類加載器(C語言編寫)
2、擴展類加載器(Java編寫)
3、應用程序類加載器(Java編寫)
4、自定義類加載器(Java編寫)
來測試一下ClassLoader的層級關系
獲取目前的系統類加載器:各個項目的類加載器都是獨立的,不會相互影響
System.out.println(System.getProperty("java.class.path")); // C:\Users\Bug\eclipse-workspace3\bilibili\bin類加載器的代理模式
交給其他加載器來加載指定的類。
雙親委托機制
父類加載器優先加載。如果父類不能加載,再讓子類加載。
這種雙親委托機制可以很安全。
假設某人自己定義了一個java.lang.string,會一層一層向上傳遞,隨后由最父類的核心包加載成功,而自己定義的永遠不會被加載。
這樣,核心類雖然能夠自己定義,但是永遠你無法使用到,保證了Java核心庫的安全。
并不是所有的類加載器都采用了雙親委托機制。
Tomcat服務器也是用代理模式,所不同的是它子類優先。首先嘗試去加載某個類,如果找不到再代理給父類加載器。這與一般類加載器的順序是相反的。因為可能對于服務器來說,在某些情況下,雙親委托機制不夠靈活,并不能適應我們的需求。
本來想測試一下自定義java.lang.String的后果,然而,直接報錯了,無法測試。
自定義類加載器(文件、網絡、加密)
實現一個自定義類加載器的流程:
(1)準備工作
在C:\picture路徑下創建一個Hello.java文件
Hello.java
編譯運行一下
運行之后的效果:
在C:\picture\cn\hanquan\myloader路徑下,產生了Hello.class
(2)編寫自定義類加載器
- 目錄結構
- FileSystemClassLoader.java
使用同樣的原理,把rootDir換成url,可以寫一個網絡的類加載器,將getClassData中的path改一下,然后輸入流改一下即可。此處不再舉例說明。
測試一下自定義的類加載器
- Demo1.java
更多嘗試:查看其他默認類加載器
- Demo2.java
(3)運行結果
Demo1.java的輸出
c1 的Class是 class cn.hanquan.myloader.Hello c2 的Class是 class cn.hanquan.myloader.Hello c1 的hashCode是 1288141870 c2 的hashCode是 1288141870 c3 的hashCode是 1908153060Demo2.java的輸出
c1 的Class是 class cn.hanquan.myloader.Hello c1 的hashCode是 966808741 c2 的Class是 class cn.hanquan.myloader.Hello c2 的hashCode是 966808741 c3 的hashCode是 1908153060 c3 的ClassLoader是 cn.hanquan.testloader.FileSystemClassLoader@77556fd c4 的ClassLoader是 null c5 的ClassLoader是 jdk.internal.loader.ClassLoaders$AppClassLoader@28c97a5對class進行加密操作
本示例使用逐位取反的方式進行加密。簡單的取反操作示例如下:
public static void main(String[] args) {int a = 3;System.out.println(Integer.toBinaryString(a));// 11System.out.println(Integer.toBinaryString(a ^ 0xff));// 11111100}了解了逐位取反的方式之后,下面,開始我們的加密與解密~
(1)寫一個加密工具
- EncodeUtil.java
注意:加密不要改變class的文件名!class文件名稱要保持與類名一致!
否則:在解密時,會拋出異常NoClassDefFoundError:wrong name
package cn.hanquan.testloader;import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;public class EncodeUtil {public static void main(String[] args) {encode(new File("c:/picture/cn/hanquan/myloader/Hello.class"),new File("c:/picture/temp/cn/hanquan/myloader/Hello.class"));// class文件名稱要保持與類名一致,否則NoClassDefFoundError:wrong name// 為了保持包名結構,我在文件夾中手動創建了一個空的C:\picture\temp\cn\hanquan\myloader// 要是不打這個package,應該就沒有這么復雜了}public static void encode(File src, File dest) {FileInputStream fis = null;FileOutputStream fos = null;try {fis = new FileInputStream(src);fos = new FileOutputStream(dest);int temp = -1;while ((temp = fis.read()) != -1) {// 讀取一個字節fos.write(temp ^ 0xff);// 取反輸出}} catch (IOException e) {} finally {// 關閉流if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}try {fos.close();} catch (IOException e) {e.printStackTrace();}}}System.out.println("Finish Encoding!");} }運行之后,可以看到,C:\picture\temp\cn\hanquan\myloader路徑下生成了Hello.class文件。
(2)嘗試加載加密過的class文件
- Demo3.java
運行結果
顯然,被加密的class文件是加載不到的。
拋出異常:是不兼容的格式。
因此,下一步,我們需要定義一個解密工具DecodeUtil.java。
(3)自定義解密工具類
- DecodeClassLoader.java
DecodeClassLoader由FileSystemClassLoader.java稍微修改而來,不要忘了extends ClassLoader
package cn.hanquan.testloader;import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream;public class DecodeClassLoader extends ClassLoader {private String rootDir;public DecodeClassLoader(String rootDir) {this.rootDir = rootDir;}@Overrideprotected Class<?> findClass(String className) throws ClassNotFoundException {Class<?> c = findLoadedClass(className);// 看這個類是否已經被加載過if (c != null) {// 已經加載過return c;} else {// 讓父類AppClassLoader去加載ClassLoader parent = this.getParent();try {c = parent.loadClass(className);// 委派給父類加載} catch (ClassNotFoundException e) {// System.out.println("父類無法加載你的class,拋出ClassNotFoundException,已捕獲,繼續運行");}if (c != null) {// 父類成功加載System.out.println("父類成功加載");return c;} else {// 讀取文件 轉化成字節數組byte[] classData = getClassData(className);if (classData == null) {throw new ClassNotFoundException();// 手動拋出一個未加載到異常} else {c = defineClass(className, classData, 0, classData.length);return c;}}}}public byte[] getClassData(String className) {String path = rootDir + "/" + className.replace('.', '/') + ".class";// 將流中的數據轉換為字節數組InputStream is = null;ByteArrayOutputStream baos = new ByteArrayOutputStream();try {is = new FileInputStream(path);byte[] buffer = new byte[1024];int temp = -1;while ((temp = is.read()) != -1) {// 讀取一個字節baos.write(temp ^ 0xff);// 取反解密輸出}return baos.toByteArray();} catch (Exception e) {e.printStackTrace();return null;} finally {if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}if (baos != null) {try {baos.close();} catch (IOException e) {e.printStackTrace();}}}} }(4)使用自定義的解密類加載器,加載經過加密的類
使用新定義的解密加載器,加載加密后的類,Demo如下:
- Demo3.java
成功解密。運行結果:
c1 的Class是 class cn.hanquan.myloader.Hello c3 的Class是 class cn.hanquan.myloader.Hello線程上下文類加載器
package com.bjsxt.test; /*** 線程上下文類加載器的測試* @author 尚學堂高淇 www.sxt.cn**/ public class Demo05 {public static void main(String[] args) throws Exception {ClassLoader loader = Demo05.class.getClassLoader();System.out.println(loader);ClassLoader loader2 = Thread.currentThread().getContextClassLoader();System.out.println(loader2);Thread.currentThread().setContextClassLoader(new FileSystemClassLoader("d:/myjava/"));System.out.println(Thread.currentThread().getContextClassLoader());Class<Demo01> c = (Class<Demo01>) Thread.currentThread().getContextClassLoader().loadClass("com.bjsxt.test.Demo01");System.out.println(c);System.out.println(c.getClassLoader());} }服務器類加載器原理和OSGI介紹
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的【Java类加载机制】深入类加载器(二)自定义加密、解密类加载器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java try catch 异常后还会
- 下一篇: 【PAT甲级 十进制转十三进制】1027