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和