APM - Javassist 入门 生成一个简单类
生活随笔
收集整理的這篇文章主要介紹了
APM - Javassist 入门 生成一个简单类
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
文章目錄
- 官網(wǎng)
- 概述
- Javassist作用
- 常用API
- Javassist 語法
- Javassist使用流程
- Demo
- Demo2
- 注意事項(xiàng)
- 參考
官網(wǎng)
http://www.javassist.org/
http://www.javassist.org/tutorial/tutorial.html
概述
Javassist是一個(gè)開源的分析、編輯和創(chuàng)建Java字節(jié)碼的類庫,可以直接編輯和生成Java生成的字節(jié)碼。
相對于bcel, asm等這些工具,開發(fā)者不需要了解虛擬機(jī)指令,就能動(dòng)態(tài)改變類的結(jié)構(gòu),或者動(dòng)態(tài)生成類。
Javassist簡單易用, 快速。
Javassist作用
- 運(yùn)行時(shí)監(jiān)控插樁埋點(diǎn)
- AOP動(dòng)態(tài)代理實(shí)現(xiàn)(性能上比Cglib生成的要慢)
- 獲取訪問類結(jié)構(gòu)信息:如獲取參數(shù)名稱信息
常用API
| ClassPool | Javassist的類池,使用ClassPool 類可以跟蹤和控制所操作的類, 與 JVM ClassLoader相似 |
| CtClass | CtClass提供了類的操作,如在類中動(dòng)態(tài)添加新字段、方法和構(gòu)造函數(shù)、以及改變類、父類和接口的方法。 |
| CtMethod | 類中的方法,通過它可以給類創(chuàng)建新的方法,還可以修改返回類型,訪問修飾符等, 甚至還可以修改方法體內(nèi)容代碼 |
| CtConstructor | 構(gòu)造函數(shù) |
| CtField | 類的屬性,通過它可以給類創(chuàng)建新的屬性,還可以修改已有的屬性的類型,訪問修飾符等 |
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 構(gòu)建 一個(gè)新的類 并執(zhí)行*/ 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()));// 構(gòu)建一個(gè)新的CtClass對象CtClass targetClass = pool.makeClass("com.artisan.Hello");// 實(shí)現(xiàn)一個(gè)接口targetClass.addInterface(pool.get(IHello.class.getName()));// 獲取返回類型CtClass returnType = pool.get(void.class.getName());// 方法名稱String mname = "sayHello";// 方法參數(shù)CtClass[] parameters = new CtClass[]{pool.get(String.class.getName())};// 實(shí)例化方法CtMethod method = new CtMethod(returnType, mname, parameters, targetClass);// 方法中的源碼String src = "{"+ "System.out.println($1);"+ "}";// 設(shè)置src到方法中method.setBody(src);// 添加方法targetClass.addMethod(method);// 裝在到當(dāng)前的ClassLoader中Class cla = targetClass.toClass();// 實(shí)例化IHello hello = (IHello) cla.newInstance();// 方法調(diào)用hello.sayHello("artisan");}/*** 接口不是必須的,只是為了方便演示,少寫點(diǎn)反射代碼*/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();// 追加系統(tǒng)ClassLoaderclassPool.appendSystemPath();// 獲取要操作的類CtClass ctClass = classPool.get("com.artisan.agent.UserService");// 獲取方法CtMethod originMethod = ctClass.getDeclaredMethod("say2Void");// copy 一個(gè)新的方法CtMethod newMethod = CtNewMethod.copy(originMethod,ctClass,null);// 設(shè)置新名字originMethod.setName(originMethod.getName()+ "$agent");// 對原方法進(jìn)行包裝,比如加計(jì)算方法耗時(shí)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();// 追加系統(tǒng)ClassLoaderclassPool.appendSystemPath();// 獲取要操作的類CtClass ctClass = classPool.get("com.artisan.agent.UserService");// 獲取方法CtMethod originMethod = ctClass.getDeclaredMethod("say2");// copy 一個(gè)新的方法CtMethod newMethod = CtNewMethod.copy(originMethod,ctClass,null);// 設(shè)置新名字originMethod.setName(originMethod.getName()+ "$agent");// 對原方法進(jìn)行包裝,比如加計(jì)算方法耗時(shí) 帶有返回值的的 $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")));}
注意事項(xiàng)
- 所引用的類型,必須通過ClassPool獲取后才可以使用
- 代碼塊中所用到的引用類型,使用時(shí)必須寫全量類名
- 代碼塊內(nèi)容寫錯(cuò)了,只有在運(yùn)行時(shí)才報(bào)錯(cuò)
- javassist只接受單個(gè)語句或用大括號括起來的語句塊
- 動(dòng)態(tài)修改的類,必須在修改之前,jvm中不存在這個(gè)類的實(shí)例對象。修改方法的實(shí)現(xiàn)必須在修改的類加載之前進(jìn)行
參考
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
總結(jié)
以上是生活随笔為你收集整理的APM - Javassist 入门 生成一个简单类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: APM - Hello Javaage
- 下一篇: MySQL - 剖析MySQL索引底层数