安卓AOP三剑客:APT,AspectJ,Javassist
细究JVM栈帧&ASM字节码的核心技术
对比图
Hunter 一个底层基于ASM 和 Gradle Transform API 实现的框架
依赖 https://asm.ow2.io/index.html
implementation 'org.ow2.asm:asm:7.1'
implementation 'org.ow2.asm:asm-commons:7.1'
ClassReader读取字节码->Visitor处理字节码->ClassWriter生成字节码
以上这两步可以用插件 ASM Bytecode viewer 查看java文件,main方法完整代码生成的字节码是这样的,后面字节码插桩方法照着对应的地方翻译过去就可以了
public static main([Ljava/lang/String;)V throws java/lang/InterruptedException
INVOKESTATIC java/lang/System.currentTimeMillis ()J
LSTORE 1
LDC 100
INVOKESTATIC java/lang/Thread.sleep (J)V
INVOKESTATIC java/lang/System.currentTimeMillis ()J
LLOAD 1
LSUB
LSTORE 3
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
LDC "time: "
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
LLOAD 3
INVOKEVIRTUAL java/lang/StringBuilder.append (J)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
RETURN
MAXSTACK = 4
MAXLOCALS = 5
}
public class InjectTest {
public static void main(String[] args) throws InterruptedException {
//long l=System.currentTimeMillis();
Thread.sleep(100);
//long allTime = System.currentTimeMillis() - l;
//System.out.println("time: "+ allTime);
}
}
public class ASMUnitTest {
@Test
public void test() throws IOException {
//1
FileInputStream file=new FileInputStream("/Users/fred/AndroidStudioProjects/Test/app/src/test/java/com/example/test/asm/InjectTest.class");
//2
ClassReader classReader = new ClassReader(file);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
classReader.accept(new ClassVisitor(Opcodes.ASM5,classWriter) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
MyMethodVistor myMethodVistor = new MyMethodVistor(Opcodes.ASM5, methodVisitor, access, name, descriptor);
System.out.println("access:" + access + " name:" + name);
return myMethodVistor;
}
},ClassReader.EXPAND_FRAMES);
//3
byte[] bytes=classWriter.toByteArray();
FileOutputStream fos=new FileOutputStream("/Users/fred/AndroidStudioProjects/Test/app/src/test/java/com/example/test/asm/InjectTest.class");
fos.write(bytes);
fos.close();
}
public class MyMethodVistor extends AdviceAdapter{
/**
* Constructs a new {@link AdviceAdapter}.
*
* @param api the ASM API version implemented by this visitor. Must be one of {@link
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
* @param methodVisitor the method visitor to which this adapter delegates calls.
* @param access the method's access flags (see {@link Opcodes}).
* @param name the method's name.
* @param descriptor the method's descriptor (see {@link //Type Type}).
*/
protected MyMethodVistor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
super(api, methodVisitor, access, name, descriptor);
}
@Override
protected void onMethodEnter() {
super.onMethodEnter();
//方法进入 但是要有方法声明了ASMTest注解的地方才插入
//long l=System.currentTimeMillis();
//INVOKESTATIC java/lang/System.currentTimeMillis ()J
//LSTORE 1
invokeStatic(Type.getType("Ljava/lang/System;"),new Method("currentTimeMills","()J"));
int index = newLocal(Type.LONG_TYPE);
storeLocal(index,Type.LONG_TYPE);
}
@Override
protected void onMethodExit(int opcode) {
super.onMethodExit(opcode);
System.out.println("onMethodExit:"+opcode);
//这里退出前处理
//long allTime = System.currentTimeMillis() - l;
//System.out.println("time: "+ allTime);
}
/*@Override
public void monitorEnter() {
super.monitorEnter();
//同步代码块的进入
}
@Override
public void monitorExit() {
super.monitorExit();
//同步代码块的退出
}*/
}
MethodVisitor中的visitAnnotation方法
插件第二个tab可以直接生成字节码数组
transform gradle插件自动埋点
获取所有输入的class文件,逐一处理