zoukankan      html  css  js  c++  java
  • 使用ASM实现动态代理

    如果对我这段代码感兴趣,直接拷贝测试debug,要不然你不知道我写的是什么鬼,如果有什么问题,可以告诉我。

    一、实现动态代理,首先得考虑有应该定义哪些类,根据JDK的动态代理思想,那么它就应该有一个生成代理的类

    package com.asm_core;
    
    import java.io.PrintWriter;
    import java.lang.reflect.Method;
    import java.util.List;
    
    import org.objectweb.asm.ClassWriter;
    import org.objectweb.asm.Opcodes;
    import org.objectweb.asm.tree.ClassNode;
    import org.objectweb.asm.tree.FieldInsnNode;
    import org.objectweb.asm.tree.FieldNode;
    import org.objectweb.asm.tree.InsnList;
    import org.objectweb.asm.tree.InsnNode;
    import org.objectweb.asm.tree.JumpInsnNode;
    import org.objectweb.asm.tree.LabelNode;
    import org.objectweb.asm.tree.LdcInsnNode;
    import org.objectweb.asm.tree.MethodInsnNode;
    import org.objectweb.asm.tree.MethodNode;
    import org.objectweb.asm.tree.TypeInsnNode;
    import org.objectweb.asm.tree.VarInsnNode;
    import org.objectweb.asm.util.TraceClassVisitor;
    
    import com.asm_core.logic.AddLogic;
    
    /**
     * 生成代理对象
     * @author may
     *
     */
    public class GenSubProxy {
        
        //逻辑接口
        private AddLogic logic = null;
        //被代理类的所有方法
        private Method[] methods = null;
        
        private String classNamePrefix = null;
        
        private String descInfoPrefix = null;
        
        private String logicPkg = null;
        
        public GenSubProxy(AddLogic logic) {
            
            String logicClassName = AddLogic.class.getName();
            
            this.logicPkg = logicClassName.substring(0, logicClassName.lastIndexOf(AddLogic.class.getSimpleName())).replace(".", "/");
                    
            this.logic = logic;
            
        }
        
        
        public Object genSubProxy(Class<?> superClass) {
            
            //获得被代理类的方法集合
            methods = superClass.getDeclaredMethods();
            
            classNamePrefix = superClass.getName().substring(0, superClass.getName().lastIndexOf(superClass.getSimpleName()));
            
            descInfoPrefix = classNamePrefix.replace(".", "/");
            
            Object obj = null;
            try {
                PrintWriter pw = new PrintWriter(System.out, true);
                //生成ClassNode
                ClassNode cn = genClassNode(superClass);
                //ClassWriter.COMPUTE_FRAMES表示让asm自动生成栈图,虽然会慢上二倍。
                ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
                
                TraceClassVisitor tcv = new TraceClassVisitor(cw, pw);
                
                cn.accept(tcv);
                
                //cn.accept(cw);
                
                byte[] b = cw.toByteArray();
                
                MyClassLoader classLoader = new MyClassLoader(b);
                
                Class<?> proxy = classLoader.loadClass(classNamePrefix + "$Proxy");
                
                obj = proxy.newInstance();
                
                Method method = proxy.getDeclaredMethod("setLogic", AddLogic.class);
                
                method.invoke(obj, logic);
                
                Method meth = proxy.getDeclaredMethod("setMethods", Method[].class);
                 
                meth.invoke(obj, new Object[] {methods});
                 
            } catch (Exception e) {
                e.printStackTrace();
            }
            
            return obj;
        }
        
        public ClassNode genClassNode(Class<?> superClass) {
            
            String superName = superClass.getName().replace(".", "/");
            
            ClassNode cn = new ClassNode(Opcodes.ASM5);
            
        
            //定义代理类的类名
            cn.name = descInfoPrefix + "$Proxy";
            //超类为当前被代理类
            cn.superName = superName;
            
            cn.access = Opcodes.ACC_PUBLIC;
            
            cn.version = Opcodes.V1_8;
            
            cn = proxyMethod(cn);
            
        
            
            
            return cn;
            
        }
        
        @SuppressWarnings("all")
        public ClassNode proxyMethod(ClassNode cn) {
            
            List<MethodNode> list = cn.methods;
            
            List<FieldNode> fields = cn.fields;
            //这里赋初始值必须是Integer, Float, Long, Double 或者 String,null
            fields.add(new FieldNode(Opcodes.ACC_PUBLIC, "logic", "L" + logicPkg + "AddLogic;", null, null));
            //添加methods字段
            fields.add(new FieldNode(Opcodes.ACC_PUBLIC, "methods", "[Ljava/lang/reflect/Method;", null, null));
            //添加方法setLogic,用于设置Logic
            MethodNode mn = new MethodNode(Opcodes.ACC_PUBLIC, "setLogic", "(L" + logicPkg + "AddLogic;)V", null, null);
            //下面的指令相当于this.logic = logic;
            InsnList insnList = mn.instructions;
            
            insnList.add(new VarInsnNode(Opcodes.ALOAD, 0));
            
            insnList.add(new VarInsnNode(Opcodes.ALOAD, 1));
            
            insnList.add(new FieldInsnNode(Opcodes.PUTFIELD, descInfoPrefix + "$Proxy", "logic", "L" + logicPkg + "AddLogic;"));
            
            insnList.add(new InsnNode(Opcodes.RETURN));
            
            mn.maxLocals = 2;//定义最大的本地变量
            
            mn.maxStack = 2;//定义最大的操作数栈
            
            list.add(mn);
            //添加一个setMethods方法,用于设置methods字段
            MethodNode meth = new MethodNode(Opcodes.ACC_PUBLIC, "setMethods", "([Ljava/lang/reflect/Method;)V", null, null);
            //这段指令相当于this.methods = methods;
            InsnList methList = meth.instructions;
            
            methList.add(new VarInsnNode(Opcodes.ALOAD, 0));
            
            methList.add(new VarInsnNode(Opcodes.ALOAD, 1));
            
            methList.add(new FieldInsnNode(Opcodes.PUTFIELD, descInfoPrefix + "$Proxy", "methods", "[Ljava/lang/reflect/Method;"));
            
            methList.add(new InsnNode(Opcodes.RETURN));
            
            meth.maxLocals = 2;
            
            meth.maxStack = 2;
            
            list.add(meth);//
            //添加构造方法
            MethodNode init = new MethodNode(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
            //这里是调用父类构造器,相当于super();
            InsnList initList = init.instructions;
            
            initList.add(new VarInsnNode(Opcodes.ALOAD, 0));
            
            initList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, descInfoPrefix + "SayHello", "<init>", "()V", false));
            
            initList.add(new InsnNode(Opcodes.RETURN));
            
            init.maxLocals = 1;
            
            init.maxStack = 1;
            
            list.add(init);
            
            int count = 0;
            //循环创建需要代理的方法
            for (Method method : methods) {
                
                MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC,  method.getName(), DescInfo.getDescInfo(method), null, null);
                
    //            System.err.println(DescInfo.getDescInfo(method));
                
                InsnList il = methodNode.instructions;
                
                //获得参数的类型
                Class<?>[] clazz = method.getParameterTypes();
                
                //计算出参数会占用掉的本地变量表长度,long,double类型占用两个slot
                int len = LocalLen.len(clazz);
                //获得这个方法的参数个数,不包括this
                int size = clazz.length;
                //或的返回值类型
                Class<?> rtClazz = method.getReturnType();
                
                il.clear();
                /**
                 * 下面的一大段指令的意思是
                 * int index = count;
                 * Method method = methods[index];//从methods中获得对应的方法对象
                 * Object[] objs = new Object[]{arg0,arg1,arg2....};用来存方法传过来的参数值的
                 * try {
                 *         logic.addLogic(method, objs);
                 * } catch(Exception e) {
                 *         e.printStackTrace();
                 * }
                 */
                il.add(new LdcInsnNode(count));//
                
                il.add(new VarInsnNode(Opcodes.ISTORE, len + 1));
                
                il.add(new VarInsnNode(Opcodes.ALOAD, 0));
                
                il.add(new FieldInsnNode(Opcodes.GETFIELD, descInfoPrefix + "$Proxy", "methods", "[Ljava/lang/reflect/Method;"));
                
                il.add(new VarInsnNode(Opcodes.ILOAD, len + 1));
                
                il.add(new InsnNode(Opcodes.AALOAD));
                
                il.add(new VarInsnNode(Opcodes.ASTORE, len + 2));//将栈顶的method存到局部变量表中
                
                //将参数长度推到栈顶
                il.add(new LdcInsnNode(size));
                
                il.add(new TypeInsnNode(Opcodes.ANEWARRAY, "java/lang/Object"));//new 出一个Object的数组
                
                il.add(new VarInsnNode(Opcodes.ASTORE, len + 3));//将数组存到本地变量表中
                
                int index = 1;
                
                //将参数值全都存到数组中
                for(int i = 0; i < size; i++) {
                    
                    il.add(new VarInsnNode(Opcodes.ALOAD, len + 3));//将数组推到栈顶
                    
                    il.add(new LdcInsnNode(i));//下标
                    
                    int opcode = OpcodeMap.getOpcodes(clazz[i].getName());//获得当前是什么类型的参数,使用什么样类型的指令
                    //如果是long,double类型的index加2
                    if(opcode == 22 || opcode == 24) {
                        
                        il.add(new VarInsnNode(opcode,index));//将long或者double参数推到栈顶
                        index += 2;
                    } else {
                        
                        il.add(new VarInsnNode(opcode, index));//将参数推到栈顶
                        index += 1;
                    }
                    
                    
                    if(AutoPKG.auto(clazz[i].getName()) != null) {
                        
                        il.add(new MethodInsnNode(Opcodes.INVOKESTATIC, AutoPKG.auto(clazz[i].getName()), "valueOf", AutoPKG_valueOf.auto(clazz[i].getName()), false));
                        
                    }
                    
                    
                    il.add(new InsnNode(Opcodes.AASTORE));//将数据存到数组中
                    
                }
                
                
                il.add(new VarInsnNode(Opcodes.ALOAD, 0));//
                
                il.add(new FieldInsnNode(Opcodes.GETFIELD, descInfoPrefix + "$Proxy", "logic", "L" + logicPkg + "AddLogic;"));
                
                il.add(new VarInsnNode(Opcodes.ALOAD, len+2));
                
                il.add(new VarInsnNode(Opcodes.ALOAD, len + 3));
                
                il.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "" + logicPkg + "AddLogic", "addLogic", "(Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true));
                
                il.add(new TypeInsnNode(Opcodes.CHECKCAST, rtClazz.getName().replace(".", "/")));
                
                LabelNode label = new LabelNode();
                
                il.add(new JumpInsnNode(Opcodes.GOTO, label));
                //由于对栈图还是不太明白是啥意思,如果有知道的麻烦告知我一声
                //il.add(new FrameNode(Opcodes.F_SAME, 0, null, 0, null));
                
                il.add(new VarInsnNode(Opcodes.ASTORE, len + 4));
                
                il.add(new VarInsnNode(Opcodes.ALOAD, len + 4));
                
                il.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false));
                
                il.add(label);
                
                il.add(new InsnNode(OpcodeRt.getOpcodes(rtClazz.getName())));
                
                methodNode.maxLocals = 5+len;
                
                methodNode.maxStack = 5;
                
                list.add(methodNode);
                
                count ++;
            }
            
            return cn;
        }
        
        
    
    }

    二、有了生成代理的类,那么就还应该有个处理逻辑的接口

    package com.asm_core.logic;
    
    import java.lang.reflect.Method;
    
    /**
     * 这个类用来增加方法逻辑,类似于JDK代理的InvocationHandler
     * @author may
     *
     */
    public interface AddLogic {
        
        /**
         * 
         * @param method 被代理对象的方法对象
         * @param args 被代理方法的参数
         * @throws Exception
         */
        public Object addLogic(Method method, Object[] args) throws Exception ;
    
    }

    三、如果方法参数中存在基本类型参数,要自动打包成Object[] args,写个基本类型对应包装类助手

    package com.asm_core;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class AutoPKG {
        
        public static final Map<String, String> map = new HashMap<>();
        
        static {
            map.put("int", "java/lang/Integer");
            
            map.put("byte", "java/lang/Byte");
            
            map.put("short", "java/lang/Short");
            
            map.put("long", "java/lang/Long");
            
            map.put("boolean", "java/lang/Boolean");
            
            map.put("char", "java/lang/Character");
            
            map.put("float", "java/lang/Float");
            
            map.put("double", "java/lang/Double");
            
        }
        
        public static String auto(String type) {
            
            if(map.containsKey(type)) {
                
                return map.get(type);
            } else {
                
                
                return null;
            }
            
            
        }
    
    }

    四、基本类型对应包装类的valueOf方法的描述符

    package com.asm_core;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class AutoPKG_valueOf {
        
        public static final Map<String, String> map = new HashMap<>();
        
        static {
            map.put("int", "(I)Ljava/lang/Integer;");
            
            map.put("byte", "(B)Ljava/lang/Byte;");
            
            map.put("short", "(S)Ljava/lang/Short;");
            
            map.put("long", "(J)Ljava/lang/Long;");
            
            map.put("boolean", "(Z)Ljava/lang/Boolean;");
            
            map.put("char", "(C)Ljava/lang/Character;");
            
            map.put("float", "(F)Ljava/lang/Float;");
            
            map.put("double", "(D)Ljava/lang/Double;");
            
        }
        
        public static String auto(String type) {
            
            if(map.containsKey(type)) {
                
                return map.get(type);
            } else {
                
                return null;
            }
            
            
        }
    
    }

    五、方法描述符生成助手

    package com.asm_core;
    
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 用于生成字节码描述符的工具类
     * @author may
     *
     */
    public class DescInfo {
        
        public static final Map<String, String> map = new HashMap<>();
        
        static {
            
            map.put("int", "I");
            
            map.put("byte", "B");
            
            map.put("short", "S");
            
            map.put("long", "J");
            
            map.put("boolean", "Z");
            
            map.put("char", "C");
            
            map.put("float", "F");
            
            map.put("double", "D");
            
            map.put("void", "V");
            
        }
        
        public static String getDescInfo(Method method) {
            
            Class<?>[]  pt = method.getParameterTypes();
            
            Class<?>  rt = method.getReturnType();
            
            String rtStr = "V";
            
            if(map.containsKey(rt.getName())) {
                
                rtStr = map.get(rt.getName());
                
            } else {
                /**
                 * 如果不为空,那么就是数组
                 */
                Class<?> clazz= rt.getComponentType();//如果原来的rt是int[][]
                Class<?> oldClazz = clazz;//int[]
                int count = 0;
                if(oldClazz != null) {
                    rtStr = "";
                    while(clazz != null) {
                        count ++;//2
                        oldClazz = clazz;
                        clazz= clazz.getComponentType();
                    }
                    for(int i = 0; i < count; i++) {
                        rtStr += "[";
                        
                    }
                    if(map.containsKey(oldClazz.getName())) {
                        rtStr += map.get(oldClazz.getName());
                    } else {
                        rtStr += "L" + oldClazz.getName().replace(".", "/") + ";";
                    }
                } else {
                    
                    rtStr = "L" + rt.getName().replace(".", "/") + ";";
                }
            }
            
            
            String descInfo = "(";
            
            for (Class<?> class1 : pt) {
                String name = class1.getName();
                
                if(map.containsKey(name)) {
                    descInfo += map.get(name);
                    
                } else {
                    if(class1.getComponentType() != null) {
                        descInfo += class1.getName().replace(".", "/");
                        
                    } else {
                        
                        descInfo += ("L" + name.replace(".", "/") + ";");
                    }
                }
                
            }
            descInfo += ")" + rtStr;
            return descInfo;
            
        }
        
    
    }

    六、用于计算局部变量表的slot长度

    package com.asm_core;
    
    /**
     * 计算本地变量表的长度,long,double类型会占用两个slot
     * @author may
     *
     */
    public class LocalLen {
        
        
        public static int len(Class<?>[] clzz) {
            
            int count = 0;
            
            for (Class<?> class1 : clzz) {
                
                String str = class1.getName();
                if(str.equals("long") || str.equals("double")) {
                    
                    count += 2;
                    
                } else {
                    
                    count ++;
                }
            }
            
            return count;
        }
        
    }

    七、根据不同类型使用不同字节码指令助手类

    package com.asm_core;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.objectweb.asm.Opcodes;
    
    public class OpcodeMap {
    
        public static Map<String, Integer> map = new HashMap<>();
        
        static {
            
            
            map.put("int", Opcodes.ILOAD);
            
            map.put("byte", Opcodes.ILOAD);
            
            map.put("short", Opcodes.ILOAD);
            
            map.put("long", Opcodes.LLOAD);
            
            map.put("boolean", Opcodes.ILOAD);
            
            map.put("char", Opcodes.ILOAD);
            
            map.put("float", Opcodes.FLOAD);
            
            map.put("double", Opcodes.DLOAD);
            
            
        }
        
        public static int getOpcodes(String type) {
            
            if(map.containsKey(type)) {
                
                return map.get(type);
                
            } else {
                
                
                return Opcodes.ALOAD;
            }
            
        }
        
    }

    八、根据不同的返回类型使用不同字节码指令的助手类

    package com.asm_core;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.objectweb.asm.Opcodes;
    
    public class OpcodeRt {
    
        public static Map<String, Integer> map = new HashMap<>();
        
        static {
            
            
            map.put("int", Opcodes.IRETURN);
            
            map.put("byte", Opcodes.IRETURN);
            
            map.put("short", Opcodes.IRETURN);
            
            map.put("long", Opcodes.LRETURN);
            
            map.put("boolean", Opcodes.IRETURN);
            
            map.put("char", Opcodes.IRETURN);
            
            map.put("float", Opcodes.FRETURN);
            
            map.put("double", Opcodes.DRETURN);
            
            map.put("void", Opcodes.RETURN);
            
            
        }
        
        public static int getOpcodes(String type) {
            
            if(map.containsKey(type)) {
                
                return map.get(type);
                
            } else {
                
                
                return Opcodes.ARETURN;
            }
            
        }
        
    }

    九、自定义类加载器

    package com.asm_core;
    
    public class MyClassLoader extends ClassLoader {
        
        private byte[] b = null;
        
        public MyClassLoader(byte[] b) {
            
            this.b = b;
            
        }
        
        
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            
            return this.defineClass(name, b, 0, b.length);
        }
    
    }

    十、测试类

    1】实现逻辑接口

    package com.asm_core.test;
    
    import java.lang.reflect.Method;
    
    import com.asm_core.logic.AddLogic;
    
    /**
     * 实现了方法逻辑的类
     * @author may
     *
     */
    public class AddLogicImpl implements AddLogic {
        
        private Object sayHello;
        
        public AddLogicImpl(Object sayHello) {
            
            this.sayHello = sayHello;
            
        }
    
        @Override
        public Object addLogic(Method method, Object[] args) throws Exception {
            
            System.out.println("Hello");
            Object obj = method.invoke(sayHello, args);//我们可以在调用目标方法的周围增加逻辑
            System.out.println("baby");
            return obj;
        }
    
    }

    2】被代理类

    package com.asm_core.test;
    
    import java.util.Date;
    
    /**
     * 需要进行代理的类
     * @author Administrator
     *
     */
    public class SayHello {
        
        
        public void sayHello(String str_1, String str_2, int age, String[] args) {
            
            System.out.println(str_1 + " " + str_2 + "嘿嘿:" + age);
            
        }
        
        private String l() {
            System.out.println("private String l() {");
            return "";
            
        }
        
        public int[][] tt(int age, long l, double d) {
            System.out.println("public int[][] tt(int age) {");
            return new int[][]{};
        }
        
        public String[][] hh(long k, double d, Double dd) {
            System.out.println("public String[][] hh(long k, double d, Double dd) {");
            return null;
        }
        
        
        public String[][] hh(short age, byte[] arg, int a, float f, char c, long l, double d, int[][] ii, String str, String[][] ss, Date date) {
            System.out.println("public String[][] hh(short age, byte[] arg, int a, float f, char c, long l, double d, int[][] ii, String str, String[][] ss, Date date) {");
            return null;
        }
        
        /*public String[][] hh(Long l, Double d) {
            System.out.println("public String[][] hh(short age, byte[] arg, double d) {");
            return null;
        }*/
    
    
        
        
    }

    3】Test

    package com.asm_core.test;
    
    import java.util.Date;
    
    import com.asm_core.GenSubProxy;
    import com.asm_core.logic.AddLogic;
    
    public class Test {
        
    public static void main(String[] args) {
            
            SayHello sayHello = new SayHello();
            
            AddLogic logic = new AddLogicImpl(sayHello);
            
            GenSubProxy genSubProxy = new GenSubProxy(logic);
            
            Object obj = genSubProxy.genSubProxy(SayHello.class);
            
            SayHello sh = (SayHello) obj;
            
            sh.hh((byte)1, new byte[]{}, 1, 1f, 's',1, 1, new int[][]{{12}}, "", new String[][]{{"sdf","s"}}, new Date());
            
            sh.sayHello("sg", "srt", 234, new String[]{});
            
        }
    
    }

    十一、总结

    使用ASM实现动态代理,需要先学懂JVM虚拟机的字节码指令。在自己写字节码指令的时候,如果你忘记了某些代码的指令的实现,别忘记使用JDK的javap -c -v -private **.class。通过javap我们可以解决好多我们曾经感到疑惑的地方,比如为什么匿名内部类使用局部变量时这个局部变量不能变?为什么在字节码层面上不能直接将基本类型复制给Object类型?synchronized字节码中如何表述的。。。。。。

  • 相关阅读:
    SQL多表关联原理研究实验验证
    SQL多表关联原理研究实验验证
    vs2015如何设置不显示类或函数前的引用数量
    vs2015如何设置不显示类或函数前的引用数量
    Visual Studio 中突出显示的引用
    Visual Studio 中突出显示的引用
    GridControl标题及单元格内容居中显示
    GridControl标题及单元格内容居中显示
    DevExpress的GridControl控件设置自定义显示方法
    DevExpress的GridControl控件设置自定义显示方法
  • 原文地址:https://www.cnblogs.com/honger/p/6815322.html
Copyright © 2011-2022 走看看