架構師之路----ASM實現簡易AOP

原理

java class 被存儲在嚴格格式定義的.class文件裏,這些類文件擁有足夠的元數據來解析類中全部元素: 類名稱,方法,屬性,java字節碼指令。 ASM從類文件中讀取以上信息,並提供了訪問和修改這些信息的接口,進而改變類原有行爲。 對於ASM來講,java class被描述爲一棵樹,ASM使用Visitor模式遍歷整個二進制結構。java

反射,Proxy,元數據,ASM庫幫助Java實現了動態語言的能力。git

Proxy必須基於接口,Cglib不須要。github

使用方式

若是對類的修改是一次性的且原始類信息是可知的,能夠經過ASM直接編譯出修改過的class文件並保存到硬盤,以後運行再也不依賴ASM,和普通類沒有區別,這種方式一般須要自定義ClassLoader。api

若是不但願改變類原有功能,只是在運行期間修改/添加一些類信息,好比動態代理,AOP等,能夠在啓動時往Java虛擬機中掛一個用戶定義的hook程序,在裝入特定類的時候使用ASM改變特定類的字節碼,從而改變類行爲。this

進行AOP

目標操做類:

public class TestBean {
    public void asmEcho(){
        System.out.println("hello asm");
    }
}

加強操做類:

public class AopInteceptor {
    public static void before(){
        System.out.println(".......before().......");
    }

    public static void after(){
        System.out.println(".......after().......");
    }
}

AopClassAdapter繼承ClassVisitor:

public class AopClassAdapter extends ClassVisitor implements Opcodes {
    public AopClassAdapter(int i, ClassVisitor classVisitor) {
        super(i, classVisitor);
    }

    public void visit(
            int version,
            int access,
            String name,
            String signature,
            String superName,
            String[] interfaces) {
        //更改類名,並使新類繼承原有的類。
        super.visit(version, access, name + "_tmp", signature, name, interfaces);
        {//輸出一個默認的構造方法
            MethodVisitor mv = super.visitMethod(ACC_PUBLIC, "<init>",
                    "()V", null, null);
            mv.visitCode();
            mv.visitVarInsn(ALOAD, 0);
            mv.visitMethodInsn(INVOKESPECIAL, name, "<init>", "()V");
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
    }

    public MethodVisitor visitMethod(
            int access,
            String name,
            String desc,
            String signature,
            String[] exceptions) {
        if ("<init>".equals(name))
            return null;//放棄原有類中全部構造方法
        if (!name.startsWith("asm"))
            return null;// 只對asm開始的方法執行代理

        MethodVisitor mv = super.visitMethod(access, name,
                desc, signature, exceptions);
        return new AopMethodVisitor(this.api, mv);
    }
}

AopMethodVisitor繼承MethodVisitor:

public class AopMethodVisitor extends MethodVisitor implements Opcodes {
    public AopMethodVisitor(int i, MethodVisitor methodVisitor) {
        super(i, methodVisitor);
    }

    public void visitCode(){
        super.visitCode();
        this.visitMethodInsn(INVOKESTATIC,"com/xxx/beecho/framework/asm/AopInteceptor","before","()V",false);
    }

    public void visitInsn(int opcode) {
        if (opcode >= IRETURN && opcode <= RETURN)//在方法返回以前
        {
            this.visitMethodInsn(INVOKESTATIC, "com/xxx/beecho/framework/asm/AopInteceptor", "after", "()V", false);
        }
        super.visitInsn(opcode);
    }
}

針對於ASM生成的字節碼,經過Classloader生成代理類:

public class AopClassLoader extends ClassLoader implements Opcodes {

    public AopClassLoader() {
        super();
    }

    public AopClassLoader(ClassLoader parent) {
        super(parent);
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        if (!name.endsWith("_tmp"))
            return super.loadClass(name);
        try {
            ClassWriter cw = new ClassWriter(0);
            InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/xxx/beecho/framework/asm/TestBean.class");
            ClassReader reader = new ClassReader(is);
            reader.accept(new AopClassAdapter(ASM4, cw), ClassReader.SKIP_DEBUG);
            byte[] code = cw.toByteArray();

//            // -----
//            FileOutputStream fos = new FileOutputStream("E:\\code\\java\\TestBean_tmp.class");
//            fos.write(code);
//            fos.flush();
//            fos.close();

            return this.defineClass(name, code, 0, code.length);
        } catch (Throwable e) {
            e.printStackTrace();
            throw new ClassNotFoundException();
        }
//        return null;
    }
}

運行

public static void main( String[] args ) throws IOException {
        try {
            AopClassLoader classLoader = new AopClassLoader();
            Class<?> asmClass = classLoader.loadClass("com.xxx.beecho.framework.asm.TestBean_tmp");

            Object obj = asmClass.newInstance();
            Method method = asmClass.getMethod("asmEcho",null);
            method.invoke(obj,null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
.......before().......
hello asm
.......after().......

代碼地址

https://github.com/zhangcj/code-example/tree/master/framework/src/main/java/com/xxx/beecho/framework代理

相關文章
相關標籤/搜索