代理(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 ...
随机推荐
- set和push方法压入栈顶的值获取方法
向值栈里面放数据(储存的位置在root域里面) 向值栈放数据有多种方式,往往我们只用其中一种 1.set方法压栈 1)在Action中获取值栈对象,使用set()方法向值栈存放数据 ActionCon ...
- LuoGu-P2863牛的舞会The Cow Prom[tarjan 缩点模板]
传送门:https://www.luogu.org/problemnew/show/P2863 思路:tarjan模板题,之前会的tarjan,一直想学缩点到底是什么操作,发现就是把同组的放在一个数组 ...
- 逆序对 线段树&树状数组 (重制版)
逆序对的定义:长度为n的数组a,求满足i<j时a[i]>a[j]条件的数对个数. 第一次接触这种问题的人可能是更先想到的是n^2去暴力数前面有几个比他大的数. int main() { i ...
- AC自动机 数组实现
AC自动机的实现原理是KMP + 字典树. 学AC自动机之前要先去学KMP 和 字典树. 第一步先构建一个字典树. void Insert(){ , len = strlen(str); ; i &l ...
- android日志搜集原理及方案比较
说明: 本文只讨论Log日志,而不是应用的埋点日志. Android 日志架构 用一张图来了解Android Log的架构: 这里涉及到三个进程: APP进程: 调用Log的接口打日志,最终通过soc ...
- KubeSphere CI/CD+GitLab+Harbor将Spring Boot项目部署至Kubernetes
上一篇文章分享了如何在 KubeSphere 对公共的代码仓库 GitHub 和镜像仓库 DockerHub 创建流水线,本文将继续使用 KubeSphere,基于 Harbor 和 GitLab 创 ...
- python 整型、字符串常用方法、for循环
整型--int 定义:用于比较和计算 python2和python3: python2:python2中油int(整型)和long(长整型):1231312L+ 进制转换: 十进制转二进制:正除2,获 ...
- MapReduce案例运行及分词
首先查询进程,发现hadoop并没有启动 如何配置hadoop,参考我的另外一篇博文<Hadoop环境准备> 接下来,启动hadoop start-all.sh 或者 start-dfs. ...
- window下tomcat的下载安装和环境配置
一.下载安装tomcat 去官网:http://tomcat.apache.org/ 下载自己所需要的版本,解压在没有中文的文件夹路径下. 直接打开压缩包下面,进入bin目录,双击startup.b ...
- 一键升级所有pip过期库
import pipfrom subprocess import callfrom pip._internal.utils.misc import get_installed_distribution ...