同生成的做法一样,添加和移除类成员只要去修改fields和methods中的元素即可。这里我们拿一个简单的类做例子,下面这个Task类,我们来移除isNeedRemove方法,并且添加一个int 类型的addedField属性。
package asm.core;
/**
* Created by yunshen.ljy on 2015/6/8.
*/
public class Task {
private int isTask = 0;
public void tellMe(){
System.out.println("call tellMe");
}
public void isNeedRemove(){
// do sth
}
}
首先我们可以构建一个Transformer用来处理ClassNode 中的fields和methods列表。为了方便维护和扩展,我们创建两个Transformer。一个RemoveMethodTransformer来移除方法,一个AddFieldTransformer来添加field。
读取Task的字节码,可以通过Core Api 的ClassReader,而且因为ClassNode继承了ClassVisitor,所以我们可以通过ClassReader的accept方法来处理,这里依旧是将ClassReader当做一个生产者,ClassNode是消费者。而且,有趣的是,ClassNode也有一个accept方法,也就是说,可以传递本身的事件给一个ClassVisitor。这个ClassVisitor会继续“消费”所有ClassNode 属性中的事件。
先来看一下RemoveMethodTransformer的实现:
package asm.tree;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.util.Iterator;
/**
* Created by yunshen.ljy on 2015/7/12.
*/
public class RemoveMethodTransformer {
private String fieldName;
private String fieldDesc;
public RemoveMethodTransformer(String fieldName, String fieldDesc) {
this.fieldName = fieldName;
this.fieldDesc = fieldDesc;
}
public void transform(ClassNode cn) {
Iterator<MethodNode> i = cn.methods.iterator();
while (i.hasNext()) {
MethodNode mn = i.next();
if (mn.name.equals(fieldName) ) {
i.remove();
}
}
}
}
可以看到transform方法利用迭代器来直接处理ClassNode对象的methods列表。下面再看一下AddFieldTransformer的实现。
package asm.tree;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
/**
* Created by yunshen.ljy on 2015/7/12.
*/
public class AddFieldTransformer {
private int fieldAccess;
private String fieldName;
private String fieldDesc;
public AddFieldTransformer(int fieldAccess, String fieldName, String fieldDesc) {
this.fieldAccess = fieldAccess;
this.fieldName = fieldName;
this.fieldDesc = fieldDesc;
}
public void transform(ClassNode cn) {
boolean isPresent = false;
for (Object fn : cn.fields) {
FieldNode ff = (FieldNode) fn;
if (fieldName.equals(ff.name)) {
isPresent = true;
break;
}
}
if (!isPresent) {
cn.fields.add(new FieldNode(fieldAccess, fieldName, fieldDesc, null, null));
}
}
}
最后,测试并利用ClassWriter来输出处理后的二进制字节流。我们刚好可以利用ClassNode的accept方法来让ClassWriter 能够Visit整个class,并且输出字节数组。
ClassReader cr = new ClassReader("asm.core.Task");
ClassNode cn = new ClassNode();
cr.accept(cn,0);
RemoveMethodTransformer rt = new RemoveMethodTransformer("isNeedRemove","V");
rt.transform(cn);
AddFieldTransformer at= new AddFieldTransformer(Opcodes.ACC_PRIVATE,"addedField","I");
at.transform(cn);
ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
byte[] toByte = cw.toByteArray();
当然,也可以自己写一个Adapter,来封装ClassNode的处理。通过构造器来传递ClassWriter,如下,将accept方法封装到visitEnd方法中,来绑定下一个消费事件:
public class MyClassAdapter extends ClassNode {
public MyClassAdapter(ClassVisitor cv) {
super(Opcodes.ASM4);
this.cv = cv;
}
@Override
public void visitEnd() {
accept(cv);
}
}
或者再增加一层代理来传递:
package asm.tree;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
/**
* Created by yunshen.ljy on 2015/7/14.
*/
public class MyClassAdapter extends ClassVisitor {
private ClassVisitor next;
public MyClassAdapter(ClassVisitor cv) {
super(Opcodes.ASM4,new ClassNode());
this.next = cv;
}
@Override
public void visitEnd() {
ClassNode cn = (ClassNode)cv;
cn.accept(cv);
}
}