1 首先创建一个实体类Student. 代码如下
package com.org.xcyz.asm;
public class Student {
private int id;
private String name;
private boolean sex;
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;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
}
2 然后准备添加字段age和setAge以及getAge方法
package com.org.xcyz.asm.trans;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.IRETURN;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RETURN;
public class AddVisitorTransformer1 {
public static void main(String[] args) throws IOException {
String className = "com.org.xcyz.asm.Student";
String classJvmName = className.replace('.', '/');
ClassReader cr = new ClassReader(className);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9,cw) {
boolean hasFiled = false;
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
if("age".equals(name) && "I".equals(descriptor))hasFiled=true;
return super.visitField(access, name, descriptor, signature, value);
}
@Override
public void visitEnd() {
if (!hasFiled) {
FieldVisitor fieldVisitor = cv.visitField(ACC_PRIVATE, "age", "I", null, null);
if (fieldVisitor != null) {
fieldVisitor.visitEnd();
}
MethodVisitor setAge = cv.visitMethod(ACC_PUBLIC, "setAge", "(I)V", null, null);
if (setAge != null) {
setAge.visitCode();
setAge.visitVarInsn(ALOAD,0);
setAge.visitVarInsn(ILOAD,1);
setAge.visitFieldInsn(PUTFIELD,classJvmName,"age","I");
setAge.visitInsn(RETURN);
setAge.visitMaxs(0,0);
setAge.visitEnd();
}
MethodVisitor getAge = cv.visitMethod(ACC_PUBLIC, "getAge", "()I", null, null);
if (getAge != null) {
getAge.visitCode();
getAge.visitVarInsn(ALOAD,0);
getAge.visitFieldInsn(GETFIELD,classJvmName,"age","I");
getAge.visitInsn(IRETURN);
getAge.visitMaxs(0,0);
getAge.visitEnd();
}
}
super.visitEnd();
}
};
cr.accept(classVisitor,ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
byte[] bytes = cw.toByteArray();
Files.write(Paths.get("target/classes/"+classJvmName+".class"),bytes,TRUNCATE_EXISTING,CREATE);
}
}
3. 动态添加字段的时机
3.1 visit方法中
override fun visit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
println("visit access $access className $className name $name signature $signature")
val numFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "age", "I", null, 25)
numFiledVisitor.visitEnd()
val strFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "str", "Ljava/lang/String;", null, "Hello World")
strFiledVisitor.visitEnd()
super.visit(version, access, name, signature, superName, interfaces)
}
3.2 visitEnd方法中
override fun visitEnd() {
val nameFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "name", "Ljava/lang/String;", null, "jdsjlzx")
nameFiledVisitor.visitEnd()
super.visitEnd()
}
4. 动态添加字段的初始化
通过ASM添加的字段,需要在ini方法中进行赋值
override fun onMethodEnter() {
println("onMethodEnter access $access className $className name $name descriptor $descriptor")
//动态生成的变量要在init方法中赋值(static变量除外)
if (name.equals("<init>") && descriptor.equals("()V")) {
//每个classFile都有一个<init>的初始化方法(固定写法)
mv.visitVarInsn(Opcodes.ALOAD, 0)
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
mv.visitVarInsn(Opcodes.ALOAD, 0)
//初始化定义的变量的值
mv.visitIntInsn(BIPUSH, 100)
mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "age", "I" )
//String的初始化会在构造方法中
mv.visitVarInsn(Opcodes.ALOAD, 0)
mv.visitLdcInsn("Hello World")
mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "str", "Ljava/lang/String" )
mv.visitVarInsn(Opcodes.ALOAD, 0)
mv.visitLdcInsn("jdsjlzx")
mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "name", "Ljava/lang/String" )
mv.visitInsn(RETURN)
}
super.onMethodEnter()
}
插桩后的class文件如下:
源码:jdsjlzx/AsmPluginProject
完整代码如下:
package com.baidu.plugin.print
import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.Attribute
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.Label
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.commons.AdviceAdapter
class PrintClassVisitor(nextVisitor: ClassVisitor, private val className: String): ClassVisitor(ASM9, nextVisitor),
Opcodes {
override fun visit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
println("visit access $access className $className name $name signature $signature")
val numFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "age", "I", null, 25)
numFiledVisitor.visitEnd()
val strFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "str", "Ljava/lang/String;", null, "Hello World")
strFiledVisitor.visitEnd()
super.visit(version, access, name, signature, superName, interfaces)
}
override fun visitEnd() {
val nameFiledVisitor = cv.visitField(Opcodes.ACC_PUBLIC, "name", "Ljava/lang/String;", null, "jdsjlzx")
nameFiledVisitor.visitEnd()
//准备添加字段age的setAge以及getAge方法(添加之前确保age字段存在)
val hasAgeField = true;
if (hasAgeField) {
var methodVisitor = cv.visitMethod(ACC_PUBLIC, "getAge", "()I", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, "com/baidu/main/MainActivity", "age", "I");
methodVisitor.visitInsn(IRETURN);
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();
methodVisitor = cv.visitMethod(ACC_PUBLIC, "setAge", "(I)V", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ILOAD, 1);
methodVisitor.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "age", "I");
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
}
super.visitEnd()
}
override fun visitField(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
value: Any?
): FieldVisitor {
//动态生成的file不会触发visitField方法,除非类中有声明的field
println("visitField access $access className $className name $name descriptor $descriptor")
val filedVisitor = super.visitField(access, name, descriptor, signature, value)
return MyPrintFieldVisitor(
Opcodes.ASM5,
filedVisitor
)
}
override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
val methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions)
return MyPrintMethodVisitor(
Opcodes.ASM5,
methodVisitor,
access,
className,
name,
descriptor
)
}
}
class MyPrintFieldVisitor(api: Int, filedVisitor: FieldVisitor) : FieldVisitor(api, filedVisitor) {
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
println("visitAnnotation descriptor $descriptor visible $visible")
return super.visitAnnotation(descriptor, visible)
}
}
class MyPrintMethodVisitor(
api: Int,
methodVisitor: MethodVisitor?,
private val access: Int,
private val className: String,
private val name: String?,
private val descriptor: String?
) : AdviceAdapter(api, methodVisitor, access, name, descriptor) {
override fun onMethodEnter() {
println("onMethodEnter access $access className $className name $name descriptor $descriptor")
//动态生成的变量要在init方法中赋值(static变量除外)
if (name.equals("<init>") && descriptor.equals("()V")) {
//每个classFile都有一个<init>的初始化方法(固定写法)
mv.visitVarInsn(Opcodes.ALOAD, 0)
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
mv.visitVarInsn(Opcodes.ALOAD, 0)
//初始化定义的变量的值
mv.visitIntInsn(BIPUSH, 100)
mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "age", "I" )
//String的初始化会在构造方法中
mv.visitVarInsn(Opcodes.ALOAD, 0)
mv.visitLdcInsn("Hello World")
mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "str", "Ljava/lang/String" )
mv.visitVarInsn(Opcodes.ALOAD, 0)
mv.visitLdcInsn("jdsjlzx")
mv.visitFieldInsn(PUTFIELD, "com/baidu/main/MainActivity", "name", "Ljava/lang/String" )
mv.visitInsn(RETURN)
}
if (name.equals("sum999")) {
//System.out.println("999999");
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("999999-begin")
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
super.onMethodEnter()
}
override fun onMethodExit(opcode: Int) {
println("onMethodExit access $access className $className name $name descriptor $descriptor")
if (name.equals("sum999")) {
//System.out.println("999999");
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("999999-end")
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
super.onMethodExit(opcode)
}
override fun visitFieldInsn(opcode: Int, owner: String?, name: String?, descriptor: String?) {
println("visitFieldInsn opcode $opcode owner $owner name $name descriptor $descriptor")
super.visitFieldInsn(opcode, owner, name, descriptor)
}
override fun visitLocalVariable(
name: String?,
descriptor: String?,
signature: String?,
start: Label?,
end: Label?,
index: Int
) {
println("visitLocalVariable name $name descriptor $descriptor start $start end $end index $index")
super.visitLocalVariable(name, descriptor, signature, start, end, index)
}
override fun visitAttribute(attribute: Attribute?) {
println("visitAttribute attribute $attribute")
super.visitAttribute(attribute)
}
override fun visitLineNumber(line: Int, start: Label?) {
println("visitLineNumber line $line start $start")
super.visitLineNumber(line, start)
}
override fun visitFrame(
type: Int,
numLocal: Int,
local: Array<out Any>?,
numStack: Int,
stack: Array<out Any>?
) {
println("visitFrame type $type numLocal $numLocal local $local numStack $numStack stack $stack")
super.visitFrame(type, numLocal, local, numStack, stack)
}
override fun visitLabel(label: Label?) {
println("visitLabel label $label")
super.visitLabel(label)
}
override fun visitCode() {
println("visitCode")
super.visitCode()
}
override fun visitMaxs(maxStack: Int, maxLocals: Int) {
println("visitMaxs maxStack $maxStack maxLocals $maxLocals")
super.visitMaxs(maxStack, maxLocals)
}
override fun visitEnd() {
println("visitEnd")
super.visitEnd()
}
}