Tips: ASM使用访问者模式,学会访问者模式再看ASM更加清晰
ClassReader
用于读取字节码,父类是Object
主要作用:
- 分析字节码里各部分内容,如版本、字段等等
- 配合其他Visitor使用
主要使用的方法
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反编译后)