APM - Javassist 入门 生成一个简单类
生活随笔
收集整理的這篇文章主要介紹了
APM - Javassist 入门 生成一个简单类
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 官網
- 概述
- Javassist作用
- 常用API
- Javassist 語法
- Javassist使用流程
- Demo
- Demo2
- 注意事項
- 參考
官網
http://www.javassist.org/
http://www.javassist.org/tutorial/tutorial.html
概述
Javassist是一個開源的分析、編輯和創建Java字節碼的類庫,可以直接編輯和生成Java生成的字節碼。
相對于bcel, asm等這些工具,開發者不需要了解虛擬機指令,就能動態改變類的結構,或者動態生成類。
Javassist簡單易用, 快速。
Javassist作用
- 運行時監控插樁埋點
- AOP動態代理實現(性能上比Cglib生成的要慢)
- 獲取訪問類結構信息:如獲取參數名稱信息
常用API
| ClassPool | Javassist的類池,使用ClassPool 類可以跟蹤和控制所操作的類, 與 JVM ClassLoader相似 |
| CtClass | CtClass提供了類的操作,如在類中動態添加新字段、方法和構造函數、以及改變類、父類和接口的方法。 |
| CtMethod | 類中的方法,通過它可以給類創建新的方法,還可以修改返回類型,訪問修飾符等, 甚至還可以修改方法體內容代碼 |
| CtConstructor | 構造函數 |
| CtField | 類的屬性,通過它可以給類創建新的屬性,還可以修改已有的屬性的類型,訪問修飾符等 |
Javassist 語法
| $0, $1, $2, … | this and actual parameters |
| $args | An array of parameters. The type of $args is Object[]. |
| $$ | All actual parameters.For example, m($$) is equivalent to m($1,$2,…) |
| $cflow(…) | cflow variable |
| $r | The result type. It is used in a cast expression. |
| $w | The wrapper type. It is used in a cast expression. |
| $_ | The resulting value |
| $sig | An array of java.lang.Class objects representing the formal parameter types |
| $type | A java.lang.Class object representing the formal result type. |
| $class | A java.lang.Class object representing the class currently edited. |
Javassist使用流程
Demo
依賴
<dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.18.1-GA</version></dependency> import javassist.*;/*** 使用Javassist 構建 一個新的類 并執行*/ public class FirstJavasisit {public static void main(String[] args) throws CannotCompileException,NotFoundException, InstantiationException, IllegalAccessException {ClassPool pool = new ClassPool(true);// 插入類路徑,通過類路徑去搜索我們要的類pool.insertClassPath(new LoaderClassPath(FirstJavasisit.class.getClassLoader()));// 構建一個新的CtClass對象CtClass targetClass = pool.makeClass("com.artisan.Hello");// 實現一個接口targetClass.addInterface(pool.get(IHello.class.getName()));// 獲取返回類型CtClass returnType = pool.get(void.class.getName());// 方法名稱String mname = "sayHello";// 方法參數CtClass[] parameters = new CtClass[]{pool.get(String.class.getName())};// 實例化方法CtMethod method = new CtMethod(returnType, mname, parameters, targetClass);// 方法中的源碼String src = "{"+ "System.out.println($1);"+ "}";// 設置src到方法中method.setBody(src);// 添加方法targetClass.addMethod(method);// 裝在到當前的ClassLoader中Class cla = targetClass.toClass();// 實例化IHello hello = (IHello) cla.newInstance();// 方法調用hello.sayHello("artisan");}/*** 接口不是必須的,只是為了方便演示,少寫點反射代碼*/public interface IHello {void sayHello(String name);} }Demo2
讓我們對UserService類 插裝一下
package com.artisan.agent;public class UserService {/*** 無參方法* @throws InterruptedException*/public void sayHello() throws InterruptedException {Thread.sleep(100);System.out.println("hello 小工匠");}/*** 無返回值的* @param name* @param age* @param other* @throws InterruptedException*/public void say2Void(String name,int age,Object other) throws InterruptedException {Thread.sleep(100);System.out.println("hello 小工匠2");}/*** 帶有返回值* @param name* @param age* @param other* @return* @throws InterruptedException*/public String say2(String name,int age,Object other) throws InterruptedException {Thread.sleep(100);System.out.println("hello 小工匠2 with return ");return "ttttt";} }@Testpublic void test3() throws NotFoundException, CannotCompileException, InterruptedException {// 類加載器ClassPool classPool = new ClassPool();// 追加系統ClassLoaderclassPool.appendSystemPath();// 獲取要操作的類CtClass ctClass = classPool.get("com.artisan.agent.UserService");// 獲取方法CtMethod originMethod = ctClass.getDeclaredMethod("say2Void");// copy 一個新的方法CtMethod newMethod = CtNewMethod.copy(originMethod,ctClass,null);// 設置新名字originMethod.setName(originMethod.getName()+ "$agent");// 對原方法進行包裝,比如加計算方法耗時newMethod.setBody("{ long begin = System.currentTimeMillis();\n" +" say2Void$agent($$);\n" +" long end = System.currentTimeMillis();\n" +" System.out.println(end - begin);" +"}");// 將新方法添加到單簽類中ctClass.addMethod(newMethod);//把修改后的class裝載到JVMctClass.toClass();new com.artisan.agent.UserService().say2Void("art2",18,"xxxx");}@Testpublic void test4() throws NotFoundException, CannotCompileException, InterruptedException {// 類加載器ClassPool classPool = new ClassPool();// 追加系統ClassLoaderclassPool.appendSystemPath();// 獲取要操作的類CtClass ctClass = classPool.get("com.artisan.agent.UserService");// 獲取方法CtMethod originMethod = ctClass.getDeclaredMethod("say2");// copy 一個新的方法CtMethod newMethod = CtNewMethod.copy(originMethod,ctClass,null);// 設置新名字originMethod.setName(originMethod.getName()+ "$agent");// 對原方法進行包裝,比如加計算方法耗時 帶有返回值的的 $rnewMethod.setBody("{ long begin = System.currentTimeMillis();\n" +" say2$agent($$);\n" +" long end = System.currentTimeMillis();\n" +" System.out.println(end - begin);" +" Object s = \"test\" ;" +" return ($r)s ;" +"}");// 將新方法添加到單簽類中ctClass.addMethod(newMethod);//把修改后的class裝載到JVMctClass.toClass();System.out.println((new com.artisan.agent.UserService().say2("art2", 18, "xxxx")));}
注意事項
- 所引用的類型,必須通過ClassPool獲取后才可以使用
- 代碼塊中所用到的引用類型,使用時必須寫全量類名
- 代碼塊內容寫錯了,只有在運行時才報錯
- javassist只接受單個語句或用大括號括起來的語句塊
- 動態修改的類,必須在修改之前,jvm中不存在這個類的實例對象。修改方法的實現必須在修改的類加載之前進行
參考
https://baijiahao.baidu.com/s?id=1660843613132087355&wfr=spider&for=pc
https://www.cnblogs.com/scy251147/p/11100961.html
https://blog.csdn.net/21aspnet/article/details/81671777
https://www.cnblogs.com/rickiyang/p/11336268.html
總結
以上是生活随笔為你收集整理的APM - Javassist 入门 生成一个简单类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: APM - Hello Javaage
- 下一篇: Spring5源码 - 02 Bean和