操作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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...