在 代理模式:女朋友这么漂亮,你缺经纪人吗? 中我们用宝强的例子介绍了静态代理模式的概念。
本来我的目的是通过大家耳熟能详的例子来加深理解,但是有些网友指责我“没底线”、“幸灾乐祸”,其实我比你们谁都爱宝强!他的每个电影我都看,电影里他受苦了我都心疼,比如说看到《盲井》里弱小的他被失足女欺负,我只想说:放开宝强,换我来!
OK,这篇文章我们将结合其他例子介绍动态代理模式。: )

回顾静态代理

为了加深理解我们回顾一下静态代理,定义一个规定行为的明星电影接口 IMovieStar

/*** 影星接口,定义影星的基本行为* Created by zhangshixin on 8/25/2016.*/
public interface IMovieStar {/*** 演电影* @param money 演电影的片酬,以 int 为单位就够了,除了星爷,没听说谁的片酬能上亿*/void movieShow(int money);/*** 演电视剧* @param money 演电视剧的片酬*/void tvShow(int money);
}

再定义一个 IMovieStar 的实现类 Star

/*** 明星,可能是影歌双栖* Created by zhangshixin on 8/25/2016.*/
public class Star implements IMovieStar{private String mName;public Star(String name) {mName = name;}@Overridepublic void movieShow(int money) {System.out.println(mName + " 出演了部片酬 " + money + " 元的电影");}@Overridepublic void tvShow(int money) {System.out.println(mName + " 出演了部片酬 " + money + " 元的电视剧");}
}

最后定义个代理类 Agent,它引用了一个 Star 对象,并且对 Star 的行为进行了控制:

/*** 经纪人,代理某个明星,有活动、广告、电影都是先找经纪人沟通* Created by zhangshixin on 8/25/2016.*/
public class Agent implements IMovieStar {/*** 代理的明星*/Star mTarget;public Agent(Star target) {mTarget = target;}@Overridepublic void movieShow(int money) {if (money < 30000000) {System.out.println(money + "块钱?!你雇 HuangZiTao 演电影去吧!");return;}mTarget.movieShow(money);}@Overridepublic void tvShow(int money) {if (money < 30000000) {System.out.println(money + "块钱?!你雇 HuangZiTao 演电视剧去吧!");return;}mTarget.tvShow(money);}
}

最后进行单元测试:

    @Testpublic void testMovieShow() throws Exception {Star huangBo = new Star("HuangBo");Agent agent = new Agent(huangBo);// 网上查到,2016年黄渤的片酬达到了 3000W ,这得敲多少年代码额呜呜agent.movieShow(1000000000);agent.tvShow(5);}

运行结果:

可以看到,被代理类 Star 只需要完成自己的功能,不用因为业务逻辑而频繁修改代码,取而代之的是用 Agent 来做中间人,由它来代替 Star 完成一些业务操作。

静态代理弊端

我们可以看到,
- 我们需要在运行前手动创建代理类,这意味着如果有很多代理的话会很累哎;
- 其次代理类 Agent 和 被代理类 Star 必须实现同样的接口,万一接口有变动,代理、被代理类都得修改,容易出问题。

作者:张拭心 http://blog.csdn.net/u011240877

主角出场:动态代理

动态代理静态代理 最大的区别就是不用我们创建那么多类,敲那么多代码。在程序运行时,运用反射机制动态创建而成。

JDK 中为我们提供了 Proxy 类来实现动态代理,其中最重要的方法是 newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler)throws IllegalArgumentException {//...方法内容略去}

参数介绍:
- ClassLoader loader // 被代理类的类加载器,用来创建代理类
- Class<?>[] interfaces //被代理类实现的接口,创建的代理类会实现这些接口
- InvocationHandler invocationHandler //最关键的接口!它只有一个 invoke 方法,是代理类进行 拦截操作 的入口,一般需要自定义一个 Handler 来实现方法增强

举个栗子

我们自定义一个 Handler 来实现上述静态代理例子中 经纪人对片酬的控制:

/*** 自定义的动态代理处理器* Created by zhangshixin on 8/26/2016.*/
public class ProxyHandler implements InvocationHandler {//被代理对象private Object mTarget;public ProxyHandler(Object target) {this.mTarget = target;}/*** 方法拦截,可以进行一些额外操作* @param proxy* @param method 拦截的方法* @param args 方法对应的参数* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodName = method.getName();if (methodName.equals("movieShow") || methodName.equals("tvShow")) {if (args[0] instanceof Integer && ((int) args[0]) < 300000000) {System.out.println(((int) args[0]) + "块钱?!你雇 HuangZiTao 演去吧!");return null;}}Object result = method.invoke(mTarget, args);return result;}/*** 获取代理* @return*/public Object getProxy() {return Proxy.newProxyInstance(mTarget.getClass().getClassLoader(), mTarget.getClass().getInterfaces(), this);}
}

可以看到,我们可以在 invoke 方法中,根据 method 的名称、创建类等信息进行相应的拦截、处理。

注意!ProxyHandler 中我们创建了 getProxy() 方法,这个方法用于调用 Proxy.newProxyInstance(…) 方法生成代理类。

单元测试:

@Testpublic void testInvoke() throws Exception {Star huangBo = new Star("HuangBo");ProxyHandler proxyHandler = new ProxyHandler(huangBo);IMovieStar agent = (IMovieStar) proxyHandler.getProxy();agent.movieShow(1000000000);agent.tvShow(100);}

走两步:

可以看到,使用时只需要传入明星即可,以后即使这个 IMovieStar 接口修改,也不会影响到经纪人。

除此以外,即使这个明星新增了其他功能,经纪人也不必修改太多。比如黄渤早年其实是个歌手,唱歌不得志只好去演戏,成为影帝后人们才关注他的歌声(真是个“看脸、看名”的世界):

/*** 明星,可能是影歌双栖* Created by zhangshixin on 8/25/2016.*/
public class Star implements IMovieStar, ISingerStar {private String mName;public Star(String name) {mName = name;}@Overridepublic void movieShow(int money) {System.out.println(mName + " 出演了部片酬 " + money + " 元的电影");}@Overridepublic void tvShow(int money) {System.out.println(mName + " 出演了部片酬 " + money + " 元的电视剧");}/*** 黄渤早年其实是个歌手!唱歌一流* @param number 歌曲数*/@Overridepublic void sing(int number) {System.out.println(mName + " 唱了 " + number + " 首歌");}
}

要使用明星的唱歌功能,就要返回一个 ISingerStar 类型的经纪人,这里

@Testpublic void testInvoke() throws Exception {Star huangBo = new Star("HuangBo");ProxyHandler proxyHandler = new ProxyHandler(huangBo);IMovieStar agent = (IMovieStar) proxyHandler.getProxy();agent.movieShow(1000000000);agent.tvShow(100);//黄渤早年其实是个歌手!唱歌不得志只好去演戏,成为影帝后人们才关注他的歌声,真是个“看脸、看名”的世界ISingerStar singerAgent = (ISingerStar) proxyHandler.getProxy();singerAgent.sing(1024);}

运行结果:

案例浅析:Retrofit 中的动态代理

我们知道,Retrofit 是使用 依赖注入+ 动态代理,其中动态代理的入口就是这句:

        // 这一句跟我们的动态代理有些相似,都是传入一个接口GankService gankService = retrofit.create(GankService.class);

我们进去一探究竟:

public <T> T create(final Class<T> 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);}ServiceMethod serviceMethod = loadServiceMethod(method);OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);return serviceMethod.callAdapter.adapt(okHttpCall);}});}

我们可以看到,自定义的请求接口中的每个方法都会被拦截,然后根据方法声明类、调用平台等筛选要控制的方法,最后将要进行网络请求的方法进行转换、适配,最后返回一个网络请求代理类。

了解动态代理后,再看这段代码不是那么吃力了吧!Retrofit 我们暂且了解到这,等掌握更多设计模式、网络基础后再进行具体的解析。

总结

上篇文章通过明星与经纪人的关系介绍了静态代理,不好之处在于一个经纪人只能代理一个明星,一旦明星有变动,或者想要代理其他明星时,需要修改、创建经纪人,大量使用这种静态代理,会使我们系统内的类的规模增大,并且不易维护;

而动态代理模式,做到了”一个经纪人代理 N 个明星“,大大减少类的创建、修改成本。此外动态代理还符合 AOP (面向切面编程) 思想,在很多场合都有使用。

Proxy 类的静态方法创建的动态代理类具有以下特点
- 动态代理类是* public* 、final 和非抽象类型的;
- 动态代理类继承了 java.lang.reflect.Proxy 类;
- 动态代理类的名字以 “$Proxy” 开头;
- 动态代理类实现 newProxyInstance() 方法中参数 interfaces 指定的所有接口;

JDK 动态代理的实现方式
1. 自定义实现 InvocationHandler
2. 根据方法信息进行拦截、控制
3. 调用时传入代理对象
4. 根据要使用方法决定返回代理类的类型

代码地址点这里

备注

本文所讨论的动态代理实现方式是使用 JDK 提供的 Proxy 类,这个类只支持对接口实现类的代理,这在有些场景下会有约束。

针对这种情况,有人创建了 CGLIB (Code Generation Library) 开源项目,它补充了JDK 动态代理仅支持接口实现类的不足:

CGLIB 是一个强大的高性能的代码生成包。广泛的被许多AOP 的框架使用,例如 Spring AOPdynaop ,为他们提供方法的 interception(拦截)。 – 百度知道

有兴趣的同学可以去了解一下

PS : 我是一名 Android 开发一年半经验的小菜鸟,由于意识到自己只会调用 API 而不了解底层原理,所以设定了一系列进阶计划并定期更新,其中包括设计模式、网络基础、多线程、注解、框架解析、性能优化、JVM 深入理解等内容,欢迎持续关注我的博客 http://blog.csdn.net/u011240877,我们一起进步!

感谢

  • http://rejoy.iteye.com/blog/1627405
  • http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html
  • http://wl9739.github.io/2016/07/23/%E7%BB%86%E8%81%8A%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/
  • http://blog.csdn.net/zhuyu714997369/article/details/52023664
  • http://zhidao.baidu.com/link?url=sh6dXxXOxOog52SPG6luMIXhR66dd4i6mOJsBN_q9j9oWwu9qsBw5QWkWLTrjBsGS5-3y5GwgOhd1sCq2uMLlq

动态代理:1 个经纪人如何代理 N 个明星相关推荐

  1. ​cglib实现动态代理构建带参数的代理实例

    2019独角兽企业重金招聘Python工程师标准>>> cglib实现动态代理构建带参数的代理实例: package com.carl.test.proxy.cglib;import ...

  2. 代理模式详解(静态代理和动态代理的区别以及联系)

    原文链接:https://www.cnblogs.com/takumicx/p/9285230.html 1. 前言 代理模式可以说是生活中处处可见.比如说在携程上定火车票,携程在这里就起到了一个代理 ...

  3. 代理模式-Java实现-静态代理、动态代理

    目录 代理模式 静态代理 动态代理 代理模式 代理模式指的是一个类代表另一个类的功能,也就是一个类可以作为另一个类的代理. 代理模式属于结构型模式. 代理类不仅拥有真实类的功能,还可以提供一些额外的附 ...

  4. SSM3==理解静态代理、动态代理Proxy.newProxyInstance、cglib代理==通过纯XML配置spring AOP,通过纯注解配置spring AOP

    静态代理: 为什么要代理?在不改动原代码的基础上,丰富调用某个方法时实现的功能. 比如service类中原本update只会更新,但是通过代理类加上了判断权限和输出时间的功能. 其实这些功能也可以写在 ...

  5. 交换机的基本原理(特别是动态ARP、静态ARP、代理ARP)

    第六章:交换机的基本配置 二层交换设备工作在OSI模型的第二层,即数据链路层,它对数据包的转发是建立在MAC(Media Access Control )地址基础之上的.二层交换设备不同的接口发送和接 ...

  6. 基于动态径向基函数(DRBF)代理模型的优化策略

    基于动态径向基函数(DRBF)代理模型的优化策略 在工程计算中,我们经常遇到需要求解优化问题,尤其在现在十分有前景的机器学习领域,如何快速.高效的求解优化问题,成为机器学习算法是否高效.准确的必要条件 ...

  7. Java设计模式:(一)动态代理分析 (含静态代理)

    代理模式:为其他对象提供一种代理以控制某个对象的访问.用在:在某些情况下,一个客户不想或者不能直接访问另一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用,代理对象还可以完成它附加的操作. ...

  8. python接入讯代理_scrapy中使用讯代理动态转发

    scrapy源代码中查找http11.py文件,相对路径为: Lib/site-packages/scrapy/core/downloader/handlers/http11.py 找到下面内容,注释 ...

  9. 代理设计模式(JDK动态代理)什么是代理,静态代理动态代理实现,分析JDK代理实现逻辑,手动实现JDK代理逻辑。

    什么是代理? 从字面意思来看,代理比较好理解,无非就是代为处理的意思.举个例子,现在哪吒有女助理了,所以很多事情都不用我自己去处理了,比如说去银行排队取钱,那么我就可以叫我的女助理去代替我取钱并交给我 ...

最新文章

  1. 自定义View字段表头
  2. 疯狂的程序员-第五章
  3. flask-migrate数据迁移
  4. 如何写单片机的寄存器,这篇文章带你入门。
  5. Linux C 数据结构---单向链表
  6. 概率论与数理统计思维导图知识框架_考研概率论与数理统计 综合题型秘籍思维导图① 随机变量1~3章 [21考研上岸之旅]...
  7. LeetCode 1601. 最多可达成的换楼请求数目(回溯+剪枝)
  8. 设置linux拨号服务端,CentOS Linux上搭建PPPoE服务器及拨号设置
  9. 双曲函数奇偶性_基本初等函数之奇偶性(强基系列42)
  10. 关于thymeleaf配置语法运用 以及 静态资源问题总结 2021-06-08
  11. 国产卫星高分四号(GF4)预处理(辐射定标)
  12. C# WinForm ListView控件用法详解
  13. Java垃圾回收机制知识点总结
  14. php 车牌号限号,机动车限行尾号今天起轮换 周一至周五分别限行4和9、5和0、1和6、2和7、3和8...
  15. java生成视频缩略图
  16. 网络抓取ts文件转mp4_TS格式的视频文件怎么转换成mp4文件。
  17. 树莓派 python 驱动 lcd tft spi 2.8寸 ili9341 240x320
  18. python在哪里学比较好,python从哪里学起
  19. 教你几招!做客服怎么应对物流太慢的问题
  20. 《设计模式解析》读书笔记

热门文章

  1. 《权游》第八季震撼开播,10行代码动态展示其中的爱恨情仇
  2. 项目中使用过的Soc
  3. CS5266设计Typec转HDMI+PD+U2+U3四合一多功能拓展坞方案
  4. 痞子衡嵌入式:串口调试工具Jays-PyCOM诞生记 - 索引
  5. 【20210409期AI简报】INT8加速训练方案、用树莓派打造的寄居蟹机器人
  6. .net软件开发工程师面试题精选
  7. 收了赎金还撕票?世界安全专家已不能忍,合力对抗勒索软件 | RSA 2017
  8. 个人评测酷盘 kanbox 网络硬盘的使用体验
  9. 检测cpu是否支持VT
  10. NOIP2013 花匠解题报告