美文网首页
Java ASM 与Aop简单实现(Version:asm5.0

Java ASM 与Aop简单实现(Version:asm5.0

作者: MicoCube | 来源:发表于2019-01-16 16:54 被阅读0次
package com.coding.asm.aop;

public class TestBean {
    public void halloAop() {
        System.out.println("Hello");
    }
}
  • 我希望在执行helloAop方法体之前和之后各插入Method beforeMethod after!两句话,也就是说在执行helloAop方法体之前和之后各执行beforeInvokeafterInvoke
package com.coding.asm.aop;

public class AopInterceptor {
    public static void beforeInvoke() {
        System.out.println("Method before!");
    };
    public static void afterInvoke() {
        System.out.println("Method after!");
    };
}
  • 代理类处理类
package com.coding.asm.aop;

import com.coding.asm.generator.AsmUtils;
import com.coding.asm.generator.ClassType;
import com.coding.asm.generator.Method;
import com.coding.asm.generator.TypeDescriptor;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.GeneratorAdapter;

import java.util.ArrayList;

public class AopClassAdapter extends ClassVisitor implements Opcodes {
    private String suffix;
    private AsmUtils asmUtils ;
    public AopClassAdapter(int api,ClassWriter classWriter,String suffix) {
        super(api, classWriter);
        this.suffix = suffix;
        asmUtils = new AsmUtils(classWriter);
    }
    public void visit(int version, int access, String name,
                         String signature, String superName, String[] interfaces) {

        System.out.println("className:"+name);

        String newClassName = StringUtils.isEmpty(suffix) ? name : name + suffix;
        //更改类名,并使新类继承原有的类。

        super.visit(version, access, newClassName, signature, name, interfaces);
            //输出一个默认的构造方法

            Method iterator = asmUtils.genMethod(Opcodes.ACC_PUBLIC,
                    "<init>",
                    new ClassType(TypeDescriptor.VOID),
                    new ArrayList() {{}}, null,null);
            GeneratorAdapter construct = asmUtils.writeMethodHeader(iterator);
            asmUtils.genDefaultConstructor(construct,name);
            asmUtils.returnAndEndMethod(construct);


    }
    public MethodVisitor visitMethod(int access, String name,
                             String desc, String signature, String[] exceptions) {
        // 只对halloAop方法执行代理
        if (!name.equals("halloAop"))
            return null;
        MethodVisitor mv = super.visitMethod(access, name,
                                          desc, signature, exceptions);
        return new AopMethodVisitor(this.api, mv);
    }
}

  • 代理方法处理类
package com.coding.asm.aop;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class AopMethodVisitor extends MethodVisitor implements Opcodes {
    public AopMethodVisitor(int api, MethodVisitor mv) {
        super(api, mv);
    }

    /**
     * 访问方法头,只访问一次
     */
    public void visitCode() {
        super.visitCode();
        this.visitMethodInsn(INVOKESTATIC, "com/coding/asm/aop/AopInterceptor", "beforeInvoke", "()V");
    }
    public void visitInsn(int opcode) {
        if (opcode == RETURN) {//在返回之前安插after 代码。
            mv.visitMethodInsn(INVOKESTATIC, "com/coding/asm/aop/AopInterceptor", "afterInvoke", "()V");
        }
        super.visitInsn(opcode);
    }
}
  • BinaryClassLoader
package com.coding.classloader;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.StringTokenizer;


public class BinaryClassLoader {
    private final ArrayList m_bases;

    public BinaryClassLoader() {
        m_bases = new ArrayList();
    }

    public void addPaths(String list) {
        StringTokenizer tkzr = new StringTokenizer(list, File.pathSeparator);
        while (tkzr.hasMoreTokens()) {
            File file = new File(tkzr.nextToken());
            String spec = null;
            if (file.isFile() && file.getName().toLowerCase().endsWith(".jar")) {
                spec = "jar:file://" + file.getAbsolutePath() + "!/";
            } else if (file.exists()) {
                spec = "file://" + file.getAbsolutePath();
                if (!spec.endsWith("/")) {
                    spec += '/';
                }
            }
            if (spec != null) {
                m_bases.add(spec);
                System.out.println("add loader path:" + spec);
            }
        }
    }

    public byte[] getBytes(String name) {
        String pname = name.replace('.', '/') + ".class";
        for (int i = 0; i < m_bases.size(); i++) {
            String base = (String) m_bases.get(i);
            try {
                URL url = new URL(base + pname);
                InputStream is = url.openStream();

                // read the entire content into byte array
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] buff = new byte[1024];
                int length;
                while ((length = is.read(buff)) >= 0) {
                    bos.write(buff, 0, length);
                }
                return bos.toByteArray();

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                // normal event when not found relative to current base
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        throw new IllegalArgumentException("Class not found: " + name);
    }


    /**
     * 实际上是调用ClassLoader.defineClass()
     * @param b class文件字节流
     * @param classFullName eg:com.coding.asm.test.TestBean_Tmp
     * @return
     */
    public static Class loadClass(byte[] b,String classFullName) {
        // Override defineClass (as it is protected) and define the class.
        Class clazz = null;
        try {
            ClassLoader loader = ClassLoader.getSystemClassLoader();
            Class cls = Class.forName("java.lang.ClassLoader");
            java.lang.reflect.Method method =
                    cls.getDeclaredMethod(
                            "defineClass",
                            new Class[] { String.class, byte[].class, int.class, int.class });

            // Protected method invocation.
            method.setAccessible(true);
            try {
                Object[] args =
                        new Object[] { classFullName, b, Integer.valueOf(0), Integer.valueOf(b.length)};
                clazz = (Class) method.invoke(loader, args);
            } finally {
                method.setAccessible(false);
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
        return clazz;
    }
}
  • 入口程序
package com.coding.asm.aop;

import com.coding.asm.generator.AsmUtils;
import com.coding.asm.reflection.AsmReflectionUtils;
import com.coding.classloader.BinaryClassLoader;
import com.coding.path.PathUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;

import java.io.InputStream;
import java.net.URL;


/**
 * Created by micocube
 * ProjectName: coding
 * PackageName: com.coding.asm.test
 * User: micocube
 * Email: ldscube@gmail.com
 * CreateTime: 2019/1/11上午11:13
 * ModifyTime: 2019/1/11上午11:13
 * Version: 0.1
 * Description:
 * asm5.0.3 支持的jdk版本不超过1.8
 * Preference:
 *      java compiler:
 *          project bytecode version: 1.8
 *          target bytecode version: 1.8
 * Project Structure:
 *      SDKs: 1.8
 *      Project:
 *          Project SDK:1.8
 *          Project language level:8
 *      Modules:Language level:8
 * 否则会报错:
 * Exception in thread "main" java.lang.IllegalArgumentException
 *  at org.objectweb.asm.ClassReader.<init>(Unknown Source)
 *  at org.objectweb.asm.ClassReader.<init>(Unknown Source)
 *  at org.objectweb.asm.ClassReader.<init>(Unknown Source)
 *  at com.coding.asm.test.AopTest.main(AopTest.java:30)
 **/
public class AopTest {

    public static void main(String[] args) throws Exception{
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        InputStream is = PathUtils.getClassInputStream(TestBean.class);
//        System.out.println(new String(is.readAllBytes(),"UTF-8"));
        ClassReader reader = new ClassReader(is);
        reader.accept(new AopClassAdapter(Opcodes.ASM5,cw,"_Proxy"), 0);
        //
        byte[] code = cw.toByteArray();
        new AsmUtils().writeClass("/Users/micocube/Documents/Utils4j/target/classes/com/coding/asm/aop/TestBean_Proxy.class",code);
        Class bean_tmp = BinaryClassLoader.loadClass(code, "com.coding.asm.aop.TestBean_Proxy");


        Object instance = bean_tmp.getConstructor().newInstance();
        Method helloAop = bean_tmp.getDeclaredMethod("halloAop");
        System.out.println(bean_tmp);
        helloAop.invoke(instance);

    }
}
  • 输出信息:
className:com/coding/asm/aop/TestBean
Method:<init>###,Method Sign:()V###,Method desc:()V
[DEBUG-console] 2019/01/16,16:52:49.791|<init>: super class:com/coding/asm/aop/TestBean
class com.coding.asm.aop.TestBean_Proxy
Method before!
Hello
Method after!
  • 生成的TestBean_Proxy.class:
package com.coding.asm.aop;

public class TestBean_Proxy extends TestBean {
    public TestBean_Proxy() {
    }

    public void halloAop() {
        AopInterceptor.beforeInvoke();
        System.out.println("Hello Aop");
        AopInterceptor.afterInvoke();
    }
}

相关文章

网友评论

      本文标题:Java ASM 与Aop简单实现(Version:asm5.0

      本文链接:https://www.haomeiwen.com/subject/vximdqtx.html