设计模式三: 代理模式(Proxy) -- JDK的实现方式
简介
代理模式属于行为型模式的一种, 控制对其他对象的访问, 起到中介作用.
代理模式核心角色: 真实角色,代理角色;
按实现方式不同分为静态代理和动态代理两种;
意图
控制对其它对象的访问。
类图
实现
JDK自带了Proxy的实现, 下面我们先使用JDK的API来演示代理如何使用, 随后再探究Proxy的实现原理,并自己来实现Proxy.
JDK代理类的使用: (InvocationHandler
,Proxy
)
使用JDK实现的代理代码如下, 先定义业务接口`Car`,然后实现该接口`QQCar`,实现类即为真实角色. 继续定义代理类`Agent`,代理类需要实现接口`InvocationHandler`的`invoke()`方法, 最后是调用; 注意代理类使用了`Proxy.newProxyInstance()`方法动态生成代理对象, 在稍后手写代码时我们将参考本方法定义我们自己的实现方式.
/** * 汽车接口,定义业务规范 */ public interface Car { void sale(); }
/** * 接口Car的具体实现,是代理模式中的真实角色 */ public class QQCar implements Car { public void sale() { System.out.println("卖了一辆qq"); } }
/** * 经纪公司,或者经销商 */ public class Agent implements InvocationHandler { private Car car ; /** * 返回代理对象,接收被代理对象 * @param car * @return * @throws Exception */ public Object getInstance(Car car) throws Exception { this.car=car; Class clazz = car.getClass(); // 看下代理前后的具体类型 System.out.println("代理前对象的类型"+car.getClass().getName()); Object obj = Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); // 看下代理前后的具体类型 System.out.println("代理后对象类型变为"+obj.getClass().getName()); return obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Agent find some costumers"); //this.car.sale(); method.invoke(this.car, args); System.out.println("Agent saled the car"); return null; } }
try { Car car = (Car) new Agent().getInstance(new QQCar()); car.sale(); }catch (Exception e){ e.printStackTrace(); }
总结JDK原理如下:
- 获取真实角色对象的引用并获取其接口
- Proxy生成一个代理类,并实现接口的方法
- 获取被代理对象的引用
- 动态生成代理类的class字节码
- 编译加载
手写实现Proxy
要自己实现JDK的代理模式,我们首先要搞清楚JDK的Proxy是如何实现的, 通过调试及查看源码可以知道JDK生成了一个$Proxy0
的类型,我们也将该类型名称打印到了控制台. 如果能动态生成,编译并将这个类加载到内存, 我们就可以自己实现Proxy了.
- 首先拿到
$Proxy0
的代码,供我们后面生成代理类的源码时参考
//生产接口Car对应的代理类class文件并保存到文件 byte[] data = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Car.class}); FileOutputStream outputStream = new FileOutputStream("E:\\design-patterns\\src\\main\\java\\com\\xlx\\pattern\\proxy\\jdk\\$Proxy0.class"); outputStream.write(data); outputStream.close();
生成的$Proxy0.class文件反编译后的代码如下, 可以看到其实现了Car接口.
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import com.xlx.pattern.proxy.jdk.Car; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Car { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void sale() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.xlx.pattern.proxy.jdk.Car").getMethod("sale"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
- 参考JDK的实现,我们分别定义
MyClassLoader
,MyInvocationHandler
,MyProxy
,分别对应ClassLoader
,InvocationHandler
,Proxy
;
其中接口MyInvocationHandler
代码如下:
/** * 代理类需要实现该接口 */ public interface MyInvocationHandler { Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
- 定义代理类
MyAgent
对应我们使用JDK时定义的Agent
, 但getInstance()
方法实现全部改为2中自定义的类,实现2中定义的接口
/** * 代理类 */ public class MyAgent implements MyInvocationHandler { private Car car; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Agent find some costumers"); //this.car.sale(); method.invoke(this.car,args); System.out.println("Agent saled the car"); return null; } public Object getInstance(Car car) throws Exception { this.car=car; Class clazz = car.getClass(); Object obj = MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this); return obj; } }
-
实现
MyProxy
中生成代理对象的方法newProxyInstance()
具体又分为以下几步:1. 定义动态代理类的源码 2. 保存源码文件到磁盘 3. 编译源码文件为.class文件 4. 加载.class字节码到内存 (具体实现见5.实现MyClassLoader)) 5. 返回代理对象
/** * 生成代理对象的代码, Proxy的具体原理在这里体现 */ public class MyProxy { private static final String ln = "\r\n"; public static Object newProxyInstance(MyClassLoader loader, Class<?>[] interfaces, MyInvocationHandler h) { File f = null; try { // 第一步: 生成源代码 String src = generateSrc(interfaces[0]); // 第二步: 保存生成的源码文件 String filePath = MyProxy.class.getResource("").getPath(); f = new File(filePath + "/$Proxy0.java"); FileWriter writer = new FileWriter(f); writer.write(src); writer.flush(); writer.close(); // 第三步: 编译生成.class文件 JavaCompiler compliler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compliler.getStandardFileManager(null, null, null); Iterable iterable = manager.getJavaFileObjects(f); JavaCompiler.CompilationTask task = compliler.getTask(null, manager, null, null, null, iterable); ((JavaCompiler.CompilationTask) task).call(); manager.close(); // 第四步: 加载class字节码到内存(MyClassLoader类实现) Class proxyClass = loader.findClass("$Proxy0"); // 第五步: 返回代理对象 Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class); return constructor.newInstance(h); } catch (Exception e) { e.printStackTrace(); } finally { if (null != f) { f.delete(); } } return null; } /** * 生成源码的方法 * * @param interfaces 为了演示,按一个接口处理 * @return */ private static String generateSrc(Class<?> interfaces) { StringBuffer src = new StringBuffer(); src.append("package com.xlx.pattern.proxy.my;" + ln); src.append("import java.lang.reflect.Method;" + ln); src.append("public class $Proxy0 extends MyProxy implements " + interfaces.getName() + "{" + ln); src.append("MyInvocationHandler h;" + ln); src.append("public $Proxy0(MyInvocationHandler h){" + ln); src.append("this.h=h;" + ln); src.append("}" + ln); // 循环定义方法,与被代理类的方法同名 for (Method m : interfaces.getMethods()) { src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln); src.append("try{" + ln); src.append("Method m =" + interfaces.getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln); src.append("this.h.invoke(this,m,null);" + ln); src.append("}catch(Throwable e){e.printStackTrace();}" + ln); src.append("}" + ln); } src.append("}" + ln); return src.toString(); } }
- 实现
MyClassLoader
的findClass()
方法,最终由父类ClassLoader.defineClass()
方法加载,完成最后拼图
/** * 代码生成,编译,重新加载到内存 * 类加载器, 使用ClassLoader */ public class MyClassLoader extends ClassLoader{ File basePath ; public MyClassLoader(){ String basePath = MyClassLoader.class.getResource("").getPath(); this.basePath = new File(basePath) ; } @Override public Class<?> findClass(String name) throws ClassNotFoundException{ String className = MyClassLoader.class.getPackage().getName()+"."+name; if (null!=basePath){ File classFile = new File(basePath,name.replaceAll("\\.","/")+".class"); if (classFile.exists()){ FileInputStream in = null; ByteArrayOutputStream out= null; try { in = new FileInputStream(classFile); out = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len=in.read(buffer))!=-1){ out.write(buffer,0,len); } return defineClass(className,out.toByteArray(),0,out.size()); }catch (Exception e){ e.printStackTrace(); }finally { classFile.delete(); if (null!=in){ try{ in.close(); }catch (Exception e){ e.printStackTrace(); } } if (null!=out){ try{ out.close(); }catch (Exception e){ e.printStackTrace(); } } } } } return null; } }
总结
上面我仿照JDK自带API用自己的代码实现了Proxy, 这样写了一次之后对Proxy的实现原理加深了很多.Proxy作为一种重要的模式已经大量用在了目前流行的很多框架上, 理解了原理就更有信心去学习框架以及框架的实现思想了.
优点: 1. 职责清晰,真实角色专注实现业务逻辑,代理角色去完成具体的事务,代码结构简洁清晰;2. 可扩展
补充
上面研究了JDK动态代理的实现, 首先定义了接口,然后用一个类实现这个接口,这个实现类就是要代理的具体对象;
cglib库也实现了Proxy模式,与JDK不同的是, cglib不需要定义接口, 而是通过生成被代理类的子类来实现代理模式.这使得代理模式的使用更加简单. 一般类型均可以作为被代理类型.
大致的实现如下(原理跟jdk实现差不多,只不过cglib使用的是类继承实现):
/** * 演示 cglib 代理方式 */ public class CGAgent implements MethodInterceptor { public Object getInstance(Class clazz) throws Exception{ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("代理开始了...."); methodProxy.invokeSuper(o,objects); System.out.println("代理结束了...."); return null; } }

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
8月分享与总结
前言:取得成功的要自律!可能有一腔热血,努力很长一阵子,但过一阵子之后,就不坚持了,所以要自律去约束自己时刻坚持着! 一、收获 8月份的收获还是很大的,主要有以下几个方面: 学会了使用github 注册账号很长时间了,但不怎么会用,这个月,基本功能都会用了,并在上面提交了leetcode的代码;Github是个巨大的宝库,里面有很多资源,C/C++很多开源项目都由源码... 看《C++ Primer Plus》基本了解了C++ 从4月份开始要学C++,一直磕磕绊绊,一直有项目要做,这个月基本没有项目,都是处理之前的bug或者现场的问题,所以有大块时间来学习,对C++的基本语法、定义、STL基本掌握,有句话觉得很对,C++不可能精通,C++的东西太多太杂,并且敲得代码太少、没做过项目,还要继续学习深造! 学习了数据结构和算法 通过看《剑指Offer》和《LeetCode.pdf》,学习并练习了数据结构和算法,觉得这两本书都不错,对于想练习这两方面的人,是一个很好的选择;PS:想要这两本书可以留言 知道了很有用的学习网站 学习C++直接看书学的,虽然写的很细,但很笼统,思路不是很清晰...
- 下一篇
Java源码阅读之红黑树在HashMap中的应用 - JDK1.8
阅读优秀的源码是提升编程技巧的重要手段之一。 如有不对的地方,欢迎指正~ 转载请注明出处https://blog.lzoro.com。 前言 基于JDK1.8。 JDK1.8之前,HashMap并没有采用红黑树,所以哈希桶上的链表过长时,就会有效率问题。 JDK1.8,则在HashMap引入了红黑树,当链表长度超过指定阈值(默认为8),则进行树化并提供相关操作(增删查等),提高了操作效率。 之前阅读了HashMap的源码,但是由于篇幅关系,略过了链表树化后红黑树的相关操作,本着打破砂锅问到底的精神,来看下红黑树在HashMap中的应用。 打破沙锅问到底,是一个成语,拼音为:dǎ pò shā guō wèn dào dǐ,其 比喻追究事情的根底,其出处自 宋·黄庭坚《拙轩颂》。 红黑树 什么是红黑树? 是这样的吗(开个玩笑,不存在的)。 树? 是这样的,这才对吧,有红有黑。 红黑树 科普时间,it KePu's tims. 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。 它是在1972年由Rudolf Ba...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS8安装Docker,最新的服务器搭配容器使用
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Red5直播服务器,属于Java语言的直播服务器
- CentOS6,CentOS7官方镜像安装Oracle11G
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- SpringBoot2整合Redis,开启缓存,提高访问速度