Javassist基本用法汇总

更新时间:2023-05-21 19:41:33 阅读: 评论:0

Javassist基本⽤法汇总
最近项⽬需要对基础架构做增强,需要基于字节码在不侵⼊原有代码的情况下实现,故把javassist的基本⽤法过了⼀遍。这篇博客就是把主要讲讲为什么要⽤javassist以及javassist的基本⽤法。
1.为什么要使⽤javassist(上⼿成本低)
基于字节码增强的框架有两个ASM和javassit,下⾯是两个框架的特点以及对⽐
Javassist & ASM 对⽐
1.Javassist源代码级API⽐ASM中实际的字节码操作更容易使⽤
2.Javassist在复杂的字节码级操作上提供了更⾼级别的抽象层。Javassist源代码级API只需要很少的字节码知识,甚⾄不需要任何实际字节码知识,因此实现起来更容易、更快。
3.Javassist使⽤反射机制,这使得它⽐运⾏时使⽤Classworking技术的ASM慢。
总的来说ASM⽐Javassist快得多,并且提供了更好的性能。Javassist使⽤Java源代码的简化版本,然后将其编译成字节码。这使得Javassist⾮常容易使⽤,但是它也将字节码的使⽤限制在Javassist源代码的
限制之内。总之,如果有⼈需要更简单的⽅法来动态操作或创建Java类,那么应该使⽤Javassist API 。如果需要注重性能地⽅,应该使⽤ASM库。髋关节疼痛怎么办
2. javassist基本⽤法(基于
3.28.0-GA的版本)
Javassist 是⼀个开源的分析、编辑和创建Java字节码的类库. 其主要优点在于简单快速. 直接使⽤ java 编码的形式, ⽽不需要了解虚拟机指令, 就能动态改变类的结构, 或者动态⽣成类.
Javassist中最为重要的是ClassPool, CtClass, CtMethod以及CtField这⼏个类.
ClassPool: ⼀个基于Hashtable实现的CtClass对象容器, 其中键是类名称, 值是表⽰该类的CtClass对象
CtClass: CtClass表⽰类, ⼀个CtClass(编译时类)对象可以处理⼀个class⽂件, 这些CtClass对象可以从ClassPool获得
CtMethods: 表⽰类中的⽅法
CtFields: 表⽰类中的字段
2.1 ClassPool对象
2.1.1 ClassPool的创建
// 获取ClassPool对象, 使⽤系统默认类路径
ClassPool pool = new ClassPool(true);
// 效果与 new ClassPool(true) ⼀致
ClassPool pool1 = Default();
为减少ClassPool可能导致的内存消耗. 可以从ClassPool中删除不必要的CtClass对象. 或者每次创建新的ClassPool对象.
// 从ClassPool中删除CtClass对象
ctClass.detach();
// 也可以每次创建⼀个新的ClassPool, ⽽不是Default(), 避免内存溢出
ClassPool pool2 = new ClassPool(true);
2.1.2 classpath
通过 Default()获取的ClassPool使⽤JVM的classpath.在Tomcat等Web服务器运⾏时, 服务器会使⽤多个类加载器作为系统类加载器, 这可能导致ClassPool可能⽆法找到⽤户的类. 这时, ClassPool须添加额外的classpath才能搜索到⽤户的类.
// 将classpath插⼊到指定classpath之前
pool.inrtClassPath(new Class()));
// 将classpath添加到指定classpath之后
pool.appendClassPath(new Class()));
// 将⼀个⽬录作为classpath
pool.inrtClassPath("/xxx/lib");
2.2 CtClass对象
2.2.1 获取CtClass
// 通过类名获取 CtClass, 未找到会抛出异常
CtClass ctClass = ("com.kawa.ssist.JustRun");
真假玉的鉴别方法// 通过类名获取 CtClass, 未找到返回 null, 不会抛出异常
CtClass ctClass1 = OrNull("com.kawa.ssist.JustRun");
2.2.2 创建CtClass
挂烫机不出蒸汽怎么办// 复制⼀个类
CtClass ctClass2 = AndRename("com.kawa.ssist.JustRun", "com.kawa.ssist.JustRunq");
// 创建⼀个新类
CtClass ctClass3 = pool.makeClass("com.kawa.ssist.JustRuna");
// 通过class⽂件创建⼀个新类
CtClass ctClass4 = pool.makeClass(new FileInputStream(new File("/home/un/test/JustRun.class")));
2.2.3 CtClass基础信息
// 类名
String simpleName = SimpleName();
// 类全名
String name = Name();
// 包名
String packageName = PackageName();
// 接⼝
CtClass[] interfaces = Interfaces();
// 继承类
CtClass superclass = Superclass();
/
/ 获取类⽅法
CtMethod ctMethod = DeclaredMethod("getName()", new CtClass[] {(Name()), (Name())});
// 获取类字段
CtField ctField = Field("name");
// 判断数组类型
ctClass.isArray();
// 判断原⽣类型
ctClass.isPrimitive();
// 判断接⼝类型
ctClass.isInterface();
// 判断枚举类型
ctClass.isEnum();
// 判断注解类型
ctClass.isAnnotation();
// 冻结⼀个类,使其不可修改
ctClass.freeze ()
// 判断⼀个类是否已被冻结
ctClass.isFrozen()
// 删除类不必要的属性,以减少内存占⽤。调⽤该⽅法后,许多⽅法⽆法将⽆法正常使⽤,慎⽤
ctClass.prune()
//解冻⼀个类,使其可以被修改。如果事先知道⼀个类会被defrost,则禁⽌调⽤prune⽅法
ctClass.defrost()
2.2.4 CtClass类操作
// 添加接⼝
ctClass.addInterface(...);
// 添加构造器
ctClass.addConstructor(...);
// 添加字段
ctClass.addField(...);
// 添加⽅法
ctClass.addMethod(...);
2.2.5 CtClass类编译
// 获取字节码⽂件需要注意的是⼀旦调⽤该⽅法,则⽆法继续修改已经被加载的class
Class clazz = Class();
// 类的字节码⽂件
ClassFile classFile = ClassFile();
樱花开的季节// 编译成字节码⽂件, 使⽤当前线程上下⽂类加载器加载类, 如果类已存在或者编译失败将抛出异常
byte[] bytes = Bytecode();
2.3 CtMethod对象
2.3.1 获取CtMethod属性
CtClass ctClass5 = (Name());
CtMethod ctMethod = DeclaredMethod("lectOrder");最简单的折纸
// ⽅法名
String methodName = Name();
/
/ 返回类型
CtClass returnType = ReturnType();
// ⽅法参数, 通过此种⽅式得到⽅法参数列表
// 格式: com.Order(java.lang.String,java.util.List)
// ⽅法签名格式: (Ljava/lang/String;Ljava/util/List;Lcom/test/Order;)Ljava/lang/Integer;
// 获取⽅法参数名称, 可以通过这种⽅式得到⽅法真实参数名称
List<String> argKeys = new ArrayList<>();
MethodInfo methodInfo = MethodInfo();
CodeAttribute codeAttribute = CodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) Attribute(LocalVariableAttribute.tag);
int len = ParameterTypes().length;
// ⾮静态的成员函数的第⼀个参数是this
int pos = Modifier.Modifiers()) ? 0 : 1;
for (int i = pos; i < len; i++) {
argKeys.add(attr.variableName(i));
}
2.3.2 CtMethod⽅法体修改
// 在⽅法体前插⼊代码块
ctMethod.inrtBefore("");
// 在⽅法体后插⼊代码块
ctMethod.inrtAfter("");
// 在某⾏字节码后插⼊代码块
ctMethod.inrtAt(10, "");
// 添加参数
ctMethod.addParameter(CtClass);
// 设置⽅法名
ctMethod.tName("newName");
// 设置⽅法体 $0=this / $1,$2,$3... 代表⽅法参数
ctMethod.tBody("{$0.name = $1;}");
//创建⼀个新的⽅法
ctMethod.make("kawa",CtClass);
2.3.3 异常块 addCatch()
在⽅法中加⼊try catch块, 需要注意的是, 必须在插⼊的代码中, 加⼊return值$e代表异常信息.插⼊的代码⽚段必须以throw或return语句结束CtMethod m = ...;
CtClass etype = Default().get("java.io.IOException");
m.addCatch("{ System.out.println($e); throw $e; }", etype);
// 等同于添加如下代码:
嗟来之食try {
// the original method body
} catch (java.io.IOException e) {
System.out.println(e);
throw e;
}
2.4 特殊标识
$0
⽅法调⽤的⽬标对象. 它不等于this, 它代表了调⽤者. 如果⽅法是静态的, 则$0为null.
$1, $2 ..
⽅法的参数 m.inrtBefore("{ System.out.println($1); System.out.println($2); }");
$$
是所有⽅法参数的简写, 主要⽤在⽅法调⽤上. 例如:
// 原⽅法
move(String a,String b)
move($$)
move($1,$2)
/
/ 如果新增⼀个⽅法, ⽅法含有move的所有参数, 则可以这些写:
move($$, context)
move($1, $2, context)
$args
$args 指的是⽅法所有参数的数组,类似Object[],如果参数中含有基本类型,则会转成其包装类型。需要注意的时候,$args[0]对应的是$1,⽽不是$0,$0!=$args[0],$0=this $cflow
描写云的四字词语$cflow意思为控制流(control flow),是⼀个只读的变量,值为⼀个⽅法调⽤的深度。例
//原⽅法
int fact(int n) {
if (n <= 1)
return n;
el
return n * fact(n - 1);
}
//javassist调⽤
CtMethod cm = ...;
//这⾥代表使⽤了cflow
cm.uCflow("fact");
//这⾥⽤了cflow,说明当深度为0的时候,就是开始当第⼀次调⽤fact的⽅法的时候,打印⽅法的第⼀个参数
cm.inrtBefore("if ($cflow(fact) == 0)"
+ "    System.out.println(\"fact \" + $1);");
$_ 与 $r
$_是⽅法调⽤的结果;$r是返回结果的类型, ⽤于强制类型转换
Object result = ... ;
$_ = ($r)result;
$w
基本类型的包装类 Integer i = ($w)5;
$class
⼀个 java.lang.Class 对象, 表⽰当前正在修改的类
$sig
类型为 java.lang.Class 的参数类型数组
$type
⼀个 java.lang.Class 对象, 表⽰返回值类型
$proceed
关于风景的诗句调⽤表达式中⽅法的名称

本文发布于:2023-05-21 19:41:33,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/922374.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:字节   类型   需要   参数   对象   加载   个类   获取
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图