ASM是怎样访问及修改编译了的class文件
2016-03-11 15:37
393 查看
java源码编译生成的class文件,其结构如何?
编译生成的class,其结构可以从java source 文件找到对应物。
一个编译后的class文件,包含以下与java source 文件一致的信息:
一个区域段,描述了修饰符(诸如privae,public),类名, super class, interface 和 anotation
每个field一个区域段,描述了field的修饰符,成员域名,类型,anotation
每个method一个区域段,描述了修饰符,方法名,返回值,参数类型,及方法的anotation
一个编译后的class文件,也有一些与java source文件不一致的地方:
compiled class描述的仅仅是一个class,源文件可以包含多个类(比如内部类等等),
于是编译后会有两个class文件, 其中一个是主class,另一个是内部类,主class包含了指向内部类的引用,内部class定义了method包含指向主类的引用
compiled class不包含注释,但是包含了与clas,field,method,attributes有关的anotation附加信息
compiled class不包含package 和 import区域的语句,因此所有的类型名称必须是全路径的
在compiled class当中, type是诸如java/lang/String , 由/隔开。在源文件里,是java.lang.String, 由点号隔开
类型的描述
java源代码中的类型,和编译后的class文件里的类型描述有区别,以下是对照表
方法的描述,去除了方法名称和参数的名称
ASM是怎样访问及修改编译了的class文件?
ASM是基于ClassVisitor接口方法的调用顺序,提供了3个核心组件,用于产生和修改转换class
ClassReader解析compiled class的byte数组,其accept方法接受classVisitor的实例参数,按顺序调用它的各个visitXxx方法。将它作为事件的生产者。
ClassWriter直接构建二进制形式的class,toByteArray返回compiled class的byte数组。将它作为事件的消费者
ClassAdapter内部包装了一个ClassVisitor实例,代理所有的ClassVisitor接口方法。将它作为事件过滤器。
访问ClassVisitor接口方法的顺序:
visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*
( visitInnerClass | visitField | visitMethod )*
visitEnd
首先必须调用visit, 然后最多访问一次visitSource, 然后最多调用一次visitOuterClass, 然后选出visitAnnotation或者visitAttribute之一,对它访问若干次,
然后选出visitInnerClass或者visitField或者visitMethod 之一, 对它调用若干次
最后必须调用一下visitEnd
以下例子说明了ClassReader如何使用accept方法, 依次按照所谓的顺序,调用 ClassPrinter作为ClassVisitor的实现类的visitXxx方法,访问java.util.Map的。
查看accept方法内部,可以发现这个所谓的调用顺序。
编译生成的class,其结构可以从java source 文件找到对应物。
一个编译后的class文件,包含以下与java source 文件一致的信息:
一个区域段,描述了修饰符(诸如privae,public),类名, super class, interface 和 anotation
每个field一个区域段,描述了field的修饰符,成员域名,类型,anotation
每个method一个区域段,描述了修饰符,方法名,返回值,参数类型,及方法的anotation
Modifiers, name, super class, interfaces |
Constant pool: numeric, string and type constants |
Source file name (optional) |
Enclosing class reference |
Annotation* |
Attribute* |
Inner class* Name |
Field* Modifiers, name, type |
Annotation* |
Attribute* |
Method* Modifiers, name, return and parameter types |
Annotation* |
Attribute* |
Compiled code |
Figure 2.1: Overall structure of a compiled class (* means zero or more) |
compiled class描述的仅仅是一个class,源文件可以包含多个类(比如内部类等等),
于是编译后会有两个class文件, 其中一个是主class,另一个是内部类,主class包含了指向内部类的引用,内部class定义了method包含指向主类的引用
compiled class不包含注释,但是包含了与clas,field,method,attributes有关的anotation附加信息
compiled class不包含package 和 import区域的语句,因此所有的类型名称必须是全路径的
在compiled class当中, type是诸如java/lang/String , 由/隔开。在源文件里,是java.lang.String, 由点号隔开
类型的描述
java源代码中的类型,和编译后的class文件里的类型描述有区别,以下是对照表
Java Type | Type descriptor |
boolean | Z |
char | C |
byte | B |
int | I |
float | F |
long | J |
double | D |
Object | Ljava/lang/Object |
int[] | [I |
Object[][] | [[Ljava/lang/Object |
源文件中的方法声明 | compiled class的方法描述 |
void m(int a, float f) | (IF)V |
int m(Object o) | (Ljava/lang/Object;)I |
int[] m(int a,String s) | (ILjava/lang/Object;)[I |
Object m(int[] a) | ([I)Ljava/lang/Object; |
ASM是基于ClassVisitor接口方法的调用顺序,提供了3个核心组件,用于产生和修改转换class
ClassReader解析compiled class的byte数组,其accept方法接受classVisitor的实例参数,按顺序调用它的各个visitXxx方法。将它作为事件的生产者。
ClassWriter直接构建二进制形式的class,toByteArray返回compiled class的byte数组。将它作为事件的消费者
ClassAdapter内部包装了一个ClassVisitor实例,代理所有的ClassVisitor接口方法。将它作为事件过滤器。
<span style="background-color: rgb(255, 255, 255);">package org.objectweb.asm; public abstract interface ClassVisitor { public abstract void visit(int paramInt1, int paramInt2, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString); public abstract void visitSource(String paramString1, String paramString2); public abstract void visitOuterClass(String paramString1, String paramString2, String paramString3); public abstract AnnotationVisitor visitAnnotation(String paramString, boolean paramBoolean); public abstract void visitAttribute(Attribute paramAttribute); public abstract void visitInnerClass(String paramString1, String paramString2, String paramString3, int paramInt); public abstract FieldVisitor visitField(int paramInt, String paramString1, String paramString2, String paramString3, Object paramObject); public abstract MethodVisitor visitMethod(int paramInt, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString); public abstract void visitEnd(); } </span>
访问ClassVisitor接口方法的顺序:
visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*
( visitInnerClass | visitField | visitMethod )*
visitEnd
首先必须调用visit, 然后最多访问一次visitSource, 然后最多调用一次visitOuterClass, 然后选出visitAnnotation或者visitAttribute之一,对它访问若干次,
然后选出visitInnerClass或者visitField或者visitMethod 之一, 对它调用若干次
最后必须调用一下visitEnd
以下例子说明了ClassReader如何使用accept方法, 依次按照所谓的顺序,调用 ClassPrinter作为ClassVisitor的实现类的visitXxx方法,访问java.util.Map的。
查看accept方法内部,可以发现这个所谓的调用顺序。
<span style="background-color: rgb(255, 255, 255);">/** * @{#} ClassPrinter.java Created on Jun 10, 2010 9:37:57 PM * * this code example from asm-guide.pdf * author. * */ package example.me; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; 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 AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null; } public void visitAttribute(Attribute attr) {} public void visitEnd() { System.out.println("}"); } public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { System.out.println(" " + desc + " " + name); return null; } public void visitInnerClass(String name, String outerName, String innerName, int access) {} public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { System.out.println(" " + name + desc); return null; } public void visitOuterClass(String owner, String name, String desc) {} public void visitSource(String source, String debug) {} public static void main(String[] args) throws Exception { ClassPrinter cp = new ClassPrinter(); ClassReader cr = new ClassReader("java.util.Map"); /* * InputStream input = Thread.currentThread().getContextClassLoader() * .getResourceAsStream( "java.lang.Runnable".replace('.', '/') + * ".class"); ClassReader cr = new ClassReader(input); */ cr.accept(cp, 0); //cp是ClassVisitor的实现类,在accept方法的内部,以既定的顺序调用cp.visitXxx(...)方法,最终输出java.util.Map的编译后class结构 } }</span>
<p style="margin-top: 0px; margin-bottom: 10px; padding-top: 0px; padding-bottom: 0px;"><span style="background-color: rgb(255, 255, 255);"> 输出结果:</span></p><p style="margin-top: 0px; margin-bottom: 10px; padding-top: 0px; padding-bottom: 0px;"><span style="background-color: rgb(255, 255, 255);">java/util/Map extends java/lang/Object { size()I isEmpty()Z containsKey(Ljava/lang/Object;)Z containsValue(Ljava/lang/Object;)Z get(Ljava/lang/Object;)Ljava/lang/Object; put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; remove(Ljava/lang/Object;)Ljava/lang/Object; putAll(Ljava/util/Map;)V clear()V keySet()Ljava/util/Set; values()Ljava/util/Collection; entrySet()Ljava/util/Set; equals(Ljava/lang/Object;)Z hashCode()I }</span></p>
相关文章推荐
- 每天一个linux命令,地址
- 手工配置tnsnames.ora的空格问题
- 解决[INSTALL_FAILED_UID_CHANGED]无法安装包问题
- 福利,一张图看懂IT售前工程师修炼之道
- [RxJS] Completing a Stream with TakeWhile
- 四种基本排序算法(java表示)
- 家长的疏忽,影响孩子的牙齿健康
- MyBatis3与Spring4整合方法详解(使用SqlSessionTemplate类)
- 质数与合数系列——素数判断
- 数据库设计三大范式
- 日期操作java和js类
- LoadRunner 之 IP欺骗
- linux命令:less 命令
- 面向对象的程序设计(1)
- 认识Integer
- C#:通过串口读取扫描枪里的值
- C# 程序集 和 反射
- SVM实现多分类的三种方案
- codeforces 149E . Martian Strings kmp
- Request failed: internal server error (500)