ASM——Java 字节码操控框架

2016/12/24 Java

jdk中的proxy必须基于接口,封装了asm的cglib没有这种限制。

关于ASM

ASM是一个Java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制class文件,也可以在类被加载入Java虚拟机之前动态改变类行为。

AOP一种较好的实现方式就是修改class文件织入逻辑,cglib正是通过ASM实现动态代理。 若想直接在字节码中增加内容,要对class文件结构和Java字节码有一定的了解,如方法和字段的描述符。(若要用复杂的逻辑需要ASM-Bytecode工具,会直接生成ASM代码)

标示字符 含义
B 基本类型byte
C 基本类型char
I 基本类型int
J 基本类型long
Z 基本类型boolean
V 特殊类型void
L 对象类型,如Ljava/lang/String

idea可以直接查看每个类的字节码。

demo

demo用ASM实现删除类中的方法、改变类中的方法、加入aop

原始的类:

public class SrcClass {
	//要删除的方法
    public void change1() {
        System.out.println("i am change-one!");
    }

    public void change2() {
        System.out.println("i am change-two");
    }
}     

方法中要增加的逻辑

System.out.println("i am new");

要加载的aop方法

public class AopContent {

    public static void content(){
        System.out.println("i am aop");
    }
}

最后相当于要把SrcClass变成

public class SrcClass {
    public void change2() {
        System.out.println("i am new");
        System.out.println("i am aop");
        System.out.println("i am change-two");
    }
}   

安插 Aop、删除方法和修改方法的ASM代码

public class AopMethodVisitor extends MethodVisitor implements Opcodes {

    public AopMethodVisitor(int i, MethodVisitor methodVisitor) {
        super(i, methodVisitor);
    }

    @Override
    public void visitCode() {
        super.visitCode();
        this.visitMethodInsn(INVOKESTATIC, "cn/qs/asm/AopContent", "content", "()V", false);
    }
}       
class DemoVisitor extends ClassVisitor implements Opcodes {

    public DemoVisitor(int api, ClassVisitor cv) {
        super(api, cv);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if (name.equals("change1")) {
            return null;
        }
        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        if (name.equals("change2")) {
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitLdcInsn("i am new");
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V",false);
            return new AopMethodVisitor(this.api, mv);
        }
        return mv;
    }
}        

ASM改写Java类

public class App extends ClassLoader implements Opcodes {
    public static void main(String[] args) throws IOException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, InvocationTargetException, NoSuchMethodException {
        ClassReader cr=new ClassReader(SrcClass.class.getName());
        ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS);
        DemoVisitor myv=new DemoVisitor(Opcodes.ASM4,cw);
        cr.accept(myv, 0);

        byte[] code=cw.toByteArray();

        //自定义加载器
        App loader=new App();
        Class<?> appClass=loader.defineClass(null, code, 0,code.length);
        appClass.getMethods()[0].invoke(appClass.newInstance(), new Object[]{});
        Method[] methodArray = appClass.getMethods();
    }       

最后输出结果:

 i am new
 i am aop
 i am change-two
 

同时查看methodArray中已经没有方法change1

Search

    Post Directory