Tips: ASM使用访问者模式,学会访问者模式再看ASM更加清晰
用于读取字节码,父类是Object
主要作用:
主要使用的方法
public void accept(ClassVisitor classVisitor, int parsingOptions) { this.accept(classVisitor, new Attribute[0], parsingOptions); } // 第一个参数是访问者,第二个参数用于跳过读取字节码的一些信息,常用ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES,跳过没必要的调试和帧信息以缩小大小
通过继承ClassVisitor,重写visitxxx方法,在方法中对访问到的数据进行操作,然后传给下一个ClassVisitor的子类处理(如果有的话)。经过一个或多个ClassVisitor的访问链处理后,传到ClassWriter手里,由ClassWriter生成最终的字节码结果
ClassVisitor方法通过构造函数可以传递下一个Visitor进去,如同链表一样,一个个的形成访问链,最后链接到ClassWriter这个特殊的ClassVisitor里去形成结果
被修改的类
package example; public class TestClass01 { public int a; public int b; public TestClass01() { System.out.println("init!!"); } }
ASM代码
package example.modify; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import java.io.IOException; import example.modify.clsvisitor.*; import org.objectweb.asm.Opcodes; import utils.FileUtils; public class Test01 { public static void main(String[] args) throws IOException { String path = FileUtils.getFilePath("example/sample/TestClass01.class"); FileUtils.writeBytes(path, modifyClass()); } public static byte[] modifyClass() { ClassReader cr = null; try { cr = new ClassReader("example.TestClass01"); } catch (IOException e) { e.printStackTrace(); } ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); ClassVisitor cv = new TestClass01Visitor(Opcodes.ASM9, cw); cr.accept(cv, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); return cw.toByteArray(); } }
package example.modify.clsvisitor; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Opcodes; public class TestClass01Visitor extends ClassVisitor { private String addFieldN = "content"; private boolean flag1 = true; private String addStaticFieldN = "myID"; private int addStaticFieldV = 100; private boolean flag2 = true; private String delFieldN = "b"; public TestClass01Visitor(int api, ClassVisitor classVisitor) { super(api, classVisitor); } /** * 把java版本改成1.6 全限定名改成sample包下,避免两个同样的类冲突 * @param version * @param access * @param name * @param signature * @param superName * @param interfaces */ @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(Opcodes.V1_6, access, "example/sample/TestClass01", signature, superName, interfaces); } /** * 添加两个成员删一个成员 * @param access * @param name * @param descriptor * @param signature * @param value * @return */ @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { if (name.equals(delFieldN)) return null; if (name.equals(addFieldN)) flag1 = false; // 存在就不添加 if (name.equals(addStaticFieldN)) flag2 = false; return super.visitField(access, name, descriptor, signature, value); } @Override public void visitEnd() { super.visitField( Opcodes.ACC_PUBLIC, this.addFieldN, "Ljava/lang/String;", null, "" ); super.visitField( Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, this.addStaticFieldN, "I", null, this.addStaticFieldV ); super.visitEnd(); } }
生成的字节码结果(idea反编译后)