如果对我这段代码感兴趣,直接拷贝测试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字节码中如何表述的。。。。。。