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

本文介绍了如何使用ASM库来实现Java代码的动态生成,包括流程控制、接口声明、类声明、数组操作、构造函数、静态块、字段访问、方法调用、异常处理、Signature、Annotation、范型和枚举的创建与使用。ASM是一个强大的字节码操作框架,常用于Java代码的字节码级操作和动态代理。
摘要由CSDN通过智能技术生成

来源: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. :

1. 创建:

MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,

"", "()V", null, null);

mv.visitCode();

mv.visitVarInsn(ALOAD, 0);

mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object",

"", ()V");

mv.visitInsn(RETURN);

mv.visitMaxs(1, 1);

mv.visitEnd();

说明:构造函数在执行时,需要首先执行父类的构造函数或者类内部其他构造

函数。

2. 调用:

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

mv.visitInsn(DUP);

mv.visitMethodInsn(INVOKESPECIAL, "asm/A",

"", "()V");

mv.visitVarInsn(ASTORE, 1);

等价于:

A a = new A();

说明:在初始化一般对象时,我们需要先调用NEW指令,来创建该对象实例。而由于

后续的INVOKESPECIAL指令是调用类的构造函数,而该指令执行完以后,对对象的引

用将从栈中弹出,所以在NEW指令执行完以后,INVOKESPECIAL指令执行以前,我们

需要调用DUP指令,来增加对象引用的副本。

ii. :

1. 创建:

MethodVisitor mv = cw.visitMethod(ACC_STATIC,

"", "()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. 调用:在类被加载时自动调用。

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",

"<:lasm>Ljava/lang/Object;Ljava/lang/Comparable;",

"java/lang/Object", new String[] {

"java/lang/Comparable" });

等价于:

public class A

extends B> implements Comparable

{

}

说明:在类定义当中使用范型时,需要增加Signature字段来添加范型信息。该Signature的组成是“父类描述

接口描述”

b)

范型字段:

i.

定义:

FieldVisitor fv = cw.visitField(ACC_PRIVATE, "l",

"Ljava/util/List;",

"Ljava/util/List;",

null);

fv.visitEnd();

等价于:

private List

l;

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

“基类型描述”

ii.

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

c)

范型方法:

i.

定义:

mv = cw.visitMethod(ACC_PUBLIC, "getList",

"(Ljava/util/Map;)Ljava/util/List;", "(Ljava/util/Map;)Ljava/util/List;",

null);

等价于:

public List

getList(Map 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;",

"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,

"", "()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",

"",

"(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",

"",

"(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,

"", "(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",

"",

(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编译器来生成枚举。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值