2.3 ASM-类-工具类
2.3 工具类
除了ClassVisitor类,以及相关的ClassReader和ClassWriter等组件, ASM在org.objectweb.asm.util还提供了一些工具类,对开发一个Class生成器和适配器非常有用,但在程序运行中不是必需的。 ASM同样也提供了一个在程序运行时,处理内部名、类型描述符和方法描述符的工具类。 所有工具都会在下面进行介绍。
2.3.1 类型
正如前面章节介绍的,ASM的API展示Java类型,像编译后的class中一样,即使用内部名、类型描述符。 使用源码中的展示方式,可以使代码更加易读。 但需要在ClassReader和ClassWriter中系统的转换,这样会降低性能。 这就是为什么ASM不透明的将内部名和类型描述符转换成源码中的格式。 但是ASM提供了一个Type类,方便在需要的时候进行手动转换。
一个Type对象代表一种Java类型,可以由类型描述符或者Class对象创建。 Type类中也包含了一些表示基本数据类型的静态变量。 例如Type.INT_TYPE代表了int类型的Type对象。
getInternalName方法会返回一个Type对象的內部名。 例如,Type.getType(String.class).getInternalName()会返回String类的内部名,即“java/lang/String”。 这个方法只能是类(class)或者接口(interface)类型调用。
getDescriptor方法会返回一个Type对象的类型描述符。 例如,相比于在代码中使用“Ljava/lang/String;”,可以使用“Type.getType(String.class).getDescriptor()”代替。 再者,相比于使用“I”,可以使用“Type.INT_TYPE.getDescriptor()”。
一个Type对象也可以表示一个方法类型。这些对象可以由方法描述符或者一个方法对象创建。 getDescriptor方法会返回该类型相对应的方法描述符。 除此以外,getArgumentTypes方法和getReturnType方法可以返回Type对象对应的参数类型和方法返回值类型。 例如,Type.getArgumentTypes(“(I)V”) 会返回包含一个Type.INT_TYPE元素的数组。 同样的,调用Type.getReturnType(“(I)V”)会返回一个Type.VOID_TYPE对象。
2.3.2. TraceClassVisitor
为了检查生成或者转换后的class是否是你所期望的,通过byte数组不会有所帮助,因为byte数组可读性太差。 使用文本表述会更容易阅读。这就是TraceClassVisitor所提供的功能。 顾名思义,这个类继承了ClassVisitor,为访问的class创建一个易读的文本描述。 这样,为了得到实际生成类可读性的描述,可以使用TraceClassVisitor代替ClassWriter。 或者,更胜一筹的是同时使用这两个类。 TraceClassVisitor除了默认的行为外,还可以委托所有调用它的方法到其他visitor上面,例如到一个ClassWriter:
ClassWriter cw = new ClassWriter(0);
TraceClassVisitor cv = new TraceClassVisitor(cw, printWriter);
cv.visit(...);
...
cv.visitEnd();
byte b[] = cw.toByteArray();
上面的代码创建了一个TraceClassVisitor代理所有对它的调用到cw上面,并且将所有的调用都以文本的方式输出到printWriter中。 例如,在2.2.3章中使用TraceClassVisitor,将会得到如下的输出:
// class version 49.0 (49)
// access flags 1537
public abstract interface pkg/Comparable implements pkg/Mesurable {
// access flags 25
public final static I LESS=-1
// access flags 25
public final static I EQUAL=0
// access flags 25
public final static I GREATER=1
// access flags 1025
public abstract compareTo(Ljava/lang/Object;)I
}
需要注意的是,为了跟踪链路中的情况,你可以在生成或者转换链的任何一个节点使用TraceClassVisitor,但需要在ClassWriter之前。 还需要注意的是,本章生成的class文本描述可以使用String.equals()进行比较。
2.3.3. CheckClassAdapter
ClassWriter不会检查方法调用的顺序是否合适,以及参数是否合法。 因此生成被Java虚拟机验证器所拒绝的非法class是可行的。 为了尽快的检测到这些错误,可以使用CheckClassAdapter类。 和TraceClassVisitor一样,该类也继承了ClassVisitor类,并且代理所有调用转发到其他ClassVisitor,比如转发给TraceClassVisitor对象或者ClassWriter对象。 然而,不同于打印被访问class的文本描述信息,在将调用转发到下一个visitor之前,该类会先检查方法调用顺序是否合适,以及参数是否合法, 如果有错误,会抛出一个IllegalStateException或者IllegalArgumentException异常对象。 为了检测class、打印文本描述,并且最终输出byte数组,你可以使用下面这种方式:
ClassWriter cw = new ClassWriter(0);
TraceClassVisitor tcv = new TraceClassVisitor(cw, printWriter);
CheckClassAdapter cv = new CheckClassAdapter(tcv);
cv.visit(...);
...
cv.visitEnd();
byte b[] = cw.toByteArray();
需要注意的是,如果这些class的visitor处在不同的调用链中,这些操作所执行的顺序也会不一样。 例如下面代码所示,检测的vistor会在跟踪的visitor之后执行:
ClassWriter cw = new ClassWriter(0);
CheckClassAdapter cca = new CheckClassAdapter(cw);
TraceClassVisitor cv = new TraceClassVisitor(cca, printWriter);
像TraceClassVisitor一样,为了检测节点中class的正确性,可以在生成或者转换调用链中的任意一点使用CheckClassAdapter,但顺序必须在ClassWriter之前:
2.3.4 ASMifier
ASMifier提供了一个代替TraceClassVisitor的后端调用(默认情况下使用一个Textifier,产生如上面所示文本描述)。 这个后端会根据TraceClassVisitor类所调用的每一个方法,打印出生成该方法的Java代码。 例如调用visitEnd()方法会打印‘cv.visitEnd();’。 产生的结果是,在后端调用ASMifier访问一个class的时候,就会打印出使用ASM构造该class的代码。 使用这个visitor访问编译后的class是非常有用的。 例如,如果不知道如何使用ASM生成一个编译后的class,你可以直接编写该类的源码,使用javac编译,最后使用ASMifier访问编译后的class。 就可以得到该编译类使用ASM生成的代码了。 ASMifier可以直接在命令行中使用,例如:
java -classpath asm.jar:asm-util.jar org.objectweb.asm.util.ASMifier java.lang.Runnable
生成的代码,使用缩进后,如下:
package asm.java.lang;
import org.objectweb.asm.*;
public class RunnableDump implements Opcodes {
public static byte[] dump() throws Exception {
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
"java/lang/Runnable", null, "java/lang/Object", null);
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "run", "()V", null, null);
mv.visitEnd();
cw.visitEnd();
return cw.toByteArray();
}
}
- Angular Service入门
- spring:如何用代码动态向容器中添加或移除Bean ?
- WebComponent魔法堂:深究Custom Element 之 标准构建
- druid 数据源 使用属性文件的一个坑
- Angular企业级开发(3)-Angular MVC实现
- spring: 加载远程配置
- java:如何让程序按要求自行重启?
- Angular学习-指令入门
- java:如何让程序按要求自行重启?
- linux:nohup 不生成 nohup.out的方法
- 让VIM支持Python2 by update-alternatives
- Angular中ngCookies模块介绍
- 如何让jboss eap 6.2+ 的多个war应用共享 jar 包?
- scala 学习笔记(07) 一等公民的函数
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 大点干!早点散----------Redis从入门到精通!!!
- Python入门摘要
- 这些 ECMAScript 模块知识,都是我需要知道的
- 微信小程序【事件绑定】入门一篇就搞定
- 一个后端狗的 Vue 笔记【入门级】
- 这才是现代C++单例模式简单又安全的实现
- Linux 学习笔记(1) 查看文件内容
- Python从入门到熟练(3):第一个程序
- 数学系的概率论和我们的不太一样。。。
- 如何实现一个优雅的Python的Json序列化库
- 还在从零开始搭建项目?手撸了款快速开发脚手架!
- Node.js 中的 require 是如何工作的?
- Ajax请求携带Cookie
- 关于kubernetes垃圾回收那点事
- 强化学习笔记11:工程师看强化学习