作者:小傅哥
博客:https://bugstack.cn
沉澱、分享、成長,讓自己和他人都能有所收穫!
目錄
一、前言
在字節碼編程方面有三個比較常見的框架;ASM
、byte-buddy
、Javassist
,他們都可以對這字節碼進行操作,只是操作方式和控制粒度不同。
其中 ASM 更偏向於底層,需要了解 JVM 虛擬機中指定規範以及對局部變量以及操作數棧的知識。雖然在編寫起來比較麻煩,但是它也是性能最好功能最強的字節碼操作框架。常見的會用在 CGLIB 動態代理類中,以及一些非入侵的探針監控場景中。
另外兩個框架都是有強大的 API,操作使用上更加容易控制。雖然對對比上會比 ASM 性能差一些,但不是說性能完全不好。同樣在一些監控場景中也用的非常多。如果你細心可以在你的工程 jar 包搜索一下。
在這之前我已經編寫了 Javaagent全鏈路監控
和 ASM
的部分文章,雖然這部分技術內容在 CRUD 開發中並不常用,但隨着自動化測試、非入侵監控的大量使用,還是蠻多人需要這樣的技能學習的。同時我也是這樣一個技能的學習者,爲此後面會陸續編寫和完善關於 字節碼編程 這個專欄。也希望這個專欄在提升自己技術棧的同時也幫助他人成長。
那麼,小傅哥計劃從 Javassist
到 ASM
陸續完成整套專欄學習的文章編寫。從簡單入門到應用操作,一步步來完成成體系的技術知識棧學習。
好!,現在開始第一個Helloworld案例。相關源碼可以通過關注 公衆號:bugstack蟲洞棧
獲取
二、開發環境
- JDK 1.8.0
- javassist 3.12.1.GA
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
<type>jar</type>
</dependency>
三、案例目標
不看實現過程的話,我們的案例目標其實很簡單,就是使用 javassist
輸出一行 Helloworld 。這話像不像產品說的
public class HelloWorld {
public static void main(String[] args) {
System.out.println("javassist hi helloworld by 小傅哥(bugstack.cn)");
}
public HelloWorld() {
}
}
以上的這段代碼就是我們接下來需要使用字節碼編程技術來實現的內容。
四、技術實現
其實輸出一個 Helloworld
還是蠻簡單的,主要是從這裏面去學習一下 Javassist
的基本語法結構,也能爲後續的學習有一個基礎的概念。
javassist Helloworld
/**
* 公衆號:bugstack蟲洞棧
* 博客棧:https://bugstack.cn - 沉澱、分享、成長,讓自己和他人都能有所收穫!
* 本專欄是小傅哥多年從事一線互聯網Java開發的學習歷程技術彙總,旨在爲大家提供一個清晰詳細的學習教程。如果能爲您提供幫助,請給予支持(關注、點贊、分享)!
*/
public class GenerateClazzMethod {
public static void main(String[] args) throws IOException, CannotCompileException, NotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
ClassPool pool = ClassPool.getDefault();
// 創建類 classname:創建類路徑和名稱
CtClass ctClass = pool.makeClass("org.itstack.demo.javassist.HelloWorld");
// 添加方法
CtMethod mainMethod = new CtMethod(CtClass.voidType, "main", new CtClass[]{pool.get(String[].class.getName())}, ctClass);
mainMethod.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
mainMethod.setBody("{System.out.println(\"javassist hi helloworld by 小傅哥(bugstack.cn)\");}");
ctClass.addMethod(mainMethod);
// 創建無參數構造方法
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{}, ctClass);
ctConstructor.setBody("{}");
ctClass.addConstructor(ctConstructor);
// 輸出類內容
ctClass.writeFile();
// 測試調用
Class clazz = ctClass.toClass();
Object obj = clazz.newInstance();
Method main = clazz.getDeclaredMethod("main", String[].class);
main.invoke(obj, (Object)new String[1]);
}
}
這段代碼分爲幾塊內容來實現功能,分別包括;
- 創建 ClassPool,它是一個基於HashMap實現的 CtClass 對象容器。
- 使用 CtClass,創建我們的類信息,也就是類的路徑和名稱。
- 接下來就是給類添加方法。包括;方法的屬性、類型、名稱、入參、出參和方法體的內容。
- 在方法創建好後還需要創建一個空的構造函數,每一個類都會在編譯後生成這樣一個構造函數。
- 當方法創建完成後,我們使用
ctClass.writeFile()
進行輸出方法的內容信息。也就可以看到通過我們使用Javassist
生成類的樣子。 - 最後就是我們的反射調用
main
方法,測試輸出結果。
五、測試結果
當我們執行測試的時候會輸出類信息到工程文件夾下,同時會輸出我們的測試結果;
1. 使用Javassist生成的類
2. 輸出的測試結果
javassist hi helloworld by 小傅哥(bugstack.cn)
Process finished with exit code 0
六、總結
- 關於
Javassist
的使用在完整的且強大的API
下,確實還是蠻容易使用的。並且代碼的使用上並不是很難理解。 - 後續會陸續推出字節碼編程的案例文章,逐步完善這部分技術知識棧的內容。最終嘗試使用這樣的技術知識完成一個案例級別的質量檢測系統。也歡迎喜歡此類內容的小夥伴跟進學習。
- 後續的文章可能在專欄類的文章裏,文章內容上會短一點。儘可能在一篇文章中描述清楚一個詳盡的知識點,也方便後續整理成 PDF 書籍,方便學習使用。