java代理模式-原来你是这样的代理
来源:互联网 发布:压缩相片软件下载 编辑:程序博客网 时间:2024/04/27 19:19
这几天在看一些框架源码时看到了一个很奇妙的设计模式,有种熟悉个感觉,一时想不出是什么模式,后面经过了解才知道是动态代理,就这样带着好奇心学习了这个模式,更深入了解代理会发现不仅有静态和动态,还有很多其他的代理类别,果然兴趣是最好的老师,效率不错,下面是我一些总结.
一起来体验下,你也会发现,原来你是这样的代理.
什么是代理?
在<大话设计模式>中说到,代理模式,为其他对象提供一种代理以控制对这个对象的访问.
下面通过一个例子,说明下.
商家需要搞活动,请了陈奕迅过来商演唱歌,那么商家需要跟陈奕迅进行
面谈->签合同->首付款->安排行程->唱歌->收尾款
.
在不使用代理的情况下是这样的.
可以看到陈奕迅
好像很忙,除了要唱歌
还要做很多的交互,这样我的爱豆不是要忙死了.使用代理模式
通过代理人来处理一些琐碎的事情后,陈奕迅
就只要负责他独有的功能唱歌
就行了,这样间接的访问对象,有效的减轻了一些重复的操作.
代理的核心角色
我们可以将代理模式分为三大角色:
- 抽象角色
代理角色
和真实角色
的公共方法都会在这里定义.
- 真实角色
- 供给
代理角色
用,这里实现了抽象角色
的业务逻辑. - 关注真正的业务逻辑
- 供给
- 代理角色
真实角色
的代理,根据真实角色的业务逻辑来实现抽象角色
的抽象方法,并可以附件自己的操作.
java中的代理
代理模式(Proxy)是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题.
它也是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.
在java中动态代理机制以巧妙的方式实现了代理模式的设计理念,因此java中也分为动态代理
和静态代理
.
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
- 反射机制,JDK自带的动态代理
- 通过
InvocationHandler
(处理器接口)- 使用invoke方法实现对
真实角色
的代理访问. - 每次通过
Proxy
生产代理类对象时,都要指定的处理器对象.
- 使用invoke方法实现对
- java动态性这块可以深入看看,反射与javassist
静态代理(StaticProxy)
借用Eason-S
大神的类图,静态代理UML类图
talk is cheap,根据上面陈奕迅的案例
来写个demo
- 抽象角色
/* * 明星和代理人的公共接口 */public interface StarInterface { // 面谈 void interview(); // 签合同 void signContract(); // 首付款 void firstPayment(); // 安排行程 void plan(); // 唱歌 void sing(); //收尾款 void finalPayment();}
- 真实角色
/* * 明星实现类 */public class EasonStar implements StarInterface { private final String STAR_NAME = "陈奕迅:"; @Override public void sing() { System.out.println(STAR_NAME+"唱歌"); } @Override public void finalPayment() { System.out.println(STAR_NAME+"收尾款"); } @Override public void signContract() { System.out.println(STAR_NAME+"签合同"); } @Override public void plan() { System.out.println(STAR_NAME+"安排行程"); } @Override public void interview() { System.out.println(STAR_NAME+"面谈"); } @Override public void firstPayment() { System.out.println(STAR_NAME+"预付款"); }}
- 代理角色
public class ProxyStar implements StarInterface { //私有化被代理角色 private StarInterface star; private final String PROXY_NAME = "代理人:"; /* * 使用接口的方式来指向真实角色(多态特性) */ public ProxyStar(StarInterface star) { super(); this.star = star; } @Override public void sing() { // 唱歌是明星的特有方法,代理是没有的,因此需要明星自己来 // 调用陈奕迅的唱歌方法... star.sing(); } @Override public void finalPayment() { System.out.println(PROXY_NAME+"签尾款"); } @Override public void signContract() { System.out.println(PROXY_NAME+"签合同"); } @Override public void plan() { System.out.println(PROXY_NAME+"安排行程"); } @Override public void interview() { System.out.println(PROXY_NAME+"面谈"); } @Override public void firstPayment() { System.out.println(PROXY_NAME+"预付款"); }}
- 客户端类
public class Client { public static void main(String[] args) { //找到陈奕迅 EasonStar realStar = new EasonStar(); //找到代理人,专门为陈奕迅代理 ProxyStar proxyStar = new ProxyStar(realStar); //代理人来完成 proxyStar.interview(); proxyStar.signContract(); proxyStar.firstPayment(); proxyStar.plan(); //这里调用的是 EasonStar的sing方法 proxyStar.sing(); proxyStar.finalPayment(); }}
输出结果:
代理人:面谈
代理人:签合同
代理人:预付款
代理人:安排行程
陈奕迅:唱歌
代理人:签尾款
从上面静态代理demo中,你会发现无论代理角色
或真实角色
都需要实现接口,并且将真实角色
的细节向调用方完全隐藏,可以看下EasonStar
类里面有很多方法,但最终被掉用的只有sing()
,其他都是代理角色
来调用的.
动态代理(DynamicProxy)
动态代理的思维模式与之前的一般模式是一样的,也是面向接口进行编码,创建代理类将具体类隐藏解耦,不同之处在于代理类的创建时机不同,动态代理需要在运行时因需实时创建.
看下代码.
由于抽象角色
和真实角色
的代码跟上面静态代理是一样的这里就直接给出代理角色
和客户端类的代码.
- 代理角色
/** * 动态代理角色 主要代理真实角色的方法, * 被调用的方法都会走 invoke()方法,可以在该方法中处理真实角色的业务逻辑 * * @author relicemxd * */public class StarHandler implements InvocationHandler { //私有的被代理角色 private Object star; private final String PROXY_NAME = "代理人:"; /* * Obj 传入的是需要被代理的真实角色 * 并且通过下面的反射技术获取到要代理的行为 */ public StarHandler(Object star) { super(); this.star = star; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 所有掉代理类的方法都会走这里 // TODO 1. 在转调具体目标对象之前,可以执行一些预处理逻辑 System.out.println(PROXY_NAME + "面谈"); System.out.println(PROXY_NAME + "签合同"); System.out.println(PROXY_NAME + "预付款"); System.out.println(PROXY_NAME + "安排行程"); Object invoke = null; // 因为proxy每调用的方法都会走这里, 因此就可以通过 invoke的特性来做一些逻辑判断 if (method.getName().equals("sing")) { // TODO 2. 转调具体目标对象的方法 // 只有当代理对象调用到了sing的方法,才进入 invoke = method.invoke(star, args); } // TODO 3.在转调具体目标对象之后,可以执行一些后处理逻辑 System.out.println(PROXY_NAME + "收尾款"); return invoke; }}
- 客户端类
public class Client { public static void main(String[] args) { // 陈奕迅准备要找代理(真实角色) EasonStar star = new EasonStar(); // 找到了这个代理人代理(代理角色) StarHandler handler = new StarHandler(star); // jdk提供的代理实例 StarInterface proxyStar = (StarInterface) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), // 类加载器 new Class[] { StarInterface.class }, // 这里必须是接口,否则会报错 handler);// 代理类 // 调用interview 会进入StarHandler类的`invoke方法`, // 但是interview方法不会被执行 // proxyStar.interview(); // 这里调用的是 EasonStar的sing方法 proxyStar.sing(); // proxyStar.finalPayment(); }}
通过动态代理可以看出对抽象角色
我们无需知道他是什么时候创建的,也不用知道改接口实现了什么,并且实现的是InvocationHandler
接口,接口的唯一方法invoke()
用于处理真实角色
的逻辑.
静态代理vs动态代理?
可以从代理的接口和创建过程来分析下他们的不同之处,进一步了解下代理模式.
代理中的接口
共同之处
- 都会在
代理角色
中会创建一个私有的成员变量 - 都需要通过接口来实现代理,主要利用java多态的特性
- 都会在
不同之处
- 静态代理的
真实角色
和代理角色
都会实现同一接口(抽象角色),动态代理则只有真实角色
实现了接口. - 静态代理利用了java的多态特性来实现代理模式,而动态代理巧妙的使用了jdk的反射机制来完成代理,也因此两者区别在于
代理角色
的实现方式不一样,看下面这条描述. - 静态代理的
代理角色
实现的是抽象角色
这个接口,而动态代理实现的是jdk的内置接口InvocationHandler
.
- 静态代理的
创建代理的过程
共同之处
- 两者的创建原理一致,需要通过创建
代理角色
来处理真实角色
的一些业务逻辑.
- 两者的创建原理一致,需要通过创建
不同之处
- 静态代理可以直接编码创建,而动态代理是利用反射机制来抽象出代理类的创建过程.
- 静态代理我们知根知底,要对哪个接口、哪个实现类来创建代理类,所以我们在编译前就直接实现与实现类相同的接口,直接在实现的方法中调用实现类中的相应(同名)方法即可;而动态代理不同,我们不知道它什么时候创建,也不知道要创建针对哪个接口、实现类的代理类(因为它是在运行时因需实时创建的).
- 在客户端中静态代理利用接口的多态来调用被代理方法,而动态代理则比较复杂通过
Proxy.newProxyInstance
来创建一个代理实例从而进行代理.这里有人会问使用的不是反射吗?也没跟接口有关联,其实同样也是使用了多态.使用接口指向代理类的实例,最后会用该实例来进行具体方法的调用即可.
优缺点是什么?
优点:
拓展新好
动态代理,不需要更改原有的代码,能在运行过程中根据接口的类型动态的调用真实角色
,符合开闭原则.解耦
代理角色
可以说是一个中介,隔离了客户端和真实角色
.
缺点:
代码量大
静态代理,需要接口和类,有比较多的重复代码,降低了维护性.编译效率?
动态代理,使用的是反射机制相对效率会降低,但实际差别如何,见下面测试代码
.代码可读性
都是对于接口实现进行代理,因此代码的可读性都不会很好.
两者的效率如何?
下面我对代理sing()
方法进行了代理测试.
public class Client { public static void main(String[] args) throws Exception { // 陈奕迅准备要找代理(真实角色) EasonStar star = new EasonStar(); long start = System.currentTimeMillis(); int threadNum = 10; CountDownLatch latch = new CountDownLatch(threadNum); for (int i = 0; i < threadNum; i++) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { dynamicProy(star);// 163 // staticProxy(star);//96 } latch.countDown(); } }).start(); } latch.await(); long end = System.currentTimeMillis(); System.out.println("总共耗时:" + (end - start)); } public static void dynamicProy(EasonStar star) { // 找到了这个代理人代理(代理角色) StarHandler handler = new StarHandler(star); // jdk提供的代理实例 StarInterface proxyStar = (StarInterface) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), // 类加载器 new Class[] { StarInterface.class }, // 这里必须是接口,否则会报错 handler);// 代理类 proxyStar.sing(); } public static void staticProxy(EasonStar star) { // 找到代理人,专门为陈奕迅代理 ProxyStar proxyStar = new ProxyStar(star); proxyStar.sing(); }}
输出结果:
dynamicProy(star);// 163ms
staticProxy(star);// 96ms
静态代理的效率稍微会比动态代理快一些,不过也没有差别很大,因此在选择代理模式类别时,最好还是根据项目需求来筛选出合适的代理模式.
代理的应用场景?
那么代理在哪里会用到呢?如果你的项目有这几个方面的需求可以考虑使用.
• 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
• 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
• 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
• 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。
代理的分类
当然除了我最常用的静态,动态代理之外根据代理的实现与目标不同还可以分成下面几种代理,具体见其他代理模式
- 安全代理:
屏蔽对真实角色
的直接访问. - 远程代理:
通过代理角色
远程方法调用(RMI) - 延迟加载:
先加载轻量级代理角色
,真正需要再加载真实角色
. - 虚拟代理
允许内存开销较大的对象在需要的时候创建.只有我们真正需要这个对象的时候才创建. - 保护代理
为不同的客户提供不同级别的目标对象访问权限.
如,你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文洁时不能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开.
项目中有没使用过呢?
在android中目前很热门的一个网路工具retrofit
源码中也使用了动态代理.
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) //这里是不是是曾相识呢Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } return loadMethodHandler(method).invoke(args); } });}
参考:
java静态代理与动态代理
公共技术点之 Java 动态代理
设计模式(结构型)之代理模式
每天设计模式-代理模式
静态代理VS动态代理
- java代理模式-原来你是这样的代理
- 原来你是这样的WebView(一)
- 原来你是这样的NullPointerException
- 原来你是这样的阿里云
- 原来你是这样的Accepted
- 原来你是这样的概念图
- 原来你是这样的鱼骨图?
- 原来你是这样的程序员
- 原来mod_cache是需要代理的
- 原来设计模式是这样的
- 这样理解java代理模式更简单!!
- Java的学习阶段原来是这样!
- 原来是这样的
- 原来是这样的
- JAVA的代理模式
- java的代理模式
- Java的代理模式
- Java的代理模式
- php des 加密解密实例
- 35. Search Insert Position★
- 打造自己的vim配置
- Angularjs bootstrap table多选(全选),支持单击行选中 ,实现编辑、删除功能
- CDOJ_758题解
- java代理模式-原来你是这样的代理
- cccc L1-006. 连续因子
- 统计学习之---多尺度变换 MDS (Multidimensional Scaling)
- 块介质恢复
- 章七例六 Bandwidth UVA
- 各种图
- JavaWeb前端知识-CSS初级
- python学习代码每日汇总
- 天梯赛总结