代理(Proxy)设计模式

概述
正文开始之前我们先考虑一个问题:什么叫做代理(Proxy)?
按照维基百科定义:
代理(英语:Proxy)也称网络代理,是一种特殊的网络服务,允许一个网络终端(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。一些网关、路由器等网络设备具备网络代理功能。一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击。
通俗讲代理就类似一个连接在客户端和服务器之间的桥梁,连通客户端和服务器之间的请求和响应,代理的存在一方面可以保护服务器的安全,在代理部分可以对请求信息进行过滤,隔绝一部分非法的请求信息吗,另一方面可以提高用户的访问速度,其具体功能可以借助下边的图来帮助理解。


我们再举个例子,突然某一天你需要见某个身份显赫的王总和他谈一个项目,一般来说你是不可能直接去见人家的,但王总必然是有秘书的,你可以提前跟秘书说,秘书代为向王总转达。王总如果对这个项目感兴趣会让秘书通知你。整个过程中,你就相当于那个客户端,秘书相当于代理,王总就相当于服务器。
如果你理解上述代理的概念,那么代理设计模式也就不难理解了。代理设计模式就是对上边上客户端-代理-服务器三者链式关系的一种抽象,进而应用到软件开发中的一种通用设计模式。
代理设计模式有如下三个优点:
- 保护真实对象
- 让对象职责更加明确
- 易于扩展
在java开发中代理设计模式有三种实现方法:
- 静态代理
- 动态代理 jdk实现
- 动态代理 cglib实现
下边我们分三种情况对这三种代理设计模式的实现进行讨论和分析
静态代理
UML类图

KeHu:客户端MiShhu:中介LaoZong:服务器GongNeng:服务器和中介要同时实现的功能接口
代码实现
GongNeng的java代码:
/**
* @program: TestBlog
* @description:
秘书和老总都要实现的功能接口
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public interface GongNeng {
public void ZuoShengYi();
public void eat();
}
Kehu的java代码
/**
* @program: TestBlog
* @description:
客户相当于客服端
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public class KeHu {
public static void main(String[] args) {
MiShu miShu=new MiShu();
miShu.ZuoShengYi();
}
}
MiShu的java代码
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public class MiShu implements GongNeng{
private LaoZong laoZong=new LaoZong();
public void ZuoShengYi() {
System.out.println("秘书:请问您预约来吗?");
laoZong.ZuoShengYi();
System.out.println("秘书备注访客信息");
}
public void eat() {
System.out.println("秘书:请问您预约来吗?");
laoZong.eat();
System.out.println("秘书备注访客信息");
}
}
LaoZong的java代码:
package proxy.staticproxy;
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public class LaoZong implements GongNeng{
public void ZuoShengYi() {
System.out.println("老总:谈个小项目!!");
}
public void eat() {
System.out.println("老总:吃饭!!");
}
}
运行结果为:
秘书:请问您预约来吗?
老总:谈个小项目!!
秘书备注访客信息
Process finished with exit code 0
代码地址
详细的代码可以参看github的上的代码
静态代理的不足
毫无疑问静态代理作为最容易实现或者说最直观的的代理设计模式的实现方式,代理模式具有的优点它必然也具有,但另一方面它也有许多缺点:
- 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
- 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
为了解决该问题我们引入了动态代理
动态代理之jdk实现
UML类图

Client:客户端MiShhu:中介LaoZong:服务器GongNeng:服务器和中介要同时实现的功能接口
代码实现
Client的java代码:
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 17:04
**/
public class Client {
public static void main(String[] args) {
//第一个参数:反射时使用的类加载器
//第二个参数:Proxy需要实现什么接口
//第三个参数:通过接口对象调用方法时,需要调用哪个类的invoke方法
GongNeng gongneng = (GongNeng) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{GongNeng.class}, new MiShu());
gongneng.eat();
}
}
GongNeng的java代码:
public interface GongNeng {
public void ZuoShengYi();
public void eat();
}
MiShu的java代码
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:56
**/
public class MiShu implements InvocationHandler {
private LaoZong laozong=new LaoZong() ;
//代理类针对被代理对象类似的功能不需要重复实现多次
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("预约时间");
Object result = method.invoke(laozong, args);
System.out.println("记录访客信息");
return result;
}
}
LaoZong的java代码:
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:49
**/
public class LaoZong implements GongNeng{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void ZuoShengYi() {
System.out.println("老总:谈生意");
}
public void eat() {
System.out.println("老总:吃饭!!");
}
}
运行结果为:
预约时间
老总:吃饭!!
记录访客信息
Process finished with exit code 0
利用JDK实现动态代理的优点
相比与静态代理,利用JDK实现动态代理的方式实现了代理类和功能接口之间的解耦。对于委托类如果增加某个方法,对于代理类代码几乎可以不变,减少了代码的复杂性,使其更加易于维护。另一方面在代理不同类型对象时可以实现代码一定程度的复用。
利用JDK实现动态代理的不足
但是该方法实现动态代理也有一定不足,由于其内部借助反射实现代理设计模式,系统开销大效率低。而且其委托类仍需实现功能接口,代码耦合性还是不够低。
代码地址
详细的代码可以参看github的上的代码
动态代理之cglib实现
UML类图

Client:客户端MiShhu:中介LaoZong:服务器
代码实现
Client的java代码
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(LaoZong.class);
enhancer.setCallback(new MiShu());
LaoZong laozong = (LaoZong) enhancer.create();
laozong.chifan();
}
}
MiShu的java代码:
public class MiShu implements MethodInterceptor{
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("预约时间");
Object result = arg3.invokeSuper(arg0, arg2);
System.out.println("备注");
return result;
}
}
LaoZong的java代码:
public class LaoZong {
public void chifan() {
System.out.println("吃饭");
}
public void mubiao() {
System.out.println("目标");
}
运行结果为:
预约时间
吃饭
备注
Process finished with exit code 0
利用cglib实现动态代理的优点
通过cglib方式几乎完美的解决来jdk方式所具有的缺点一方面cglib方式内部是通过字节码方式实现动态代理,效率高,执行速度快;另一方面,该方式解耦了委托类和功能接口之间的耦合,提高了代码的灵活性。
代码地址
详细的代码可以参看github的上的代码
代理(Proxy)设计模式的更多相关文章
- JAVA设计模式-动态代理(Proxy)示例及说明
在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...
- JAVA设计模式-动态代理(Proxy)源码分析
在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...
- 100行代码让您学会JavaScript原生的Proxy设计模式
面向对象设计里的设计模式之Proxy(代理)模式,相信很多朋友已经很熟悉了.比如我之前写过代理模式在Java中实现的两篇文章: Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理 J ...
- 初识代理——Proxy
无处不在的模式——Proxy 最近在看<设计模式之禅>,看到代理模式这一章的时候,发现自己在写spring项目的时候其实很多时候都用到了代理,无论是依赖注入.AOP还是其他,可以说是无处不 ...
- 代理(Proxy)和反射(Reflection)
前面的话 ES5和ES6致力于为开发者提供JS已有却不可调用的功能.例如在ES5出现以前,JS环境中的对象包含许多不可枚举和不可写的属性,但开发者不能定义自己的不可枚举或不可写属性,于是ES5引入了O ...
- 深度揭秘ES6代理Proxy
最近在博客上看到关于ES6代理的文章都是一些关于如何使用Proxy的例子,很少有说明Proxy原理的文章,要知道只有真正掌握了一项技术的原理,才能够写出精妙绝伦的代码,所以我觉得有必要写一篇关于深刻揭 ...
- Webpack代理proxy配置,解决本地跨域调试问题,同时允许绑定host域名调试
Webpack代理proxy配置,解决本地跨域调试问题,同时允许绑定host域名调试 会撸码的小马 关注 2018.05.29 17:30* 字数 212 阅读 1488评论 0喜欢 2 接到上一章, ...
- 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance
浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...
- java动态代理--proxy&cglib
大纲 代理 proxy cglib 小结 一.代理 为什么要用代理?其实就是希望不修改对象的情况下,增强对象. 静态代理: 静态代理模式,需要代理类和目标类实现同一接口,代理类的方法调用目标类的方法, ...
- 设计模式--代理(Proxy)模式
在公司,经常性听到采购部的人说采购某样东材料,采购不了,需要通过代理商才可以.以前Insus.NET也做有一个练习<找人办事,代理设计模式(Proxy)>http://www.cnblog ...
随机推荐
- java学习之String类
标签(空格分隔): String类 String 的概述 class StringDemo{ public static void main(String[] args){ String s1=&qu ...
- Badboy - 导出脚本,用于JMeter性能测试
参考: http://leafwf.blog.51cto.com/872759/1131119 http://www.51testing.com/html/00/130600-1367743.html ...
- poj - 1860 Currency Exchange Bellman-Ford 判断正环
Currency Exchange POJ - 1860 题意: 有许多货币兑换点,每个兑换点仅支持两种货币的兑换,兑换有相应的汇率和手续费.你有s这个货币 V 个,问是否能通过合理地兑换货币,使得你 ...
- CodeForces 812E Sagheer and Apple Tree 树上nim
Sagheer and Apple Tree 题解: 先分析一下, 如果只看叶子层的话. 那么就相当于 经典的石子问题 nim 博弈了. 那我们看非叶子层. 看叶子层的父亲层. 我们可以发现, 如果从 ...
- codeforces 361 C. Levko and Array Recovery(暴力+思维)
题目链接:http://codeforces.com/contest/361/problem/C 题意:对一个数列有这么两个操作 1.(1,l,r,p)..将区间[l,r]所有数都加上p 2.(2,l ...
- CCPC-Wannafly Summer Camp #1(部分解题报告)
A:Birthday 时间限制: 1 Sec 内存限制: 256 MB 题目描述 恬恬的生日临近了.宇扬给她准备了一个大蛋糕. 正如往常一样,宇扬在蛋糕上插了n支蜡烛,并把蛋糕分为m个区域.因为某种 ...
- java课堂测试样卷-----简易学籍管理系统
程序设计思路:分别建立两个类:ScoreInformation类(用来定义学生的基本信息以及设置set和get函数)ScoreManagement类(用来定义实现学生考试成绩录入,考试成绩修改,绩点计 ...
- Linux运维基础提高之RAID卡和磁盘分区
磁盘大小计算: 柱面的数量*每个柱面的大小(容量) [root@luffy001 ~]# fdisk -l Disk /dev/sda: 10.7 GB, 10737418240 bytes 255 ...
- Springboot中RedisTemplate的操作
Springboot中RedisTemplate的操作 @Autowired private RedisTemplate redisTemplate; @Autowired private Strin ...
- 线上LVM磁盘挂载
ESC云服务器磁盘扩容 日常运行中,容器服务所在目录由于container-log.image的增加需要占用大量的磁盘空间,所以对/var/lib/docker/目录进行扩容挂载LVM数据盘. 首先查 ...