不仅知其然,还得知其所以然。既然 JDK Proxy功能如此强大,那么它是如何实现的呢?我们现在来探究一下原理,并模仿 JDK Proxy自己动手写一个属于自己的动态代理。我们都知道 JDK Proxy采用字节重组,重新生的对象来替代原始的对象以达到动态代理的目的。JDK Proxy生成对象的步骤如下:

1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。

2、JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口。

3、动态生成 Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)。

4、编译新生成的 Java代码.class。

5、再重新加载到 JVM中运行。

以上这个过程就叫字节码重组。JDK中有一个规范,在 ClassPath下只要是$开头的 class文件一般都是自动生成的。那么我们有没有办法看到代替后的对象的真容呢?做一个这样测试,我们从内存中的对象字节码通过文件流输出到一个新的 class文件,然后,利用反编译工具查看 class的源代码。来看测试代码:

创建 GPProxy类:

/*** 用来生成源代码的工具类* Created by Tom.*/
public class GPProxy {public static final String ln = "\r\n";public static Object newProxyInstance(GPClassLoader classLoader, Class<?> [] interfaces, GPInvocationHandler h){try {//1、动态生成源代码.java文件String src = generateSrc(interfaces);//           System.out.println(src);//2、Java文件输出磁盘String filePath = GPProxy.class.getResource("").getPath();
//           System.out.println(filePath);File f = new File(filePath + "$Proxy0.java");FileWriter fw = new FileWriter(f);fw.write(src);fw.flush();fw.close();//3、把生成的.java文件编译成.class文件JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);Iterable iterable = manage.getJavaFileObjects(f);JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);task.call();manage.close();//4、编译生成的.class文件加载到JVM中来Class proxyClass =  classLoader.findClass("$Proxy0");Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);f.delete();//5、返回字节码重组以后的新的代理对象return c.newInstance(h);}catch (Exception e){e.printStackTrace();}return null;}private static String generateSrc(Class<?>[] interfaces){StringBuffer sb = new StringBuffer();sb.append("package com.leon.vip.pattern.proxy.dynamicproxy.gpproxy;" + ln);sb.append("import com.leon.vip.pattern.proxy.Person;" + ln);sb.append("import java.lang.reflect.*;" + ln);sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);sb.append("GPInvocationHandler h;" + ln);sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);sb.append("this.h = h;");sb.append("}" + ln);for (Method m : interfaces[0].getMethods()){Class<?>[] params = m.getParameterTypes();StringBuffer paramNames = new StringBuffer();StringBuffer paramValues = new StringBuffer();StringBuffer paramClasses = new StringBuffer();for (int i = 0; i < params.length; i++) {Class clazz = params[i];String type = clazz.getName();String paramName = toLowerFirstCase(clazz.getSimpleName());paramNames.append(type + " " +  paramName);paramValues.append(paramName);paramClasses.append(clazz.getName() + ".class");if(i > 0 && i < params.length-1){paramNames.append(",");paramClasses.append(",");paramValues.append(",");}}sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames.toString() + ") {" + ln);sb.append("try{" + ln);sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})",m.getReturnType()) + ";" + ln);sb.append("}catch(Error _ex) { }");sb.append("catch(Throwable e){" + ln);sb.append("throw new UndeclaredThrowableException(e);" + ln);sb.append("}");sb.append(getReturnEmptyCode(m.getReturnType()));sb.append("}");}sb.append("}" + ln);return sb.toString();}private static Map<Class,Class> mappings = new HashMap<Class, Class>();static {mappings.put(int.class,Integer.class);}private static String getReturnEmptyCode(Class<?> returnClass){if(mappings.containsKey(returnClass)){return "return 0;";}else if(returnClass == void.class){return "";}else {return "return null;";}}private static String getCaseCode(String code,Class<?> returnClass){if(mappings.containsKey(returnClass)){return "((" + mappings.get(returnClass).getName() +  ")" + code + ")." + returnClass.getSimpleName() + "Value()";}return code;}private static boolean hasReturnValue(Class<?> clazz){return clazz != void.class;}private static String toLowerFirstCase(String src){char [] chars = src.toCharArray();chars[0] += 32;return String.valueOf(chars);}}

创建GPClassLoader 类:

public class GPClassLoader extends ClassLoader {private File classPathFile;public GPClassLoader(){String classPath = GPClassLoader.class.getResource("").getPath();this.classPathFile = new File(classPath);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {String className = GPClassLoader.class.getPackage().getName() + "." + name;if(classPathFile  != null){File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");if(classFile.exists()){FileInputStream in = null;ByteArrayOutputStream out = null;try{in = new FileInputStream(classFile);out = new ByteArrayOutputStream();byte [] buff = new byte[1024];int len;while ((len = in.read(buff)) != -1){out.write(buff,0,len);}return defineClass(className,out.toByteArray(),0,out.size());}catch (Exception e){e.printStackTrace();}}}return null;}
}

创建GPMeipo 类:

public class GPMeipo implements GPInvocationHandler {private Object target;public Object getInstance(Object target) throws Exception{this.target = target;Class<?> clazz = target.getClass();return GPProxy.newProxyInstance(new GPClassLoader(),clazz.getInterfaces(),this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();Object obj = method.invoke(this.target,args);after();return obj;}private void before(){System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");System.out.println("开始物色");}private void after(){System.out.println("OK的话,准备办事");}
}

到此,手写JDK 动态代理就完成了。小伙伴们,是不是有多了一个面试用的杀手锏呢?

高仿真 JDK Proxy手写实现相关推荐

  1. 鬼节聊聊手写proxy思路

    缘起 今天是鬼节,小编心血来潮,觉得应该更新文章了!于是应广大童鞋们要求,今天来聊聊proxy手写思路.看了这篇文章后,你会思如泉涌,会迫不及待的想试一试!本篇文章只是思路和少量代码,具体实践,有兴趣 ...

  2. 使用caffemodel模型(由mnist训练)测试单张手写数字样本

    caffe中训练和测试mnist数据集都是批处理,可以反馈识别率,但是看不到单张样本的识别效果,这里使用windows自带的画图工具手写制作0~9的测试数字,然后使用caffemodel模型识别. 1 ...

  3. 基于SVM技术的手写数字识别

    老师常说,在人工智能未发展起来之前,SVM技术是一统江湖的,SVM常常听到,但究竟是什么呢?最近研究了一下基于SVM技术的手写数字识别.你没有看错,又是手写数字识别,就是喜欢这个手写数字识别,没办法( ...

  4. ipad用别的品牌手写笔可以吗?第三方好用的手写笔推荐

    要不要买一个苹果的原装版本 Ipad手写笔?苹果的原版售价如此之高,到底值不值得?当然,如果你有更多的钱,你可以使用苹果的原装版本,但是,如果你不愿意花很多钱在 ipad上,那么你可以使用平替电容笔. ...

  5. 【手写系列】纯手写实现JDK动态代理

    前言 在Java领域,动态代理应用非常广泛,特别是流行的Spring/MyBatis等框架.JDK本身是有实现动态代理技术的,不过要求被代理的类必须实现接口,不过cglib对这一不足进行了有效补充.本 ...

  6. 30个类仿真手写spring框架V2.0版本

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  7. 两万字吐血总结,代理模式及手写实现动态代理(aop原理,基于jdk动态代理)

    代理模式及手写实现动态代理 一.代理模式 1. 定义 2. 示例 (1)静态代理 (2)动态代理 3. 通用类图 4. 代理模式的优点 二.jdk动态代理实现原理 1. jdk动态代理源码分析(通过该 ...

  8. 【干货】JDK动态代理的实现原理以及如何手写一个JDK动态代理

    动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 在解 ...

  9. 【手写系列】纯手写实现一个高可用的RPC

    前言 在实际后台服务开发中,比如订单服务(开发者A负责)需要调用商品服务(开发者B负责),那么开发者B会和A约定调用API,以接口的形式提供给A.通常都是B把API上传到Maven私服,然后B开始写A ...

最新文章

  1. Java学习总结:36(日期处理类)
  2. Android第三十八期 - 评价标签FlowLayout
  3. RediSQL 0.8.0 发布,将查询结果写入流中
  4. java值传递string_关于java:按值传递(StringBuilder与String)
  5. 阶乘和(信息学奥赛一本通-T1173)
  6. 用.NET提供的Mail来发邮件
  7. 滴滴的焦虑,从未根治!
  8. 计算机程序可以通过删除卸载嘛,怎么彻底卸载电脑软件程序多种方法
  9. php全局标签,dedecms的全局标签是什么意思
  10. 经典贪吃蛇大战逆向 去广告+游戏内购
  11. java开发手机app_java 怎么开发手机app接口?
  12. Flutter时间转换工具类
  13. Mysql 与 python 的使用
  14. python中写sql语句添加for循环和变量。(一种SQL引入循环的思想实现)
  15. Qt udp组播Qt udp组播
  16. Android 源码 图形系统之请求布局
  17. ue4怎么用虚幻商城场景_如何利用虚幻商城创造被动收入【经验分享】
  18. sklearn代码21 2-2020天猫双十一销量
  19. [答疑]会议申请序列图
  20. shortcut icon地址栏显示个性图标

热门文章

  1. spring cloud 微服务相关信息
  2. 【[SDOI2014]数数】
  3. [国家集训队]部落战争
  4. 为什么硬盘明明还有空间,linux却说硬盘空间不足?inode;mkdir: 无法创建目录shen1: 设备上没有空间...
  5. python-global全局变量
  6. PyTorch学习问题记录
  7. 【SignalR学习系列】7. SignalR Hubs Api 详解(JavaScript 客户端)
  8. js-ES6学习笔记-module(2)
  9. Nginx多站点虚拟主机实现单独启动停止php-fpm、单独控制权限设置
  10. 2440,6410,210存储器接口比较