若是對我這段代碼感興趣,直接拷貝測試debug,要否則你不知道我寫的是什麼鬼,若是有什麼問題,能夠告訴我。java
1、實現動態代理,首先得考慮有應該定義哪些類,根據JDK的動態代理思想,那麼它就應該有一個生成代理的類web
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; } }
2、有了生成代理的類,那麼就還應該有個處理邏輯的接口數組
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 ; }
3、若是方法參數中存在基本類型參數,要自動打包成Object[] args,寫個基本類型對應包裝類助手ide
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; } } }
4、基本類型對應包裝類的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; } } }
5、方法描述符生成助手測試
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; } }
6、用於計算局部變量表的slot長度this
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; } }
7、根據不一樣類型使用不一樣字節碼指令助手類spa
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; } } }
8、根據不一樣的返回類型使用不一樣字節碼指令的助手類debug
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; } } }
9、自定義類加載器代理
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); } }
10、測試類
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[]{}); } }
11、總結
使用ASM實現動態代理,須要先學懂JVM虛擬機的字節碼指令。在本身寫字節碼指令的時候,若是你忘記了某些代碼的指令的實現,別忘記使用JDK的javap -c -v -private **.class。經過javap咱們能夠解決好多咱們曾經感到疑惑的地方,好比爲何匿名內部類使用局部變量時這個局部變量不能變?爲何在字節碼層面上不能直接將基本類型複製給Object類型?synchronized字節碼中如何表述的。。。。。。