盘古BPM体验地址    盘古BPM交流群盘古BPM交流群号:963222735

使用asm动态增加类中的成员

分享牛 2677℃

本文重点学习一下如何使用asm修改类中的字段或者方法,比如动态给指定的类增加一个成员变量,同其他文章一样,我们还是使用asm,这点一定要了解。

因为asm解析。读取字节码的时候是按照顺序流来的,解析的顺序如下:

 visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*  

( visitInnerClass | visitField | visitMethod )*  visitEnd  

因此,如果我们可以在visitEnd方法中,来完成方法或者成员的添加逻辑。下面开始代码演示:

1.同样还是为Person类添加一个成员,然后展开说明,Person类示例代码如下:

public class Person {

private int id;

private String name;

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}


}

2,我们这里期望达到的目标就是在Person类中添加一个成员,如下所示:

private String addFiled;

3.动态增加类成员,还是跟动态移除类中的方法做法类似,自定义一个类并继承ClassVisitor类,然后进行操作,示例代码如下:

public class ShareniuAddClassesVisitor extends ClassVisitor {

private int fAcc;

private String fName;

private String fDesc;

private String fVal;

private boolean isFieldPresent;

public ShareniuAddClassesVisitor(ClassVisitor cv, int fAcc, String fName, String fDesc,String fVal) {

super(Opcodes.ASM5, cv);

this.fAcc = fAcc;

this.fName = fName;

this.fDesc = fDesc;

this.fVal=fVal;

}

 

public ShareniuAddClassesVisitor(int api) {

super(Opcodes.ASM5);

}

 

public ShareniuAddClassesVisitor(ClassVisitor cv) {

super(Opcodes.ASM5, cv);

}

 

@Override

public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {

System.out.println("visit方法:");

 if (name.equals(fName)) {  

            isFieldPresent = true;  

        }  

super.visit(version, access, name, signature, superName, interfaces);

}

 

@Override

public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {

System.out.println("visitMethod:" + name);

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

}

 

@Override

public void visitEnd() {

  if (!isFieldPresent) {  

            FieldVisitor fv = cv.visitField(fAcc, fName, fDesc, null, fVal);  

            if (fv != null) {  

                fv.visitEnd();  

            }  

        }  

        cv.visitEnd();  

}

 

}

上述代码中,visitEnd方法逻辑如下:

1:首先判断需要添加的字段是否已经被添加到字节码中,如果已经添加了,无需再次添加,这样做的目的无外乎是防止重复的添加字段。

2:如果没有添加,则开始执行添加成员逻辑。

3:添加完毕之后,访问结束。

 

4.为类添加成员字段演示,示例代码如下:

public static void main(String[] args) throws Exception {

ClassReader cr = new ClassReader("com.shareniu.asm.helloword.Person");  

        ClassWriter cw = new ClassWriter(0);  

     ClassVisitor cv1 = new ShareniuAddClassesVisitor(cw,Opcodes.ACC_PRIVATE,"add1","I","shareniu");

        cr.accept(cv1, 0);  

        byte[] toByte = cw.toByteArray();// byt 和toByte其实是相同的数组  

        // 输出到class文件  

        FileOutputStream fout = new FileOutputStream("Shareniu.class");

        fout.write(toByte);  

        fout.close();  


}

上述代码执行完毕之后,新生成的字节码如下所示:

public class Person

{

  private int id;

  private String name;

  private int add1 = "shareniu";

换言之,我们已经添加成功。


转载请注明:分享牛 » 使用asm动态增加类中的成员