您的位置:首页 > 运维架构 > 网站架构

java中如何使用asm动态的生成或修改一个class文件以及asm的架构思想

2012-06-21 08:47 1526 查看
在开发中一般情况下我们写好的代码然后编译成class文件并运行属于静态的class文件生成,那是不是class文件就只有静态生成一种啊,其实不然,在jdk的动态代理应用Proxy类就是已经使用了动态生成一个class文件来实现代理功能的,只是这一部分我们都看不到,而asm是一个专门的字节码动态生成的一个框架,其架构使用的是生产、消费和过滤模式对应的接口和类分别是ClassReader读取一个Class文件属于生产模式,classWriter是生产一个class文件属于消费模式,ClassAdapter是对ClassWriter的过滤,做修改功能的,classReader接受一个ClassWriter并在方法中调用ClassWriter的所有的方法来实现生产并消费。当我们修改一个类的时候就必须继承ClassAdapter利用ClassAdapter中包含的ClassWriter对象对class文件进行修改。如果只是动态生成一个class就直接使用classWriter和CodeWriter就可以了。运用asm进行动态生成和修改一个class文静必须了解class文件的结构和一些指令。但是CGLib(Code
Generator Library代码生成库)帮我们做了封装和扩展让我们更容易去实现这个功能,不用了解class文件的结构也可以做到。其中CGLib的类的动态代理就是利用asm字节码动态生成而实现的,在这里说一下CGLib只是对asm的封装和扩展,CGLib的对class文件及一个类的操作是基于asm的欢聚换说asm是CGLib的底层,关于asm可以参考asm使用手册。网上是可以下载的,有兴趣的话可以研究一下。下面给出一些实际操作的小例子:

public void generator()

{//自动生成一个class文件

ClassWriter cw=new ClassWriter(0);

cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT, "com/test/bean/Base", null,"com/test/bean/Test", new String[]{"com/test/bean/IDAO"});

cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC+Opcodes.ACC_FINAL, "name", "Ljava/lang/String;",null,"accp" ).visitEnd();

cw.visitField(Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC+Opcodes.ACC_FINAL, "age", "I",null, new Integer(20)).visitEnd();

cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"hello", "()V", null,null).visitEnd();

cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"add", "(Ljava/lang/Object;)V", null,null).visitEnd();

cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"update","(Ljava/lang/Object;)V",null,null).visitEnd();

cw.visitMethod(Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"delete","(Ljava/lang/Object;)V", null,null).visitEnd();

cw.visitEnd();

byte[] b=cw.toByteArray();//构建好一个class文件

ClassReader cr=new ClassReader(b);

ClassWriter cw2=new ClassWriter(0);

//给class文件添加一个方法

cr.accept(new AddMethodAdapter(cw2,Opcodes.ACC_PUBLIC+Opcodes.ACC_ABSTRACT,"get","(Ljava/lang/String;)Ljava/lang/Object;"), 0);

//从class文件中移除一个方法

//cr.accept(new RemoveMethodAdapter(cw2,"delete","(Ljava/lang/Object;)V"), false);

b=cw2.toByteArray();//生成新的class文件

try {

FileOutputStream out=new FileOutputStream(new File("D:Base.class"));

out.write(b);

out.close();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 移除方法过滤器

* @author liurm

*

*/

public class RemoveMethodAdapter extends ClassAdapter {

private String name;//方法名称

private String desc;//方法描述

public RemoveMethodAdapter(ClassVisitor classvisitor,String name,String desc) {

super(classvisitor);

// TODO Auto-generated constructor stub

this.name=name;

this.desc=desc;

}

public MethodVisitor visitMethod(int access, String name, String desc,

String signature, String[] exceptions) {

// TODO Auto-generated method stub

if(name.equals(this.name)&&desc.equals(this.desc))

{

return null;//移除方法

}

return super.visitMethod(access, name, desc, signature, exceptions);

}

}

/*

* 添加方法过滤器

* */

public class AddMethodAdapter extends ClassAdapter{

private int acc;//访问修饰符

private String name;//方法名称

private String desc;//方法描述

private boolean isMethodPersent;//是否已经添加

public AddMethodAdapter(ClassVisitor classvisitor,int acc,String name,String desc) {

super(classvisitor);

// TODO Auto-generated constructor stub

this.acc=acc;

this.name=name;

this.desc=desc;

}

public MethodVisitor visitMethod(int access, String name, String desc,

String signature, String[] exceptions) {

// TODO Auto-generated method stub

if(name.equals(this.name)&&desc.equals(this.desc))

{//方法已经添加

isMethodPersent=true;

}

return super.visitMethod(access, name, desc, signature, exceptions);

}

public void visitEnd() {

// TODO Auto-generated method stub

if(!isMethodPersent)

{//未添加是才添加

super.cv.visitMethod(acc, name, desc, null, null);

}

super.visitEnd();

}

}

class PrintClass implements org.objectweb.asm.ClassVisitor{

public void visitAttribute(Attribute attribute) {

// TODO Auto-generated method stub

}

public void visitEnd() {

// TODO Auto-generated method stub

System.out.println("}");

}

public void visitInnerClass(String name, String outername, String innername, int access) {

// TODO Auto-generated method stub

}

public void visit(int version, int access, String name, String signutre,

String supername, String[] interfacename) {

// TODO Auto-generated method stub

System.out.println(name+" extends "+supername+"{");

}

public AnnotationVisitor visitAnnotation(String desc, boolean visible) {

// TODO Auto-generated method stub

return null;

}

public FieldVisitor visitField(int access, String name, String desc,

String signature, Object value) {

// TODO Auto-generated method stub

System.out.println(desc+" "+name);

return null;

}

public MethodVisitor visitMethod(int access, String name, String desc,

String signature, String[] exceptions) {

// TODO Auto-generated method stub

System.out.println(name+desc);

return null;

}

public void visitOuterClass(String owner, String name, String desc) {

// TODO Auto-generated method stub

}

public void visitSource(String arg0, String arg1) {

// TODO Auto-generated method stub

}

}

生成Get,Set方法,一个普通的java类

ClassWriter cw=new ClassWriter(0);//手动计算内存

cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC,"com/test/bean/Student",null,"java/lang/Object",null);//声明一个类

cw.visitField(Opcodes.ACC_PRIVATE, "age", "I", null,new Integer(20)).visitEnd();//定义age字段

cw.visitField(Opcodes.ACC_PRIVATE, "name", "Ljava/lang/String;",null,"accp").visitEnd();//定义name字段

//构造函数

MethodVisitor mv4=cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);

mv4.visitCode();

mv4.visitVarInsn(Opcodes.ALOAD, 0);

mv4.visitMethodInsn(Opcodes.INVOKESPECIAL, "com/test/bean/Student", "<init>", "()V");

mv4.visitInsn(Opcodes.RETURN);

mv4.visitMaxs(1, 1);

mv4.visitEnd();

//构建getAge方法

MethodVisitor mv=cw.visitMethod(Opcodes.ACC_PUBLIC, "getAge", "()I", null,null);

mv.visitCode();

mv.visitVarInsn(Opcodes.ALOAD, 0);

mv.visitFieldInsn(Opcodes.GETFIELD, "com/test/bean/Student", "age", "I");

mv.visitInsn(Opcodes.IRETURN);

mv.visitMaxs(1, 1);

mv.visitEnd();

//构建setAge方法

MethodVisitor mv1=cw.visitMethod(Opcodes.ACC_PUBLIC, "setAge", "(I)V", null, null);

mv1.visitCode();

mv1.visitVarInsn(Opcodes.ALOAD, 0);

mv1.visitVarInsn(Opcodes.ALOAD, 1);

mv1.visitFieldInsn(Opcodes.PUTFIELD, "com/test/bean/Student", "age", "I");

mv1.visitInsn(Opcodes.RETURN);

mv1.visitMaxs(2, 2);

mv1.visitEnd();

//构建getName方法

MethodVisitor mv2=cw.visitMethod(Opcodes.ACC_PUBLIC, "getName", "()Ljava/lang/String;", null,null);

mv2.visitCode();

mv2.visitVarInsn(Opcodes.ALOAD, 0);

mv2.visitFieldInsn(Opcodes.GETFIELD, "com/test/bean/Student", "name", "Ljava/lang/String;");

mv2.visitInsn(Opcodes.ARETURN);

mv2.visitMaxs(1, 1);

mv2.visitEnd();

//构建setName方法

MethodVisitor mv3=cw.visitMethod(Opcodes.ACC_PUBLIC, "setName", "(Ljava/lang/String;)V", null, null);

mv3.visitCode();

mv3.visitVarInsn(Opcodes.ALOAD, 0);

mv3.visitVarInsn(Opcodes.ALOAD, 1);

mv3.visitFieldInsn(Opcodes.PUTFIELD, "com/test/bean/Student", "name", "Ljava/lang/String;");

mv3.visitInsn(Opcodes.RETURN);

mv3.visitMaxs(2, 2);

mv3.visitEnd();

//sayhello方法

MethodVisitor mv5=cw.visitMethod(Opcodes.ACC_PUBLIC, "sayhello", "(Ljava/lang/String;)Ljava/lang/String;", null, null);

mv5.visitCode();

mv5.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");

mv5.visitInsn(Opcodes.DUP);

mv5.visitLdcInsn("Hello:");

mv5.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");

mv5.visitVarInsn(Opcodes.ALOAD, 1);

mv5.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");

mv5.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");

mv5.visitInsn(Opcodes.ARETURN);

mv5.visitMaxs(3, 2);

mv5.visitEnd();

//静态块

MethodVisitor mv6=cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>","()V", null,null);

mv6.visitCode();

//System.out.println("Hello World!");

mv6.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");

mv6.visitLdcInsn("Hello World!");

mv6.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");

//创建数组int [] a=new int[5];

//mv6.visitInsn(Opcodes.ICONST_5);

//int [] a=new int[6];

mv6.visitLdcInsn(new Integer(6));

//mv6.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT);

mv6.visitVarInsn(Opcodes.NEWARRAY, Opcodes.T_INT);

mv6.visitVarInsn(Opcodes.ASTORE, 4);

mv6.visitInsn(Opcodes.RETURN);

mv6.visitMaxs(4,0);

mv6.visitEnd();

cw.visitEnd();

byte[] b=cw.toByteArray();//可以装载到JVM中了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐