设计模式 ---- 代理Proxy

代理其实本质很好理解,网络上那些花里胡哨的解释一律不用管,我们要抓住精髓:就是增强一个对象的功能。打个比方买火车票,12306的app就是一个代理,代理了火车站售票窗口的功能。小区当中的代售点也是代理,黄牛也是代理。他们替你买了,你就不需要去火车站售票窗口了,就相当于增强了售票窗口的功能。那么回到编程Java当中如何实现代理呢?更多Spring内容进入【Spring解读系列目录】。

Java实现代理有两种办法

静态代理和动态代理,本篇博客主要讲静态代理,并且由静态代理的缺点引出为什么要使用动态代理。首先要明白两个概念代理对象和目标对象。

  • 代理对象:增强后的对象(app,黄牛等等)。
  • 目标对象:被增强的对象(火车站窗口)。

Java中代理对象和目标对象不是绝对的,会根据情况发生变化。比如代理对象也可能是另一个代理的目标对象。这就好比买黄牛手里火车票的不一定是乘客,也有可能是二道贩子。那么黄牛是火车站售票窗口的代理,二道贩子就是黄牛的代理,这就是代理对象和目标对象的身份做了变换。

静态代理

静态代理也分为两种,一种叫做继承,一种叫做聚合。继承大家都懂,什么是聚合呢?名字高大上,其实就是实现实现接口。为了更好的展示我们还是写个小例子。比如,我们有一个业务类QueryDaoImpl,和一个执行类MainTest。直接运行,执行query()方法是没有问题的。做完主要功能以后,有可能需求增加了,比如我要给QueryDaoImpl加个日志。那该怎么做呢?正常来说,我们可以搞一个公共日志方法,然后在query()方法里调用。或者我们在调用的时候的前面加上,这样就满足了当前的业务需求。

public class QueryDaoImpl {public void query(){//Util.log();  //方案一:加个静态log方法?System.out.println("查询数据库内容");}
}
public class MainTest {public static void main(String[] args) {QueryDaoImpl queryDao=new QueryDaoImpl();//Util.log();  //方案二:或者加到这里?queryDao.query();}
}

方案一,我们设想这种情况,如果这里做的是一个公司的项目,加进来的是一个第三方jar包,手里根本就没有源码怎么加?而且就算有源码,加进去了,也违背了单一职责的编码原则。query()方法就是用来查询的,干嘛再加一个功能进去呢。

方案二,如果这个query()方法是一个通用方法,最后发现好几百个地方都用了,一个一个的加好几百次,那不是要疯了。今天这样写死了,万一有天不要加这个log了,又要返工好几百次,显然这样加很不像话。 那么问题就来了,如果不让动源码的话,怎么解决问题呢?在面向对象的思想下,应该能想到第一个解决方案:继承。

继承

假设我们现在没有源码,用继承怎么实现呢?一句话来说就是:代理对象继承目标对象,重写需要增强的方法。

那么我们从新建一个类QueryDaoLogImpl,用来实现日志功能,并且继承QueryDaoImpl重写里面query()方法。然后再修改我们的调用类,让其中实现QueryDaoLogImpl。

public class QueryDaoLogImpl extends QueryDaoImpl {@Overridepublic void query() {System.out.println("---extends log---");super.query();}
}
public class MainTest {public static void main(String[] args) {QueryDaoImpl queryDao=new QueryDaoLogImpl();queryDao.query();}
}
输出结果,完成了目标:
---extends log---
查询数据库内容

我们先不考虑耦合度这种问题,在这个例子中,QueryDaoImpl就是目标对象,而QueryDaoLogImpl就是代理对象,我们现在写的这个例子,就是完成了一个代理的模型。可以很清楚的看出,代理其实已经是一个类级别的改动了,代理出来以后,虽然我们使用的了执行了原来的逻辑,但是对象已经改变,不再是原来的对象了。这点一定要记好。

那我们再看代理对象和目标对象是相对的这句话。假如boss看这个改动不爽了,换成记录时间的,那怎么办呢?依葫芦画瓢,再整一个QueryDaoTimeImpl。

public class QueryDaoTimeImpl extends QueryDaoImpl {@Overridepublic void query() {System.out.println("---extends time---");super.query();}
}
MainTest:QueryDaoImpl queryDao=new QueryDaoTimeImpl();
输出结果,完成了目标:
---extends time---
查询数据库内容

又过了一天产品说业务改动了,这俩都得要。得,接着改QueryDaoTimeLogImpl。

public class QueryDaoTimeLogImpl extends QueryDaoLogImpl {@Overridepublic void query() {System.out.println("---extends time---");super.query();}
}
MainTest:QueryDaoImpl queryDao=new QueryDaoTimeLogImpl();
---extends time---
---extends log---
查询数据库内容

结果刚改完第二天,业务又说了,想要先打印log再打印time。那我们就又要多搞一个QueryDaoLogTimeImpl。第三天业务又想加个权限认证,再搞一个类。第四天又要把日志,时间,权限整合一起,类+1。第五天又要分开,类+2,等等等等。说了这么多,想必大家已经看到继承的缺点了。如果我们使用继承作为静态代理,随者需求的变化创造代理类将是无限的,各种需求之间进行种种的排列组合,这种链是继承的关系也是非常复杂。此外链式继承本身就是一个代理角色替换的例子。显然,继承不是一个合理的解决方案,那我们接着看聚合会不会好一点。

聚合

也可以总结为一句话:目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。

既然说到了接口,那么首先要有一个接口QueryDao,然后用主业务类QueryDaoImpl去实现它。那么为了实现代理,还是要有一个代理类QueryDaoLog,同样我们还要修改MainTest去运行。

public interface QueryDao {void query();
}
public class QueryDaoImpl implements QueryDao{ //实现接口public void query(){System.out.println("查询数据库内容");}
}
public class QueryDaoLog implements QueryDao { //实现同一个接口QueryDao queryDao;public QueryDaoLog(QueryDao queryDao) {this.queryDao=queryDao;}@Overridepublic void query() {System.out.println("---interface log---");queryDao.query();}
}
public class MainTest {public static void main(String[] args) {QueryDao target=new QueryDaoImpl();QueryDao proxy=new QueryDaoLog(target);proxy.query();}
}
输出结果
---interface log---
查询数据库内容

我们通过让代理对象实现同一个接口,然后传入目标对象完成了这个需求。这个就是聚合,代理对象通过把目标对象聚合到自身来完成任务,就叫做聚合。

如果完成time的代理,也需要添加一个新的代理类,毕竟是一个新功能对吧。

public class QueryDaoTime implements QueryDao {QueryDao dao;public QueryDaoTime(QueryDao dao) {this.dao=dao;}@Overridepublic void query() {System.out.println("---interface time---");dao.query();}
}
输出结果
---interface time---
查询数据库内容

但是要把他们合并在一起的时候,就看出来区别了,比如我们要Time和Log一起用,只需要修改调用过程就好了。如果想要Time和Log反过来,只要把代理顺序换一下就搞定了。完全不需要构造新的类出来,把已有功能聚合在一起就可以完成了。

public class MainTest {public static void main(String[] args) {QueryDao target=new QueryDaoLog(new QueryDaoImpl());QueryDao proxy=new QueryDaoTime(target);//QueryDao target=new QueryDaoTime(new QueryDaoImpl()); //换一下顺序就可以完成切换//QueryDao proxy=new QueryDaoLog(target);proxy.query();}
}
输出结果
---interface time---
---interface log---
//---interface log---
//---interface time---
查询数据库内容

可以看出,聚合能够避免我们每有一个新功能就创建一个类的繁琐操作。但是大家想一个问题:我们当前的代理对象仅仅能够代理QueryDao,万一未来需要一个UserDao,是不是我们要把这一套再来一遍。如果还有OrderDao,又要重复做一遍。我相信在座的肯定不止写过一个Dao,有百个Dao难道要把这整一套过程也重复个几百遍?所以可以看出,虽然聚合比继承要灵活,但是其缺点和继承一样,也会产生类爆炸,只不过比继承少一点点。

总结

代理主要目的是为了在不需要修改代码的情况下就能够增强代码功能。然后才是增强安全性,增加扩展度,灵活度等等。但是如果在需求不确定的情况下,使用静态代理将会造成毁灭性的编码灾难。因为一旦开始构建需求,就会构建对应的类。一旦类产生了,就会因为需求的改变,演变成新的类,进而引起类爆炸。为了解决这个问题,码农的前辈们就整出了动态代理,那么我们下一篇【什么是动态代理?】就会讲讲动态代理怎么解决这个问题的。

什么是代理(Proxy)?相关推荐

  1. 动态代理proxy与CGLib的区别

    转载自 动态代理proxy与CGLib的区别 昨天被人问及动态代理与CGlib的区别,赶紧回顾一下: 什么是代理? 静态代理与动态代理 静态代理实例 JDK动态代理实例 CGLib 简介 CGLib ...

  2. 设计模式学习笔记——代理(Proxy)模式

    设计模式学习笔记--代理(Proxy)模式 @(设计模式)[设计模式, 代理模式, proxy] 设计模式学习笔记代理Proxy模式 基本介绍 代理案例 类图 实现代码 Printable接口 Pri ...

  3. Java二十三种设计模式 之代理(proxy)

    Java二十三种设计模式 之代理(proxy) 今天我们学习一下静态代理和动态代理 我们来看代码(写一个坦克运行了多少时间): 第一种方法: public calss Tank implements ...

  4. 【java】深入分析Java反射-动态代理 proxy

    1.概述 转载:深入分析Java反射(四)-动态代理 [Java]Java 反射机制浅析 [Java]java代理 静态代理 动态代理 proxy [java]静态代理 proxy 2.动态代理的简介 ...

  5. 从入门到入土:Python requests代理 proxy配置 HTTP

    此博客仅用于记录个人学习进度,学识浅薄,若有错误观点欢迎评论区指出.欢迎各位前来交流.(部分材料来源网络,若有侵权,立即删除) 本人博客所有文章纯属学习之用,不涉及商业利益.不合适引用,自当删除! 若 ...

  6. 网络中的代理(proxy)和NAT

    简介ip地址和子网掩码 答: ip地址包括网络地址(前三段数字)和主机地址(第四段数字) 子网掩码用于计算出网络地址,控制广播范围. 网络中的代理(proxy)是什么意思?NAT是什么意思? 答: p ...

  7. js-4 代理Proxy,object原型链, prototype, 继承,

    1代理Proxy 1.什么是代理Proxy拦截? 可以对对象,函数,数组进行拦截,将其原本的函数操作改写. Proxy在目标对象前设一个拦截层,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机 ...

  8. yum配置代理proxy

    yum代理proxy: Linux/CentOS设置全局代理(http) 说明:为什么说是http代理,其实这个还不能说是全称走代理,罪名写的区别就是ICMP协议这个设置就无效,只能说是90%的应用都 ...

  9. linux下最简单多线程单文件socks5代理proxy服务器程序(仅一个c文件,带详细注解)

    2020.9.11 网上看的都比较复杂,GITHUB找了一个多线程的相对简单的MicroSocks项目,花了几周时间(没办法,菜鸟一个)改了一下,改成单文件,测试OK. /* wxl_socks5_p ...

最新文章

  1. ios游戏开发 Sprite Kit教程:初学者 2
  2. 如何跟面试公司谈论薪资?
  3. 【Azure Show】|第五期(下)当下最火热的Blazor与App Service, 嘉宾闫晓迪Alan Tsai...
  4. vgp虚拟路面_长安大学工程机械国家虚拟仿真实验教学中心|公路路面摊铺施工虚拟仿真实验|...
  5. 2021年中国主轴修复服务市场趋势报告、技术动态创新及2027年市场预测
  6. Python 进阶之递归函数一点都不难!
  7. 利用rsync+crontab实现linux的定时增量备份,利用rsync+crontab实牡现Linux服务器间的定时增量备份.doc...
  8. php在windows安装,php在windows环境下的安装
  9. shell脚本中的日期处理
  10. 我和老公清北毕业,我能接受自己的孩子读三流学校吗?
  11. 重置Winsock2
  12. 基于MATLAB 的X-CT图像重建计算机仿真实验研究实验
  13. 【转载】 mybatis入门系列四之动态SQL
  14. pyspark groupby 后将遍历的每一行转成pandas df
  15. 如何盘活客户资源,提升成单率?
  16. 苹果开发者关联封号扫盲贴
  17. BZOJ 3876 AHOI2014 支线剧情 费用流
  18. 网球爱好者小程序的设计与实现
  19. 京东金融APP被曝侵犯隐私
  20. Excalidraw:开源趣味画图工具(在线和本地均可)

热门文章

  1. 在拼多多上抢了点茅台
  2. Java 面向对象 习题2(基础篇)
  3. 妙招!如何用Python巧妙的批量合并 Excel!
  4. 红孩儿编辑器的输入法模块的函数依赖关系图
  5. 2021CCPC网络预选赛
  6. 配置GeeM2传奇登陆器详细图文教程
  7. 怎么使用黑鲨U盘启动制作U盘系统?
  8. Word上次启动失败,安全模式可以希助您解决问题,但是部分功能在此模式下可能不可用
  9. 亿级流量电商JVM调优(转图灵学院)
  10. App crash原因以及解决办法