代理模式简介

代理模式(Proxy Pattern)是面向对象中一种非常常见的设计模式。其实,不单是在软件开发领域,在我们的日常生活中对于代理也时常可见。比如:房东要将自家的房租出售,于是到房地产中介公司找一个代理,由他来帮自己完成销售房屋,签订合同等等事宜。

在此,就以该生活场景为蓝本介绍Java的代理模式。一般而言,代理技术可分为:静态代理和动态代理;我们先来看静态代理。


静态代理示例

房东通过一纸协议将自己的房子挂靠在房屋中介公司,委托中介出售其房屋。嗯哼,我们来一起瞅瞅这个房东和中介公司共同达成的协议:

/*** 本文作者:谷哥的小弟* 博客地址:http://blog.csdn.net/lfdfhl*/
package cn.com;public interface TradeProtocol {public void sellHouse(int money);
}

嗯哼,这个协议很简单,房东委托中介公司售卖自己的房子。

既然是房东和房屋中介共同达成的协议,那么房东和中介都需要遵守该协议。

先来看房东:

/*** 本文作者:谷哥的小弟* 博客地址:http://blog.csdn.net/lfdfhl*/
package cn.com;public class Owner implements TradeProtocol{@Overridepublic void sellHouse(int money) {System.out.println("我是房主,我的房子卖了"+money+"块钱");}}

再来看中介:

/*** 本文作者:谷哥的小弟* 博客地址:http://blog.csdn.net/lfdfhl*/
package cn.com;public class HouseAgent implements TradeProtocol {private TradeProtocol mHouseProtocol;public HouseAgent(TradeProtocol houseProtocol){mHouseProtocol=houseProtocol;}@Overridepublic void sellHouse(int money) {System.out.println("我是中介,业主的房子卖了"+money+"块钱");mHouseProtocol.sellHouse(money);}}

请注意HouseAgent的实现

  • 在HouseAgent的构造方法中传入TradeProtocol的对象,比如Owner
  • 在HouseAgent的sellHouse()中调用TradeProtocol的对象(比如Owner)的sellHouse()
  • 所以,HouseAgent售卖(sellHouse())房子,实际上是房东自己售卖了房屋!

测试如下:

/*** 本文作者:谷哥的小弟* 博客地址:http://blog.csdn.net/lfdfhl*/
package cn.com;public class Test {public static void main(String[] args) {Owner owner=new Owner();HouseAgent houseAgent=new HouseAgent(owner);houseAgent.sellHouse(10000*100);}}

测试结果:

我是中介,业主的房子卖了1000000块钱
我是房主, 我的房子卖了 1000000块钱

在该静态代理中,房东将房屋的售卖交给了中介代理,中介将房屋出售后又把钱一分不少地交给了房东。咦,这咋不对呢?现实中有这样大公无私,视金钱如粪土的房屋中介么?没有!绝对没有!万一,你发现有类似的情况,请举起自己的双手抽自己两耳光:醒醒,别睡了,别做白日梦了!

那么,中介是怎么赚钱的呢?我们继续往下看


动态代理示例

与之前一样,房东和房屋中介公司之间存有一纸协议:

/*** 本文作者:谷哥的小弟* 博客地址:http://blog.csdn.net/lfdfhl*/
package cn.com;public interface TradeProtocol {public void sellHouse(int money);
}

同样地,房东遵守该协议:

/*** 本文作者:谷哥的小弟* 博客地址:http://blog.csdn.net/lfdfhl*/
package cn.com;public class Owner implements TradeProtocol{@Overridepublic void sellHouse(int money) {System.out.println("我是房主,中介告诉我:房子卖了"+money+"块钱");}}

现在,房东又去房屋中介公司挂靠自己的房子,到了那一看:原来熟悉的那个中介出去办事了,刚好不在。正准备走呢,中介公司的经理挺着啤酒肚走过来,笑嘻嘻地说:请问您是来办理业务的吗?来,请坐,我给你们引荐以为我们这里服务最好的一个房屋中介人员办理您的相关事宜!

嗯哼,我们来看看这个这位被称为”服务最好的中介”是怎么卖掉房东的房子的:

package cn.com;import java.lang.reflect.Proxy;/*** 原创作者:谷哥的小弟* 博客地址:http://blog.csdn.net/lfdfhl*/
public class TestProxy02 {public static void main(String[] args) {TradeProtocol owner=new Owner();ClassLoader classLoader=owner.getClass().getClassLoader();Class<?>[] interfaces = owner.getClass().getInterfaces();//创建InvocationHandlerHouseAgentInvocationHandler invocationHandler=new HouseAgentInvocationHandler(owner);//生成动态代理TradeProtocol tradeProtocol=(TradeProtocol) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);tradeProtocol.sellHouse(1000000);}}

HouseAgentInvocationHandler 的实现如下:

package cn.com;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** 原创作者:谷哥的小弟 * 博客地址:http://blog.csdn.net/lfdfhl*/
public class HouseAgentInvocationHandler implements InvocationHandler {private Object object;public HouseAgentInvocationHandler(Object object) {this.object=object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Integer money=(Integer) args[0];System.out.println("我是中介,业主的房子我实际卖了"+money+"块钱");Object result=method.invoke(object, new Object[]{money/2});System.out.println("我是中介,这次交易我赚了不少钱!!!");return result;}
}

好了,所涉及到的代码就这么多。或许有一点看不懂,这也无妨,我们先看看运行的结果再作详细的解释。控制台输出信息如下:

我是中介,业主的房子我实际卖了1000000块钱
我是房主,我的房子卖了500000块钱
我是中介,这次交易我赚了不少钱!!!

哇哈,狡猾的中介把房子卖了100W却告诉房东只买了50W,自己狠狠地赚了一笔!这是怎么回事呢?不急,我们通过这两个示例来回顾一下卖房子的经过。

  • 第一个示例中,房东将房屋委托给一个自己熟知的中介人员售卖房产;中介卖了多少钱就给房东多少钱
  • 第二个示例中,房屋中介公司的经理临场(动态)地指定了一个中介人员为房东办理售卖房屋的相关事宜
  • 第二个示例中,中介对售卖的过程做了手脚。由此可见:动态代理可方便地对被代理类的方法进行某些处理。比如,在方法实际调用前做一些过滤或者拦截操作或者修改,在方法调用后做一些善后处理等。

好了,生活中的例子看完了,我们再回到工作中的代码,结合示例分析Java动态代理技术。


动态代理技术分析

在Java的动态代理机制中,有两个非常重要的不可或缺的东西:

  • InvocationHandler接口

  • Proxy类

先来瞅瞅InvocationHandler接口

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

这段官方文档的主要含义是:
每个动态代理类都必须要实现InvocationHandler接口,并且每个动态代理类的实例都关联到了一个handler。当我们通过代理调用被代理对象的方法时,该方法的调用会被转发至InvocationHandler接口的 invoke( )中进行处理,方法如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{}

该方法有三个输入参数,它们分别代表什么呢?

  • proxy:最终生成的代理
  • method:被代理对象正在执行的方法
  • args:被代理对象正在执行的方法的输入参数

这么说,或许有点摸不着头脑。没事,我们在HouseAgentInvocationHandler类的invoke( )方法开头加几句打印语句:

@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("proxy.getClass()="+proxy.getClass());System.out.println("method.getName()="+method.getName());for(Object object:args) {System.out.println("object.getClass()="+object.getClass());}..............}

输出结果:

proxy.getClass()=class com.sun.proxy.$Proxy0
method.getName()=sellHouse
object.getClass()=class java.lang.Integer

嗯哼,看了InvocationHandler接口,再来瞅瞅Proxy类。

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

这段官方文档的主要含义是:
Proxy用于动态创建一个代理类及其实例。该类中我们常用 newProxyInstance( )方法:

 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)  throws IllegalArgumentException {}

该方法有三个输入参数,它们分别代表什么呢?

  • loader:加载动态代理类的ClassLoader,该输入参数通常为被代理类的类加载器
  • interfaces:动态代理类需要实现的接口,该输入参数通常为被代理类使用的接口
  • handler:动态代理对象在调用方法时与之关联的InvocationHandler

从这里我们可以看出: 动态代理与静态代理的原理是一样的,但是在动态代理中它没有具体的代理类而是通过Proxy在JVM运行时利用反射动态地生成了一个代理。在静态代理中,需要代理(中介)与被代理对象(房东)遵守同一份协议;其实在动态代理中也是非常类似的,只不过它换了一种方式罢了,这点从newProxyInstance( )方法的参数interfaces就能看出端倪:这些接口(interface)不就是静态代理中的协议么?也可以这么理解:动态代理对于协议的遵守发生在newProxyInstance( )时;并且动态代理可以对原来协议中的方法”做手脚”!

至此,我们再回过头来梳理一下动态代理示例及其相关技术:

  • 房东遵守房屋售卖协议
  • 动态地生成代理,在生成代理的过程中代理也遵守了房屋售卖协议
  • 代理在执行协议中的方法时(比如sellHouse( )),将该方法转发至与该代理密切相关的InvocationHandler中
  • 在InvocationHandler中可对协议中的方法(比如sellHouse( ))进行额外的附加的处理
  • 在InvocationHandler中利用反射调用协议中的方法(比如sellHouse( ))。其实,从这里也可以看出来:动态代理属于Java反射技术范畴;或许细心的人从导包语句中也有所发现:java.lang.reflect.InvocationHandler;嗯哼,InvocationHandler就在reflect包下

小感悟

很多刚开始做开发的童鞋喜欢拿着一本厚厚的设计模式在角落里默默地啃。学习的劲头很足,态度也很端正,配得上10086个赞。在此,我也想提醒一下小伙伴们:学习态度和努力程度固然非常重要,但是我们也要注意学习方法。抛开实际应用和业务逻辑单纯地看设计模式是很难理解其精髓的。我们不妨将设计模式和自己的实际工作结合起来学习,比如做Android开发的小伙伴可结合Android源码或者非常流行的第三方库来深入地研究设计模式,在此推荐一篇《Retrofit分析-漂亮的解耦套路》供大家学习参考


参考资料

  • Head First设计模式

  • Android架构设计方法、技巧与实践

  • Retrofit架构分享

  • Java应用架构设计:模块化模式与OSGi

从狡猾的房产中介来看动态代理相关推荐

  1. JAVA基础加强篇12——单元测试、反射、注解、动态代理

    单元测试.反射.注解.动态代理 课程安排 单元测试 单元测试概述 单元测试 单元测试就是针对最小的功能单元编写测试代码,Java 程序最小的功能单元是方法,因此,单元测试就是针对 Java 方法的测试 ...

  2. Java进阶(十四)单元测试、反射、注释、动态代理

    十四.单元测试.反射.注释.动态代理 需要学会什么? 单元测试:开发好的系统中存在很多的方法,如何对这些方法的正确性进行测试. 反射:如何在程序运行时去得到Class对象,然后去获取Class中的每个 ...

  3. Retrofit2 源码解析之动态代理

    基于 Retrofit 2.3.0 & Android 8.1 分析 Java 动态代理在 Android 上的实现 未经允许不得转载 Retrofit 使用示例 public interfa ...

  4. 动态代理Java实现

    思考:在IBuyWatermelon添加一个方法selectWatermelon() 静态代理中需要在RealSubject中实现该方法,而且Proxy也要实现该方法调用RealSubject中的实现 ...

  5. java动态代理原理剖析

    首先来谈为什么需要代理. 比如我有一个接口如下 public interface Moveable {void move();} 它有一个move方法.现在我有这样一个需求,当我要通过子类实现该接口调 ...

  6. java 委托_动态代理:Java开发必学

    一句话概括:java 动态代理通过反射机制,可在不修改原代码的情况下添加新的功能,应用于多种场景,简单.实用.灵活,是 java 开发必学知识,本文将对动态代理使用进行详细介绍. 1. 引言 最近开发 ...

  7. 动态代理 ---- 框架基础技术

    JSK动态代理 内容导航 代理模式 proxy pattern proxy proxy pattern proxy role 静态代理 static proxy 静态代理的缺点 dynamic pro ...

  8. 我佛了,居然有人用古代皇帝与臣子的关系讲清了JDK动态代理

    代理模式 Proxy Pattern,代理模式,一个类可以代表另一个类的功能. 在生活中代理模式也是比比皆是,房产中介.委托律师.海外代购.同城闪送,他们都是代理了实际用户的一些行为,并且极大地提供了 ...

  9. 房产中介楼盘信息管理系统计算机毕业设计源码84855

    摘要 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确. ...

最新文章

  1. Dataframe花样切片~(Python)
  2. 欲从事服务端工作不懂seata?一篇小demo零基础带你快速掌握分布式事务框架seata的基本使用!
  3. pip通过阿里源安装
  4. PlayFrameWork实现文件上传,完整流程
  5. leetcode: 451. Sort Characters By Frequency
  6. 【概率论】对弈输光模型,ruin model
  7. java面试要点---oracle,mysql,DB2数据库的分页
  8. 数据挖掘:模型选择——集成算法与树模型
  9. 打造机器人:为遥控小车加一个树莓派
  10. 肿瘤免疫浸润资源汇总
  11. 一个开源的会议管理系统,适合初学者练手(老司机请忽略)
  12. 用FAI制作debian自动安装盘
  13. poj 1321 排兵布阵问题 dfs算法
  14. 阿里 卫哲谈阿里人力招聘价值观
  15. 计算机电子电路原理图,简易电子琴设计电路图大全(八款模拟电路设计原理图详解) - 消费类电子电路图...
  16. 20050714日记
  17. 滴滴云上使用 eCryptfs 加密数据
  18. 中国不投美国国债还能投什么?
  19. matlab四元一次非线性方程求解,[转载]MATLAB求解非线性方程(转)
  20. Java压缩字符串的方法收集

热门文章

  1. IIS下发布mvc遇到的HTTP错误 403.14-Forbidden解决办法
  2. 分享 25 道常见的 TypeScript 面试题及答案
  3. 全网搜歌音乐播放器Listen1 Mac中文版附chrome版
  4. 第五代 智能计算机和生物计算机
  5. 三维建模入门,从软件设置开始
  6. 【Python】Python安装升级mkl包
  7. chrome点击书签栏书签_如何将多个书签组织在一起
  8. Dos中echo off ,echo on 和 @echo on的使用
  9. 4月17日 (PS:由于时间问题,现在才发,望老师见谅)疯狂猜成语-----第三次站立会议 参会人员:杨霏,袁雪,胡潇丹,郭林林,尹亚男,赵静娜...
  10. php+条形码在线怎么生成,php实现生成code128条形码的方法详解