欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > java >内容正文

java

APM - Javassist 入门 生成一个简单类

发布时间:2025/3/21 java 63 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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

类说明
ClassPoolJavassist的类池,使用ClassPool 类可以跟踪和控制所操作的类, 与 JVM ClassLoader相似
CtClassCtClass提供了类的操作,如在类中动态添加新字段、方法和构造函数、以及改变类、父类和接口的方法。
CtMethod类中的方法,通过它可以给类创建新的方法,还可以修改返回类型,访问修饰符等, 甚至还可以修改方法体内容代码
CtConstructor构造函数
CtField类的属性,通过它可以给类创建新的属性,还可以修改已有的属性的类型,访问修饰符等

Javassist 语法

项目Value
$0, $1, $2, …this and actual parameters
$argsAn array of parameters. The type of $args is Object[].
$$All actual parameters.For example, m($$) is equivalent to m($1,$2,…)
$cflow(…)cflow variable
$rThe result type. It is used in a cast expression.
$wThe wrapper type. It is used in a cast expression.
$_The resulting value
$sigAn array of java.lang.Class objects representing the formal parameter types
$typeA java.lang.Class object representing the formal result type.
$classA 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 入门 生成一个简单类的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。