如果对我这段代码感兴趣,直接拷贝测试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);//生成ClassNodeClassNode 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,nullfields.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,用于设置LogicMethodNode 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类型占用两个slotint len = LocalLen.len(clazz);//获得这个方法的参数个数,不包括thisint 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加2if(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 ++;//2oldClazz = 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;}@Overrideprotected 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;}@Overridepublic 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字节码中如何表述的。。。。。。

转载于:https://www.cnblogs.com/honger/p/6815322.html

使用ASM实现动态代理相关推荐

  1. 动态代理竟然如此简单!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 这篇文章我们来聊一下 Java 中的动态代理. 动态代理在 ...

  2. Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出 ...

  3. 动态代理机制详解(JDK 和CGLIB,Javassist,ASM)

    2019独角兽企业重金招聘Python工程师标准>>> 在运行时期可以按照Java虚拟机规范对class文件的组织规则生成对应的二进制字节码.当前有很多开源框架可以完成这些功能,如A ...

  4. JVM插桩之四:Java动态代理机制的对比(JDK和CGLIB,Javassist,ASM)

    一.class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件, ...

  5. Java-AOP(Hook)实现机制(JDK/cglib动态代理/ASM/Javassist/AspectJ)

    原文: http://www.iteye.com/topic/1116696 1 AOP各种的实现 AOP就是面向切面编程,我们可以从几个层面来实现AOP. AOP实现时有三种方式:生成子类字节码.生 ...

  6. 学习动态代理前先看看-ASM

    关键词:ASM    字节码 在<学习动态代理前先看看-class字节码>中提到,我们是直接可以面向.class文件进行编码的,但是编写一个字节码内容是十分复杂的,好在有框架提供了功能.比 ...

  7. java动态代理【一】

    java动态代理的定义:为其他目标类的方法增加切面的逻辑,即在执行目标类方法的时候,先去执行一段如校验检测的逻辑代码.java通俗一点就是生成一个继承目标类的子类,并在每个调用方法都添加一段逻辑. 应 ...

  8. 支撑Java框架的基础技术:泛型,反射,动态代理,cglib

    以Spring为例要想看明白他的源码需要彻底理解Java的一些基础技术泛型,反射同时对于一些高级技术例如动态代理,cglib和字节码技术也需要掌握,下面就按章节来一一说清楚这些技术的核心部分,最后手写 ...

  9. 支撑Spring的基础技术:泛型,反射,动态代理,cglib等

    1.静态代码块和非静态代码块以及构造函数 出自尚学堂视频:<JVM核心机制 类加载全过程 JVM内存分析 反射机制核心原理 常量池理解> public class Parent {stat ...

  10. JDK动态代理和Cglib的动态代理

    2019独角兽企业重金招聘Python工程师标准>>> 最简单的是静态代理方法,即代理模式,这里就不多啰嗦了.. 重点说一下JDK的动态代理和Cglib的动态代理吧 先说JDK的,需 ...

最新文章

  1. [Python爬虫] 之二十二:Selenium +phantomjs 利用 pyquery抓取界面网站数据
  2. 4月30日或为上半年“最难打车日”
  3. 全局程序集缓存gac中安装程序集_我就不信2W字把源码拆的这么碎,你还不明白mybatis缓存...
  4. 如何在IDA软件中找到自己需要的目标函数(关键函数)
  5. CMMI for Development读书笔记-目录
  6. Java内存泄露和内存溢出、JVM命令行工具、.JDK可视化工具、Java Class文件
  7. Enterprise Library 企业库 V4.1
  8. android ios logo原型,iOS关于logo和LaunchImage处理
  9. jedis操作set_Jedis对redis的五大类型操作代码详解
  10. shell中Connection closed by foreign host问题解决
  11. linux下nginx环境搭建
  12. java wifi 对讲机_freevoice(局域网对讲机)——Android4项目实战视频教程 - 移动编程 - 私塾在线 - 只做精品视频课程服务...
  13. 基于AVR-BootLoader,通过霜蝉远程串口可实现单片机的远程升级
  14. 在Docker里部署ReviewBoard4.0-RC
  15. 大漠插件常见问题汇总
  16. java jdbc 批处理_JDBC的批处理操作
  17. Efficient polynomial commitment schemes for multiple points and polynomials学习笔记
  18. 2011移动互联网企业评优
  19. 新站快速排名的优化技巧
  20. Atom配置markdown

热门文章

  1. 基本概念---part3
  2. Xfce4里添加登录后程序自动运行
  3. 微信小程序不行了?连接应用场景面临挑战
  4. Chrome Vimium 快捷键
  5. 地铁沉降观测数据分析之巧用VBA编程处理
  6. node.js框架 express
  7. 推荐15个在线多媒体(图片、音频、视频)编辑器,互联网营销
  8. 第九大陆服务器未找到文件,我的世界1.7.x第九大陆生存服务器
  9. vue基础-25-vue的基础面试知识
  10. 计算机网络与应用测试题,计算机网络技术与应用测试题.doc