使用修改类
如果我们一次性修改的类比较多,如果想使用这些类,我们可以使用java.lang.instrument.ClassFileTransformer
public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new ClassFileTransformer() {
public byte[] transform(ClassLoader l, String name, Class c, ProtectionDomain d, byte[] b) throws IllegalClassFormatException {
ClassReader cr = new ClassReader(b);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = new ChangeVersionAdapter(cw);
cr.accept(cv, 0);
return cw.toByteArray();
}
});
}
- 删除类的相关成员
对于删除内部及外部类,我们可以在相关visit函数中不调用super方法即可。对于函数和成员变量我们需要在visit方法中返回null - 添加成员
我们只需调用ClassWriter的相关visit方法即可添加对应的成员
我们不能在visitSource visitOuterClass cisitAnnnotation或者visitAttribute之后添加visitField。我们只能在visitInnerClass visitField visitMethod或者visitEnd方法后调用visitField
不过一般来说,我们更倾向于在visitEnd后添加相应的visit函数。
我们在添加相关成员时一般倾向于选择类似_counter$或者4B7F来避免重名。 - 链式转换
对于复杂的修改或者翻译,我们可以使用链式的修改模式。我们可以在一个ClassVisitor中将其中的visitor方法分发到很多个ClassVisitor中,当然也可以将多个Visitor的消息派发到同一个ClassVisitor中。
一些工具类
-
Type, Type提供了Class对象和其内部描述相转换的方法。Type.getType(String.class).getDescriptor()方法返回"Ljava/lang/String"。Type也可用于描述方法,可以使用方法描述符或者Method对象来初始化Type对象。Type的getArgumentTypes 和 getReturnType 可以用于获取方法的参数类型及返回值类型。
-
TraceClassVisitor 对于转换后的class可以提供更好的可读性
ClassWriter cw = new ClassWriter(0); TraceClassVisitor cv = new TraceClassVisitor(cw, printWriter); cv.visit(...);...cv.visitEnd(); byte b[] = cw.toByteArray();
其打印结果如下:
// class version 49.0 (49) // access flags 1537
public abstract interface pkg/Comparable implements pkg/Mesurable{ // access flags 25
public final static I LESS = -1 // access flags 25
public final static I EQUAL = 0 // access flags 25
public final static I GREATER = 1 // access flags 1025
public abstract compareTo(Ljava / lang / Object;)I
}
-
CheckClassAdapter 由于ClassWriter对visit的调用并不会进行check,因此有可能会生成根本无法运行的class。对此我们可以使用CheckClassAdapter来进行校验,如果调用时机不对,其会抛出相关异常。使用方法类似TraceClassVisitor
-
ASMIfier 该类能在访问对应的方法的时候生成对应的生成改类的ASM代码。ASMifier也可以通过控制台来使用:java -classpath asm.jar:asm-util.jar \ org.objectweb.asm.util.ASMifier \ java.lang.Runnable 会生成如下代码:
package asm.java.lang;import org.objectweb.asm.*; public class RunnableDump implements Opcodes { public static byte[] dump() throws Exception { ClassWriter cw = new ClassWriter(0); FieldVisitor fv; MethodVisitor mv; AnnotationVisitor av0; cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "java/lang/Runnable", null, "java/lang/Object", null); { mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "run", "()V", null, null); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); } }
网友评论