读书人

应用ASM来书写Java代码(转)

发布时间: 2013-01-23 10:44:49 作者: rapoo

使用ASM来书写Java代码(转)


要了解ASM,参见:http://www.ibm.com/developerworks/cn/java/j-lo-asm30/
来源:http://nickcen.spaces.live.com/?_c11_BlogPart_BlogPart=summary&_c=BlogPart&partqs=amonth%3D3%26ayear%3D2006

1.???流程控制:

a)????????说明:JVM提供了基本的流程控制结构,这些结构都是基于Label而实现的。这些跳转指令,包括基于比较结果的有条件跳转和无条件的GOTO指令。另外,JVM也提供了实现switch结构的LOOKUPSWITCH和TABLESWITCH指令,其中LOOKUPSWITCH是基于键比较的,而TABLESWITCH则是基于键索引的,因此后者的匹配速度更快。

2.???接口声明:

ClassWriter cw = new ClassWriter(false);

cw.visit(V1_5,?ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "asm/IA", null,

"java/lang/Object", null);

cw.visitSource("IA.java", null);

cw.visitEnd();

?

等价于:

public interface IA{}

?

3.???类声明:

a)????????抽象类:

ClassWriter cw = new ClassWriter(false);

cw.visit(V1_5,?ACC_PUBLIC + ACC_SUPER + ACC_ABSTRACT, "asm/A", null,

"java/lang/Object", null);

cw.visitSource("A.java", null);

?

等价于:

public abstract class A{}

?

b)????????具体类:

ClassWriter cw = new ClassWriter(false);

cw.visit(V1_5,?ACC_PUBLIC + ACC_SUPER, "asm/A", null, "java/lang/Object", null);

cw.visitSource("A.java", null);

cw.visitEnd();

?

等价于:

public class A{}

?

1.???类:a)????????数组:??????????????????????i.??????????????创建:mv.visitInsn(ICONST_3);mv.visitIntInsn(NEWARRAY, T_INT);mv.visitVarInsn(ASTORE, 1);??????//???将数组引用存到局部变量栈1号的位置?等价于:int[] a = new int[3];????????????????????ii.??????????????取值:mv.visitVarInsn(ALOAD, 1);???????//???数组引用在局部变量栈1号的位置mv.visitInsn(ICONST_1);mv.visitInsn(IALOAD);mv.visitVarInsn(ISTORE, 2);?等价于:int b = a[1];?????????????????iii.??????????????赋值:mv.visitVarInsn(ALOAD, 1);mv.visitInsn(ICONST_1);mv.visitInsn(ICONST_2);mv.visitInsn(IASTORE);?等价于:a[1] = 2;?b)????????构造函数:??????????????????????i.??????????????<init>:1.????????创建:MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);mv.visitCode();mv.visitVarInsn(ALOAD, 0);mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", ()V");mv.visitInsn(RETURN);mv.visitMaxs(1, 1);mv.visitEnd();?说明:构造函数<init>在执行时,需要首先执行父类的构造函数或者类内部其他构造函数。?2.????????调用:mv.visitTypeInsn(NEW, "asm/A");mv.visitInsn(DUP);mv.visitMethodInsn(INVOKESPECIAL, "asm/A", "<init>", "()V");mv.visitVarInsn(ASTORE, 1);?等价于:A a = new A();?说明:在初始化一般对象时,我们需要先调用NEW指令,来创建该对象实例。而由于后续的INVOKESPECIAL指令是调用类的构造函数,而该指令执行完以后,对对象的引用将从栈中弹出,所以在NEW指令执行完以后,INVOKESPECIAL指令执行以前,我们需要调用DUP指令,来增加对象引用的副本。????????????????????ii.??????????????<clinit>:1.????????创建:MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);mv.visitCode();mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out","Ljava/io/PrintStream;");mv.visitLdcInsn("hello world");mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println","(Ljava/lang/String;)V");mv.visitInsn(RETURN);mv.visitMaxs(2, 0);mv.visitEnd();?等价于:static {System.out.println("hello world");}?2.????????调用:<clinit>在类被加载时自动调用。?c)????????字段:??????????????????????i.??????????????一般字段:1.????????创建:FieldVisitor fv = cw.visitField(ACC_PRIVATE, "a", "I", null, null);fv.visitEnd();?等价于:private int a;?2.????????读取:读取类当中名字为a,类型为int的字段的值。mv.visitVarInsn(ALOAD, 0);mv.visitFieldInsn(GETFIELD, "asm/A", "a", "I");?3.????????设置:mv.visitVarInsn(ALOAD, 0);mv.visitInsn(ICONST_2);mv.visitFieldInsn(PUTFIELD, "asm/A", "a", "I");?等价于:a = 2;????????????????????ii.??????????????静态字段:1.????????创建:FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC, "a", "I", null,null);fv.visitEnd();?等价于:private static int a;?2.????????读取:mv.visitFieldInsn(GETSTATIC, "asm/A", "a", "I");?3.????????设置:mv.visitInsn(ICONST_2);mv.visitFieldInsn(PUTSTATIC, "asm/A", "a", "I");?等价于:a = 2;?d)????????方法:??????????????????????i.??????????????接口方法:1.????????定义:mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "getA", "()V", null, null);mv.visitEnd();?2.????????调用:mv.visitVarInsn(ALOAD, 1);mv.visitMethodInsn(INVOKEINTERFACE, "asm/IA", "getA", "()V");?等价于:public interface IA{?????public void geA();}public class A implements IA{?????public void geA(){…}}?IA a = new A();a.getA();????????????????????ii.??????????????一般方法:1.????????定义:MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getA", "()V", null, null);mv.visitCode();mv.visitInsn(RETURN);mv.visitMaxs(0, 1);mv.visitEnd();?等价于:public void getA() {}?2.????????调用:mv.visitVarInsn(ALOAD, 1);mv.visitMethodInsn(INVOKEVIRTUAL, "asm/A", "getA", "()V");?等价于:A a = new A():a.getA();?????????????????iii.??????????????静态方法:1.????????定义:MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "getA", "()V",null, null);mv.visitCode();mv.visitInsn(RETURN);mv.visitMaxs(0, 0);mv.visitEnd();?等价于:public static void getA() {}?2.????????调用:mv.visitMethodInsn(INVOKESTATIC, "asm/A", "getB", "()V");?等价于:A.getB();???????????????????????iv.??????????????说明:一般方法比静态方法在声明和调用时均要多传入一个this引用作为参数。另外,当使用INVOKESPECIAL来调用方法时,虚拟机将直接根据指令当中所指明的类类型来调用方法;而当使用INVOKEVIRTUAL来调用方法时,虚拟机将根据实例的实际类型来调用方法。?e)????????异常处理:??????????????????????i.??????????????声明:mv.visitTryCatchBlock(l0, l1, l1, "java/lang/Exception");mv.visitLabel(l0);mv.visitLabel(l1);…等价于:try {…} catch (Exception e) {…}?说明:在visitTryCatchBlock()当中,第一,二,三个参数均是Label实例,其中一,二表示try块的范围,三则是catch块的开始位置。而第四个参数则是异常类型。而当异常发生时,JVM将会将异常实例置于运行栈的栈顶。?

1.???Signature:

a)????????说明:J2SE 5.0为了支持范型,参数化参数,Annotation和枚举等新增特性,因此增加了一个Signature属性,作为类,字段,方法的Description之外的一个辅助机制。

?

2.???Annotation:

a)????????Annotation:

??????????????????????i.??????????????定义:

cw.visit(V1_5,?ACC_PUBLIC + ACC_ANNOTATION + ACC_ABSTRACT + ACC_INTERFACE,

"asm/AN", null, "java/lang/Object",?new String[]

{ "java/lang/annotation/Annotation" });

?

等价于:

public @interface AN {}

?

???????????????????ii.??????????????使用:通过ClassVisitor,FieldVisitor,MethodVisitor上的visitAnnotation()方法,来获取一个AnnotationVisitor实例,从而为类,字段,方法设置Annotation。

AnnotationVisitor?av0 = cw.visitAnnotation("Lasm/AN;", false);

av0.visitEnd();

?

@AN

public class A{}

?

b)????????属性:

??????????????????????i.??????????????定义:

mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "age", "()I", null, null);

mv.visitEnd();

?

mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "name", "()Ljava/lang/String;",

null, null);

av0 = mv.visitAnnotationDefault();

av0.visit(null, "A");

av0.visitEnd();

mv.visitEnd();

?

等价于:

public @interface AN {

?????int age();

?????String name() default "A";

}

?

???????????????????ii.??????????????使用:

av0 = cw.visitAnnotation("Lasm/AN;", false);

av0.visit("age", new Integer(1));

av0.visit("name", "B");

av0.visitEnd();

?

等价于:

@AN(age = 1, name = "B")

public class A {

}

?

3.???范型:

a)????????定义:

cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "asm/A", "<T::Lasm/IA;G:Lasm/B;>Ljava/lang/Object;Ljava/lang/Comparable;", "java/lang/Object", new String[] { "java/lang/Comparable" });

?

等价于:

public class A<T extends IA, G extends B> implements Comparable {

}

?

说明:在类定义当中使用范型时,需要增加Signature字段来添加范型信息。该Signature的组成是“<范型参数名:范型扩展的类:范型扩展的接口…>父类描述 接口描述”

?

b)????????范型字段:

??????????????????????i.??????????????定义:

FieldVisitor fv = cw.visitField(ACC_PRIVATE, "l", "Ljava/util/List;",

"Ljava/util/List<Ljava/lang/String;>;", null);

fv.visitEnd();

?

等价于:

private List<String> l;

?

说明:在声明范型字段时,需要增加Signature来增加范型信息。该Signature的组成是

“基类型描述<参数类型描述>”

?

???????????????????ii.??????????????使用:由于范型信息只是供编译器在编译时进行类型检查,而在编译以后该信息将会被擦除,因此在使用时与没有范型的情况一致。

?

c)????????范型方法:

??????????????????????i.??????????????定义:

mv = cw.visitMethod(ACC_PUBLIC, "getList", "(Ljava/util/Map;)Ljava/util/List;", "(Ljava/util/Map<Ljava/lang/String;Ljava/lang/Integer;>;)Ljava/util/List<Ljava/lang/String;>;", null);

?

等价于:

public List<String> getList(Map<String, Integer> maps) {…}

?

???????????????????ii.??????????????使用:由于范型信息只是供编译器在编译时进行类型检查,而在编译以后该信息将会被擦除,因此在使用时与没有范型的情况一致。

?

4.???枚举:

a)????????定义:

ClassWriter cw = new ClassWriter(false);

FieldVisitor fv;

MethodVisitor mv;

AnnotationVisitor av0;

?

cw.visit(V1_5, ACC_PUBLIC +?ACC_FINAL?+ ACC_SUPER +?ACC_ENUM, "asm/E",

"Ljava/lang/Enum<Lasm/E;>;", "java/lang/Enum", null);

cw.visitSource("E.java", null);

?

fv = cw.visitField(ACC_PUBLIC +?ACC_FINAL + ACC_STATIC + ACC_ENUM, "E1", "Lasm/E;",

null, null);

fv.visitEnd();?????????//???定义静态字段E1

?

fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC + ACC_ENUM, "E2", "Lasm/E;",

ull, null);

fv.visitEnd();?????????//???定义静态字段E2

?

fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC + ACC_SYNTHETIC, "ENUM$VALUES",

"[Lasm/E;", null, null);

fv.visitEnd();?????????//???定义存储所有枚举值的静态字段ENUM$VALUES

?

mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);

mv.visitCode();

?

//???初始化E1

mv.visitTypeInsn(NEW, "asm/E");

mv.visitInsn(DUP);

mv.visitLdcInsn("E1");

mv.visitInsn(ICONST_0);

mv.visitMethodInsn(INVOKESPECIAL, "asm/E", "<init>", "(Ljava/lang/String;I)V");

mv.visitFieldInsn(PUTSTATIC, "asm/E", "E1", "Lasm/E;");

?

//???初始化E2

mv.visitTypeInsn(NEW, "asm/E");

mv.visitInsn(DUP);

mv.visitLdcInsn("E2");

mv.visitInsn(ICONST_1);

mv.visitMethodInsn(INVOKESPECIAL, "asm/E", "<init>", "(Ljava/lang/String;I)V");

mv.visitFieldInsn(PUTSTATIC, "asm/E", "E2", "Lasm/E;");

?

//???初始化ENUM$VALUES,将E1,E2存入ENUM$VALUES当中

mv.visitInsn(ICONST_2);

mv.visitTypeInsn(ANEWARRAY, "asm/E");

mv.visitInsn(DUP);

mv.visitInsn(ICONST_0);

mv.visitFieldInsn(GETSTATIC, "asm/E", "E1", "Lasm/E;");

mv.visitInsn(AASTORE);

mv.visitInsn(DUP);

mv.visitInsn(ICONST_1);

mv.visitFieldInsn(GETSTATIC, "asm/E", "E2", "Lasm/E;");

mv.visitInsn(AASTORE);

mv.visitFieldInsn(PUTSTATIC, "asm/E", "ENUM$VALUES", "[Lasm/E;");

mv.visitInsn(RETURN);

mv.visitMaxs(8, 0);

mv.visitEnd();

?

mv = cw.visitMethod(ACC_PRIVATE, "<init>", "(Ljava/lang/String;I)V", null, null);

mv.visitCode();

mv.visitVarInsn(ALOAD, 0);

mv.visitVarInsn(ALOAD, 1);

mv.visitVarInsn(ILOAD, 2);

mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Enum", "<init>",

(Ljava/lang/String;I)V");

mv.visitInsn(RETURN);

mv.visitMaxs(3, 3);

mv.visitEnd();

?

//???使用arraycopy()方法,将ENUM$VALUES的值存入一个新数组当中,并返回。

mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "values", "()[Lasm/E;", null,

null);

...

mv.visitEnd();

?

//???返回某个枚举值的字符表示

mv = cw.visitMethod(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "valueOf",

"(Ljava/lang/String;)Lasm/E;", null, null);

...

mv.visitEnd();

?

cw.visitEnd();

?

等价于:

public enum E {

?????E1, E2

}

?

b)????????使用:

mv.visitFieldInsn(GETSTATIC, "asm/E", "E1", "Lasm/E;");

?

等价于:

E e = E.E1;

?

c)????????说明:从上面的代码可以看到,即使是一个简单的枚举,也需要使用很多的代码才能定义,因此更可行的办法是使用Java编译器来生成枚举。

读书人网 >开源软件

热点推荐