Core API之Class接口和组件
来源:互联网 发布:阿里的数据平台两个 编辑:程序博客网 时间:2024/05/17 04:10
Presentation
ASM生成和转换编译后class的API是基于ClassVisitor接口。简单部分通过简单方法调用(参数描述它们的内容,且返回类型为void)来访问。哪些内容长度不定且复杂的部分可以通过初始方法调用来访问,它返回附加的访问接口。visitAnnotation,visitField和visitMethod方法各自返回AnnotationVisitor、FieldVisitor和MethodVisitor。public interface ClassVisitor { void visit(int version, int access, String name, String signature, String superName, String[] interfaces); void visitSource(String source, String debug); void visitOuterClass(String owner, String name, String desc); AnnotationVisitor visitAnnotation(String desc, boolean visible); void visitAttribute(Attribute attr); void visitInnerClass(String name, String outerName, String innerName,int access); FieldVisitor visitField(int access, String name, String desc, String signature, Object value); MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions); void visitEnd();}public interface FieldVisitor { AnnotationVisitor visitAnnotation(String desc, boolean visible); void visitAttribute(Attribute attr); void visitEnd();}
ClassVisitor接口必须按以下顺序调用:visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )* ( visitInnerClass | visitField | visitMethod )* visitEnd
意味着,visit必须第一个被调用,紧接着最多调用一次visitSource,接着最多调用一次visitOuterClass,接着顺序调用多次visitAnnotation和visitAttribute,接着多次顺序调用visitInnerClass,visitFieldvisitMethod,调用最后visitEnd结束。ASM提供了三个基于ClassVisitor接口的核心组件来生成和转换class。
ASM提供了三个基于ClassVisitor接口的核心组件来生成和转换class。
CLassReader:将编译过的class转换为字节数组,且调用ClassVisitor实例的visitXxx方法,ClassVisitor作为参数传递给accept方法。可以看作是事件生产者。
ClassWriter:ClassVisitor接口的一个实现,它以二进制格式直接构建编译后的class。它产生包含编译后的class的二进制字节数组作为输出。可以看作是事件消费者。
ClassAdapter:ClassVisitor接口的一个实现,它委托所有来自另外一个ClassVisitor实例的方法调用。它可以看作是过滤器。
Parsing classes
转换一个已存在public class ClassPrinter implements ClassVisitor { public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { System.out.println(name + " extends " + superName + " {"); } public void visitSource(String source, String debug) { } public void visitOuterClass(String owner, String name, String desc) { } public AnnotationVisitor visitAnnotation(String desc,boolean visible) { return null; } public void visitAttribute(Attribute attr) { } public void visitInnerClass(String name, String outerName,String innerName, int access) { } public FieldVisitor visitField(int access, String name, String desc,String signature, Object value) { System.out.println("" + desc + " " + name); return null; } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { System.out.println("" + name + desc); return null; } public void visitEnd() { System.out.println("}"); }}第二步Classprinter与ClassReader一起使用,以便ClassReader产生的事件能被ClassPrinter来消费。
ClassPrinter cp = new ClassPrinter();ClassReader cr = new ClassReader("java.lang.Runnable");cr.accept(cp, 0);accept方法在Runnable类字节码最后一行被转换后被调用,并且会调用cp上的ClassVisitor方法。
Generating classes
生成Class的唯一组件是ClassWriter。package pkg;public interface Comparable extends Mesurable { int LESS = -1; int EQUAL = 0; int GREATER = 1; int compareTo(Object o);}
ClassWriter cw = new ClassWriter(0);cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "pkg/Comparable", null, "java/lang/Object", new String[] { "pkg/Mesurable" });cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I", null, new Integer(-1)).visitEnd();cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I", null, new Integer(0)).visitEnd();cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I", null, new Integer(1)).visitEnd();cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo", "(Ljava/lang/Object;)I", null, null).visitEnd();cw.visitEnd();byte[] b = cw.toByteArray();visit方法定义了class的头信息。V1_5是在ASM Opcodes接口中定义的常量,它指定了class的版本。ACC_XXX常量对应Java修饰符。这里我们指定
连续的调用visitField方法,用来定义三个接口域,第一个参数是修饰符,第二个参数是field的名称,第三个参数是field类型。第四个参数泛型,在这里没有使用泛型,故为null。最后一个参数是field的常量值,这个参数仅仅被常量域使用。这里没有annotation,调用visitEnd方法。 visitMethod调用被用来定义compareTo方法。第一个参数是修饰符,第二个是方法名称。第三个参数是方法的描述。第四个为泛型,最后一个参数为方法能抛出的异常数组。visitMethod方法返回MethodVisitor,用来定义方法的annotation、属性和最为重要的方法的代码。接着调用visitEnd方法返回MethodVisitor。
最后一次调用visitEnd用来通知cw类已经结束,调用toByteArray获取字节数组。
可以使用自定义ClassLoader来加载生成的class。
class MyClassLoader extends ClassLoader { public Class defineClass(String name, byte[] b) { return defineClass(name, b, 0, b.length); }}
Transforming classes
目前为止,ClassReader和ClassWriter组件被单独使用。联合使用这两个组件:
1)ClassReader生产的event直接传递给ClassWriter。
byte[] b1 = ...;ClassWriter cw = new ClassWriter();ClassReader cr = new ClassReader(b1);cr.accept(cw, 0);byte[] b2 = cw.toByteArray();//b2代表了与b1Class一样的类.2)在ClassReader和ClassWriter引入ClassAdapter
byte[] b1 = ...;ClasssWriter cw = new ClassWriter();ClassAdapter ca = new ClassAdapter(cw); // ca forwards all events to cwClassReader cr = new ClassReader(b1);cr.accept(ca, 0);byte[] b2 = cw.toByteArray(); // b2 represents the same class as b1转换链:ClassReader->ClassAdapter->ClassWriter
public class ChangeVersionAdapter extends ClassAdapter { public ChangeVersionAdapter(ClassVisitor cv) { super(cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { cv.visit(V1_5, access, name, signature, superName, interfaces); }}通过修改visit方法的其他参数,你可以完成其他转换。
Using transformed classes
转换过的class可以存储到磁盘上或使用ClassLoader加载,但是在一个ClassLoader内的class转换只能转换被该类的classLoader加载进来的类。如果你向转换所有class,你必须将转换置于ClassFileTransformer(定义在java.lang.instrument)内。public static void premain(String agentArgs, Instrumentation inst) { inst.addTransformer(new ClassFileTransformer() { public byte[] transform(ClassLoader l, String name, Class c, ProtectionDomain d, byte[] b) throws IllegalClassFormatException { ClassReader cr = new ClassReader(b); ClassWriter cw = new ClassWriter(cr, 0); ClassVisitor cv = new ChangeVersionAdapter(cw); cr.accept(cv, 0); return cw.toByteArray(); } });}
Removing class members
通过改变visitField和visitMethod方法中的access和name参数,就可以改变数据域和方法的访问修饰符或名字。这样作的效果就是数据域或方法被移除了。本例子就是移除外部类、内部类和源文件名称。public class RemoveDebugAdapter extends ClassAdapter { public RemoveDebugAdapter(ClassVisitor cv) { super(cv); } @Override public void visitSource(String source, String debug) { } @Override public void visitOuterClass(String owner, String name, String desc) { } @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { }}下例移除了指定方法public class RemoveMethodAdapter extends ClassAdapter { private String mName; private String mDesc; public RemoveMethodAdapter( ClassVisitor cv, String mName, String mDesc) { super(cv); this.mName = mName; this.mDesc = mDesc; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (name.equals(mName) && desc.equals(mDesc)) { // do not delegate to next visitor -> this removes the method return null; } return cv.visitMethod(access, name, desc, signature, exceptions); }}
Adding class members
public class AddFieldAdapter extends ClassAdapter { private int fAcc; private String fName; private String fDesc; private boolean isFieldPresent; public AddFieldAdapter(ClassVisitor cv, int fAcc, String fName, String fDesc) { super(cv); this.fAcc = fAcc; this.fName = fName; this.fDesc = fDesc; } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { if (name.equals(fName)) { isFieldPresent = true; } return cv.visitField(access, name, desc, signature, value); } @Override public void visitEnd() { if (!isFieldPresent) { FieldVisitor fv = cv.visitField(fAcc, fName, fDesc, null, null); if (fv != null) { fv.visitEnd(); } } cv.visitEnd(); }}
- Core API之Class接口和组件
- Core API Method之接口和组件
- Core API之Class类结构
- Core API之Class工具类
- 组件接口(API)设计指南[2]-类接口(class interface)
- core组件之基本数据结构和绘图函数
- API之Class学习
- Core API之Method结构
- java基础教程之Class类和反射API介绍
- 组件和接口
- 接口和组件
- 组件和接口
- 组件接口(API)设计指南-目录
- 【openCV入门之四】 Core组件进阶
- Spring架构详解之Core组件详解
- core组件之操作图像中的像素
- 函数API调用,COM组件接口调用和SOA服务调用
- asm-giude阅读笔记003(ASM核心API接口和对应组件---读取字节码)
- Oracle 数据类型及存储方式
- 关于二维码分块上色(彩色二维码)的算法研究
- [C++]数据结构:链表二叉树的创建与四种遍历方式
- 浙大CS延毕本科生,今年拒掉Google硅谷总部offer的就是他
- css
- Core API之Class接口和组件
- AJAX异步更改数据库
- 小学妹啊,不要在机房选择自动登录啊!!!
- XMLDecoder/XMLEncoder
- Ckeditor事件绑定
- Top 10 Java Debugging Tips with Eclipse(Eclipse调试Java的10个技巧)
- PHP捕捉错误并显示友好信息的方法(不使用try...catch)
- 八款开源 Android 游戏引擎 (巨好的资源)
- 数据结构专题——线段树