日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

【Java类加载机制】深入类加载器(二)自定义加密、解密类加载器

發布時間:2024/2/28 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Java类加载机制】深入类加载器(二)自定义加密、解密类加载器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

類加載器原理

將class文件字節碼內容加載到內存中,并將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個代表這個類的java.lang.Class對象,作為方法區類數據的訪問入口。


類加載器樹狀結構、雙親委托(代理)機制

1、引導類加載器(C語言編寫)

2、擴展類加載器(Java編寫)

3、應用程序類加載器(Java編寫)

4、自定義類加載器(Java編寫)

來測試一下ClassLoader的層級關系

package cn.hanquan.classloader;public class Loader {public static void main(String[] args) {System.out.println(ClassLoader.getSystemClassLoader());System.out.println(ClassLoader.getSystemClassLoader().getParent());System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());} } jdk.internal.loader.ClassLoaders$AppClassLoader@28c97a5 jdk.internal.loader.ClassLoaders$PlatformClassLoader@512ddf17 null

獲取目前的系統類加載器:各個項目的類加載器都是獨立的,不會相互影響

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

package cn.hanquan.myloader;// 包名可以任意 public class Hello{public static void main(String[] args){System.out.println("Hello,World~~");} }

編譯運行一下

運行之后的效果:

在C:\picture\cn\hanquan\myloader路徑下,產生了Hello.class

(2)編寫自定義類加載器

  • 目錄結構
  • FileSystemClassLoader.java
package cn.hanquan.testloader;import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream;/** 自定義文件系統類加載器: 雙親委派式* 實現:自定義一個目錄,傳遞進來,里面的class文件可以自動加載*/ public class FileSystemClassLoader extends ClassLoader {private String rootDir;public FileSystemClassLoader(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;}}}}/** 傳入cn.hanquan.myloader.Hello 返回字節數組c:/picture/cn/hanquan/myloader/Hello.class*/private byte[] getClassData(String className) {String path = rootDir + "/" + className.replace('.', '/') + ".class";// 可以使用CommonsIO將流中的數據轉換為字節數組InputStream is = null;ByteArrayOutputStream baos = new ByteArrayOutputStream();try {is = new FileInputStream(path);byte[] buffer = new byte[1024];int temp = 0;while ((temp = is.read(buffer)) != -1) {// 從輸入流中讀取baos.write(buffer, 0, temp);// 放進字節數組}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();}}}} }

使用同樣的原理,把rootDir換成url,可以寫一個網絡的類加載器,將getClassData中的path改一下,然后輸入流改一下即可。此處不再舉例說明。

測試一下自定義的類加載器

  • Demo1.java
package cn.hanquan.testloader; /** 測試自定義的FileSystemClassLoader*/ public class Demo1 {public static void main(String[] args) throws ClassNotFoundException {FileSystemClassLoader loader = new FileSystemClassLoader("c:/picture");// 加載器Class<?> c1 = loader.loadClass("cn.hanquan.myloader.Hello");// 加載這個類Class<?> c2 = loader.loadClass("cn.hanquan.myloader.Hello");// 加載這個類System.out.println("c1 的Class是 " + c1);System.out.println("c2 的Class是 " + c2);System.out.println("c1 的hashCode是 " + c1.hashCode());System.out.println("c2 的hashCode是 " + c2.hashCode());/** c1 c2是同一個加載器 同一個類 所以是同一個對象* * 被同一個類加載器加載,并且是同一個類,JVM認作是同一個類* 同一個類被不同不容的加載器加載,JVM會認為是不相同的類*/FileSystemClassLoader loader3 = new FileSystemClassLoader("c:/picture");// 換一個加載器Class<?> c3 = loader3.loadClass("cn.hanquan.myloader.Hello");// 加載這個類System.out.println("c3 的hashCode是 " + c3.hashCode());} }

更多嘗試:查看其他默認類加載器

  • Demo2.java
package cn.hanquan.testloader;/** 測試自定義的FileSystemClassLoader*/ public class Demo2 {public static void main(String[] args) throws ClassNotFoundException {FileSystemClassLoader loader = new FileSystemClassLoader("c:/picture");// 加載器FileSystemClassLoader loader3 = new FileSystemClassLoader("c:/picture");// 換一個加載器Class<?> c1 = loader.loadClass("cn.hanquan.myloader.Hello");// 加載這個類Class<?> c2 = loader.loadClass("cn.hanquan.myloader.Hello");Class<?> c3 = loader3.loadClass("cn.hanquan.myloader.Hello");Class<?> c4 = loader.loadClass("java.lang.String");Class<?> c5 = loader.loadClass("cn.hanquan.testloader.Demo1");System.out.println("c1 的Class是 " + c1);System.out.println("c1 的hashCode是 " + c1.hashCode());System.out.println("c2 的Class是 " + c2);System.out.println("c2 的hashCode是 " + c2.hashCode());System.out.println("c3 的hashCode是 " + c3.hashCode());//同一個類被不同的加載器加載,JVM認為是不相同的類System.out.println("c3 的ClassLoader是 " + c3.getClassLoader());// 自定義的類加載器System.out.println("c4 的ClassLoader是 " + c4.getClassLoader());// 引導類加載器System.out.println("c5 的ClassLoader是 " + c5.getClassLoader());// 系統默認的類加載器} }

(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是 1908153060

Demo2.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
package cn.hanquan.testloader;/** 測試簡單的加密解密*/ public class Demo3 {public static void main(String[] args) throws ClassNotFoundException {FileSystemClassLoader loader = new FileSystemClassLoader("c:/picture");// 加載器Class<?> c1 = loader.loadClass("cn.hanquan.myloader.Hello");// 加載未加密的類System.out.println("c1 的Class是 " + c1);Class<?> c2 = loader.loadClass("cn.hanquan.myloader.Hello_Encode");// 加載已加密的類System.out.println("c2 的Class是 " + c1);} }

運行結果

顯然,被加密的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
package cn.hanquan.testloader;/** 測試簡單的加密解密*/ public class Demo3 {public static void main(String[] args) throws ClassNotFoundException {FileSystemClassLoader loader = new FileSystemClassLoader("c:/picture");// 加載器Class<?> c1 = loader.loadClass("cn.hanquan.myloader.Hello");// 加載未加密的類System.out.println("c1 的Class是 " + c1);DecodeClassLoader deLoader = new DecodeClassLoader("c:/picture/temp");// 解密加載器Class<?> c3 = deLoader.loadClass("cn.hanquan.myloader.Hello");// 加載已加密的類System.out.println("c3 的Class是 " + c3);} }

成功解密。運行結果:

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类加载机制】深入类加载器(二)自定义加密、解密类加载器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。