美文网首页
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