代理(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 ...
随机推荐
- tit文件的加密解密
加密 # hello 源文件 # ifmmp 加密 # hello 解密 file = open("Email.txt","r") # 源文件 只针对字符不需要 ...
- MAC使用小技巧
同一窗口中显示多个标签页:使用鼠标左右键中间的滑轮点击 修改文件或文件夹的名字:选中文件或文件夹,按下回车,即可修改
- bzoj 2002 弹飞绵羊 lct裸题
上一次用分块过了, 今天换了一种lct(link-cut tree)的写法. 学lct之前要先学过splay. lct 简单的来说就是 一颗树, 然后每次起作用的都是其中的某一条链. 所以每次如果需要 ...
- codeforces C. Sonya and Problem Wihtout a Legend(dp or 思维)
题目链接:http://codeforces.com/contest/713/problem/C 题解:这题也算是挺经典的题目了,这里附上3种解法优化程度层层递进,还有这里a[i]-i<=a[i ...
- 创建最简单的exe形式COM组件并在MFC程序调用
来新公司学习接手新项目,拿到代码打开解决方案看到里面竟然有40几个工程,有点吃惊.具体看代码也有很多之前没见过的写法,上了几天火. 有件事就没太搞明白,按照文档的说法上层很多软件都要调用IO服务器,但 ...
- [系列] go-gin-api 路由中间件 - 捕获异常(四)
概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常.当系统发生异常时,提示 "系统异常,请联系管理员!",同时并发送 ...
- FreeSql (八)插入数据时指定列
插入数据时指定列,和忽略列对应,未被指定的列将被忽略. var connstr = "Data Source=127.0.0.1;Port=3306;User ID=root;Passwor ...
- 使用java程序作为celery的工作节点
celery是python实现的分布式调度框架,有时候想用celery去调用java服务,正好有一个celery-java的库可以使用,能达到这个效果,记录一下: 先添加依赖: <depende ...
- Hive bucket表
Hive 桶 对于每一个表(table)或者分区, Hive可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分.Hive也是 针对某一列进行桶的组织.Hive采用对列值哈希,然后除以桶的个数求余 ...
- java使用FileSystem上传文件到hadoop文件系统
import java.io.FileNotFoundException; import java.io.IOException; import java.net.URI; import org.ap ...