操作Java位元組碼

来源:https://www.cnblogs.com/aishangJava/archive/2018/10/29/9873595.html
-Advertisement-
Play Games

本博客主要介紹通過 Javassist、ASM 操作 Java 位元組碼。 Class 文件是什麼 通常對於用 idea 的同學來說,class 文件是直接可以查看的,可以看到像 java 那樣的代碼。其實 class 文件是一種位元組碼文件,我們平時在 idea 所看到的,是 idea 自動反編譯後的 ...


本博客主要介紹通過 Javassist、ASM 操作 Java 位元組碼。

Class 文件是什麼

通常對於用 idea 的同學來說,class 文件是直接可以查看的,可以看到像 java 那樣的代碼。其實 class 文件是一種位元組碼文件,我們平時在 idea 所看到的,是 idea 自動反編譯後的結果。如果把 class 文件用 sublime 打開,就會看到許多位元組碼,而不是 Java 代碼了。像這樣:

cafe babe 0000 0034 0017 0100 1163 6e2f
6863 6873 7475 6469 6f2f 5573 6572 0700
0101 0010 6a61 7661 2f6c 616e 672f 4f62
6a65 6374 0700 0301 0004 6e61 6d65 0100
......

Class文件是一組以 8 位位元組為基礎單位的二進位流,各個數據項目嚴格按照順序緊湊排列在 Class 文件中,中間無任何分隔符。 

我們這裡所說的操作 Java 位元組碼,就是操作修改 class 文件內容。

Why

同學們可能會有這樣一個疑問,為什麼要操作 Java 位元組碼,直接改 java 文件不是很好嗎?
很多情況下是無法操作的 java 文件的,或者使用修改位元組碼的方式更方便:

  1. 在第三方依賴中加入一些檢測數據
  2. AOP 操作,例如 Android 自動埋點統計
  3. Spring 框架的 AOP 操作使用 ASM 操作 Java 位元組碼

總的來說,可以更方便開發,也同時瞭解一些底層的原理。

Javassist

Javassist是一個開源的分析、編輯和創建Java位元組碼的類庫。是由東京工業大學的數學和電腦科學系的 Shigeru Chiba(千葉 滋)所創建的。它已加入了開放源代碼 JBoss 應用伺服器項目,通過使用Javassist對位元組碼操作為 JBoss 實現動態”AOP”框架。

導包

compile group: 'org.javassist', name: 'javassist', version: '3.23.1-GA'


類搜索路徑
版本號可能不是最新的,想要最新的話查找 Maven 倉庫獲取最新的版本號即可。

通過 ClassPool.getDefault() 獲取的 ClassPool 使用 JVM 的類搜索路徑。如果程式運行在 JBoss 或者 Tomcat 等 Web 伺服器上,ClassPool 可能無法找到用戶的類,因為 Web 伺服器使用多個類載入器作為系統類載入器。在這種情況下,ClassPool 必須添加額外的類搜索路徑。

下麵的例子中,pool 代表一個 ClassPool 對象:

pool.insertClassPath(new ClassClassPath(this.getClass()));


上面的語句將 this 指向的類添加到 pool 的類載入路徑中。你可以使用任意 Class 對象來代替 this.getClass(),從而將 Class 對象添加到類載入路徑中。傳參支持 ClassPath、URLClassPath、ByteArrayClassPath 類型。

編輯

// 創建 User 類
CtClass ctClass = classPool.makeClass("cn.hchstudio.User");
// 獲取 String 類
CtClass CtString = classPool.get("java.lang.String");


通過 makeClass 和 get 方法可以分別創建、獲取 CtClass,進而操作類。

上面的語句是創建一個變數,new CtField 中分別傳入類型、名稱、ctClass。setModifiers 設置變數修飾符;addField 表示把變數加入到這個類中。

CtField name = new CtField(CtString, "name", ctClass);
name.setModifiers(Modifier.PRIVATE);
ctClass.addField(name);

對於已存在的方法,可以使用 insertBefore、insertAfter 方法插入到方法函數之後或之後。Javassist 有一個簡單除暴的新增方法方式,就是直接把要寫的 java 代碼變為字元串,之後 Javassist 便可自動完成代碼校驗,轉為位元組碼的過程。

ctMethod.insertBefore("System.out.println(\"lalala\");");
ctMethod.insertAfter("System.out.println(\"lalala\");");

一個慄子 

舉一個慄子,這裡通過 Javassist 生成一個 User 類,其中包括 name、sex 屬性,並有其 set、get 方法。並且輸出到 ./out/production/classes 目錄下。

ClassPool classPool = ClassPool.getDefault();

try {
    CtClass ctClass = classPool.makeClass("cn.hchstudio.User");

    CtClass CtString = classPool.get("java.lang.String");

    CtField name = new CtField(CtString, "name", ctClass);
    name.setModifiers(Modifier.PRIVATE);
    ctClass.addField(name);
    CtField sex = new CtField(CtString, "sex", ctClass);
    sex.setModifiers(Modifier.PRIVATE);
    ctClass.addField(sex);

    CtMethod setName = new CtMethod(CtClass.voidType, "setName",
            new CtClass[]{CtString}, ctClass);
    setName.setModifiers(Modifier.PUBLIC);
    setName.setBody("name = $1;");
    ctClass.addMethod(setName);
    CtMethod getName = new CtMethod(CtString, "getName",
            new CtClass[]{}, ctClass);
    getName.setModifiers(Modifier.PUBLIC);
    getName.setBody("return name;");
    ctClass.addMethod(getName);
    CtMethod setSex = CtMethod.make("public void setSex(java.lang.String sex){" +
            "this.sex = sex;" +
            "}", ctClass);
    ctClass.addMethod(setSex);
    CtMethod getSex = new CtMethod(CtString, "getSex",
            new CtClass[]{}, ctClass);
    getSex.setModifiers(Modifier.PUBLIC);
    getSex.setBody("return sex;");
    ctClass.addMethod(getSex);

    ctClass.writeFile("./out/production/classes");
} catch (Exception e) {
    System.out.println(e.toString());
    e.printStackTrace();
}

ASM 

ASM 也是一個操作 Java 位元組碼的框架,相比於 Javassist,它更加底層、輕量級、速度也快,不過在編寫代碼的時候可能容易出錯,它需要我們直接寫 Java 位元組碼。

Java jdk 自帶了 ASM 的依賴,在 rt.jar!/jdk/internal/org/objectweb/asm 下。
Android 環境下則需要自己導入依賴,因為 Android 去掉了 rt.jar!/jdk 包。

編輯

ASM 編輯代碼則比較複雜,需要對彙編有一定瞭解的同學才可以。
通常的方式是我們需要用 java 寫出一個想要自動生成的類,然後查看他的 class 位元組碼

mv = cw.visitMethod(ACC_PUBLIC, "setName", "(Ljava/lang/String;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, "cn/hchstudio/User", "name", "Ljava/lang/String;");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();


這裡改出一段示例代碼,意思為新建一個 setName 方法,並給 name 屬性賦值。 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 字元串的創建 字元串創建符號 ' ' " " ''' ''' """ """ 轉義符\ >>> string_long = """This is another long string ... value that will span multiple ... lines in the output ...
  • 一、對Redis持久化的探討與理解 目前Redis持久化的方式有兩種: RDB 和 AOF 首先,我們應該明確持久化的數據有什麼用,答案是用於重啟後的數據恢復。 Redis是一個記憶體資料庫,無論是RDB還是AOF,都只是其保證數據恢復的措施。 所以Redis在利用RDB和AOF進行恢復的時候,都會讀 ...
  • Python爬蟲目前是基於requests包,下麵是該包的文檔,查一些資料還是比較方便。 http://docs.python-requests.org/en/master/ 爬取某旅游網站的產品評論,通過分析,獲取json文件需要POST指令。簡單來說: GET是將需要發送的信息直接添加在網址後面 ...
  • 1、基礎介紹 常用功能 1、HTTP服務 動靜分離、WEB緩存、虛擬主機設置、URL Rewrite 2、負載均衡 3、反向代理 4、正向代理 5、郵件伺服器 優點 高擴展、高可用、支持高併發、低資源消耗、可平滑升級重啟(熱部署) 2、安裝部署 1、下載 nginx-1.15.3.tar.gz 2、 ...
  • 繼續上次的進度:https://www.cnblogs.com/flashBoxer/p/9847521.html 正文: 裝飾類 在類中有兩種不通的方式使用裝飾器,第一個和我們之前做過的函數非常相似:在類的方法上應用。這也是當時引入裝飾器的原因之一一些常用的裝飾器已經內置到python中,像@cl ...
  • 一、環境 tomcat7.0、notepad++(這個是一個文本編輯器,用記事本也可以)二、修改方法 例如:現在把預設的8080埠改成8082埠 第一步:找到tomcat7的conf目錄下的 server.xml這個文件,並用notepad++打開。 第二步:將 改成 (可以利用ctrl+f 快... ...
  • 開發語言 高級語言:基於C/彙編等封裝的語言,如Python、Java、C#、PHP、Go、ruby、C++……生成位元組碼讓C/彙編去識別 低級語言:直接讓電腦底層能識別成機器碼的語言(電腦再將機器碼識別為0101010……),如C、彙編等 Python特點:開發效率高,執行效率低,但 摩爾定律 ...
  • maven pom屬性 內置屬性(預定義,可直接使用) ${basedir} 表示項目根目錄,即包含pom.xml文件的目錄; ${version} 表示項目版本; ${project.basedir}同${basedir}; ${maven.build.timestamp} 表示項目構件開始時間; ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...