您的位置:首页 > 其它

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

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)
一个编译后的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文件里的类型描述有区别,以下是对照表

Java TypeType descriptor
booleanZ
charC
byteB
intI
floatF
longJ
doubleD
ObjectLjava/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是怎样访问及修改编译了的class文件?

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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: