有情怀,有干货,微信搜索【三太子敖丙】关注这个有一点点东西的程序员。

本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点、资料以及我的系列文章。

设计模式已经跟大家分享很多了常见的模式了,感兴趣的小伙伴可以再回顾一下,巩固一下理解。

这次要跟大家分享的是设计模式中三大类创建型中的代理模式,代理模式在业务场景上我们可能不会经常用到,但是面试官却会经常问一个问题?

请你跟我讲讲Spring里面AOP的代理模式?jdk的代理模式和cglib的代理模式又啥区别?

清楚和不清楚的同学都可以接着向下看,一定会有收获。

言归正传,接下来开始一步一步分析一下代理模式。

定义以及目的

首先代理模式可以分为多种类型

  • 远程代理:就是将工作委托给远程对象(不同的服务器,或者不同的进程)来完成。常见的是用在web Service中。还有就是我们的RPC调用也可以理解为一种远程代理。
  • 保护代理:该模式主要进行安全/权限检查。(接触很少)
  • 缓存代理:这个很好理解,就是通过存储来加速调用,比如Sping中的@Cacheable方法,缓存特定的参数获取到的结果,当下次相同参数调用该方法,直接从缓存中返回数据。
  • 虚拟代理:这种代理主要是为方法增加功能,比如记录一些性能指标等,或进行延迟初始化

上面只是我们作为了解的概念,接下来再看看代理模式有哪些部分构成。

  • Subject(共同接口):客户端使用的现有接口
  • RealSubject(真实对象):真实对象的类
  • ProxySubject(代理对象):代理类

从图中可以看出其实整个接口还是很简单,就是一个真实对象以及代理对象。

目的:提供一个实际代理对象,以便更好的控制实际对象。 以上定义来自《设计模式之美》

代码举例实现

为了方便理解,还是举一个例子,不知道大家在读初中或者高中是否经历过传小纸条的过程,假如现在同学A 对同学C有一些话想聊(比如放学相约一起打游戏)但是因为现在是上课时间,又不能大声说,同学A和同学C之间坐了一个同学B,所以现在同学A只能是先找到同学B把纸条给它,让他转告同学C,但是去玩还是不是不去玩,那还是只能真正的同学C自己才能决定。

所以代理模式可以理解为 同学B是同学的C的代理,同学A要找同学C,只能找到同学B,通过同学B转达同学C,同时将同学的C的执行结果反馈给同学A。

说完了例子还是具体看看代码的实现吧

public interface Subject {// 共同的接口void doSomething();
}

定义一个共同的接口(即大家要做的事请:放学一起打游戏)

public class RealSubject implements Subject {// 真实对象@Overridepublic void doSomething() {System.out.println("放学去打游戏");}
}

构建一个真实对象,即例子中的同学C

public class ProxySubject implements Subject {private RealSubject realSubject;public ProxySubject(RealSubject realSubject) {this.realSubject = realSubject;}public ProxySubject() throws ClassNotFoundException, IllegalAccessException, InstantiationException {this.realSubject = (RealSubject) this.getClass().getClassLoader().loadClass("com.ao.bing.demo.proxyPattern.RealSubject").newInstance();}@Overridepublic void doSomething() {realSubject.doSomething();}public static void main(String[] args) {try {// 第一种方式new ProxySubject().doSomething();// 打印结果: 放学去打游戏} catch (Exception e) {// 异常情况,代理失败,// 传纸条的被老师抓了。或者同学C不在座位上了 等异常情况}// 第二种方式new ProxySubject(new RealSubject()).doSomething();// 打印结果: 放学去打游戏}
}

构建代理对象,即同学B,那么可以看到同学A并没有真实接触到同学C,通过同学B对同学C的代理就能知道同学C放学能不能跟他一起去打游戏

在Main方法里面,有两种方式来调用真实对象

  • 第一种:采用类加载器形式,去加载实列对象,这样我们就不同关心到底什么时候需要真实的实列化对象

  • 第二种:通过传值的形式,把实列化对象传过来。(理解为装饰器模式了)

    这里大家要区别一下,代理模式是提供完全相同的接口,而装饰器模式是为了增强接口。

静态代理、动态代理和cglib代理分析

静态代理

在上面的举的列子实现其实就是静态代理,大家可以看到整体也比较简单。但是它的缺点也很明显

静态代理需要为每一个对象都创建一个代理类,增加了维护成本以及开发成本,那么为了解决这个问题,动态代理就出来了,不要再固定为每一个需要代理的类而创建一个代理类

动态代理

动态代理合理的避免了静态代理的那种方式,不用事先为要代理的类而构建好代理类。而是在运行时通过反射机制创建。

在写动态代理事需要理解两个东西:Proxy 可以理解为就是调度器,InvocationHandler 增强服务接口可以理解为代理器。 所以我个人理解动态代理其实就是一种行为的监听。

具体的代码实现举一个例子:螳螂捕蝉,通过通过螳螂监听到蝉的动作。方便后面讲到多级代理模式。

public interface BaseService {void mainService();
}public class Cicada implements BaseService {@Overridepublic void mainService() {System.out.println("主要业务,以蝉为例,当蝉出现业务调用时,螳螂监听到");}
}

创建共同接口,以及真实对象蝉

public class PrayingMantis implements InvocationHandler {private BaseService baseService;// 这里采用的是构建传参数,可以用反射,举的第一个例子有样式代码public PrayingMantis(BaseService baseService) {this.baseService = baseService;}// 螳螂主要业务,也就是监听对象@Overridepublic Object invoke(Object listener, Method method, Object[] args) throws Throwable {method.invoke(baseService,args);secondaryMain();return null;}// 这里理解增强业务,即我们可以在实现InvocationHandler里面添加其他的业务,比如日志等等。private void secondaryMain(){System.out.println("螳螂捕蝉 - 次要业务");}
}

创建螳螂类,监听着蝉的类的动作

public class BeanFactory {public static BaseService newInstanc(Class classFile) {// 1. 创建蝉,真实类对象BaseService trueCicada = new Cicada();// 2.创建代理类 螳螂InvocationHandler prayingMantis = new PrayingMantis(trueCicada);// 3.向Jvm索要代理对象 其实就是监听的对象,Class classArray[] = {BaseService.class};BaseService baseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, prayingMantis);return baseService;}// 测试Demopublic static void main(String[] args) {BaseService baseService  = newInstanc(Cicada.class);baseService.mainService();// 测试结果 :主要业务//           螳螂捕蝉 - 次要业务}
}

通过结果可以看出当蝉主要业务发生调用时,螳螂能监听到蝉的业务并且能处理其他业务逻辑,这也就是Spring里面AOP为什么能处理日志切面等。

代理的本质:

我认为其实就是一种行为的监听,对代理对象($proxy InvocationHandler)的一种监听行为。

代理模式组成:

  • 接口:声明需要被监听行为
  • 代理实现类(InvocationHandler):次要业务,次要业务和主要业务绑定执行
  • 代理对象(监听对象)

Cglib动态代理

cglib动态代理其实和jdk的动态代理是很相似的,都是要去实现代理器接口完成。

具体代码如下:

public class PrayingMantis implements MethodInterceptor {private Cicada cicada;// 代理对象public Cicada getInstance(Cicada cicada) {this.cicada = cicada;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.cicada.getClass());enhancer.setCallback(this);return (Cicada) enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {Object object = methodProxy.invokeSuper(o, objects);secondaryMain();return object;}private void secondaryMain() {System.out.println("螳螂捕蝉 - 次要业务");}public static void main(String[] args) {PrayingMantis prayingMantis = new PrayingMantis();Cicada instance = prayingMantis.getInstance(new Cicada());instance.mainService();// 结果:主要业务//      螳螂捕蝉 - 次要业务}

因为蝉类都是一样的所以我就不单独这里再贴出来。

细心的同学已经发现,Cglib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。

Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。因为是采用继承方式,所以代理类不能加final修饰,否则会报错。

final类:类不能被继承,内部的方法和变量都变成final类型

JDK和Cglib的区别:

jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

cglib动态代理是利用ASM开源包,对被代理对象类的class文件加载进来,通过修改其字节码生成子类来处理

ASM: 一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。 – 以上ASM解释来自简书

多级动态代理

看完上面的动态代理,不知道大家有没有想法,实现一个多级动态代理。

还是以螳螂捕蝉为例子,再加上一个黄雀在后,实现多级动态代理模式。

public class Cardinal implements InvocationHandler {// 监听代理代理对象private Object proxyOne;public Cardinal(Object proxyOne) {this.proxyOne = proxyOne;}// 螳螂主要业务,也就是监听对象@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws            Throwable {method.invoke(proxyOne, args);secondaryMain();return null;}private void secondaryMain() {System.out.println("黄雀吃螳螂 - 次要业务");}
}

创建一个黄雀代理对象,那作为他的真实对象就变成螳螂了,当螳螂对象发生调用时,黄雀就能坚挺到,同时作出对应业务逻辑

public class BeanFactory {public static BaseService newInstanc(Class classFile) {// 1. 创建蝉,真实类对象BaseService trueCicada = new Cicada();// 2.创建代理类 螳螂InvocationHandler prayingMantis = new PrayingMantis(trueCicada);// 3.向Jvm索要代理对象 其实就是坚挺的对象Class classArray[] = {BaseService.class};BaseService baseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, prayingMantis);// 4.创建代理实现类 黄雀 二级代理InvocationHandler cardinal = new Cardinal(baseService);BaseService secondBaseService = (BaseService) Proxy.newProxyInstance(classFile.getClassLoader(), classArray, cardinal);// 假设要实现三级,四级代理,则在黄雀类上再加一层代理即可实现。// 省略其他的更多级代理对象return secondBaseService;}// 测试demopublic static void main(String[] args) {BaseService baseService  = BeanFactory.newInstanc(Cicada.class);baseService.mainService();// 结果:主要业务//      螳螂捕蝉 - 次要业务//      黄雀吃螳螂 - 次要业务}
}

根据这个代码基本就实现多级代理过程。螳螂监听着蝉类的动作,黄雀监听着螳螂类的动作。

同样的如果要实现三级代理,四级代理也就不是什么难事了,在每一层的上面再加一个代理对象就可以了。

动态代理本质还是可以理解为将“次要业务”与“主要业务”解耦合,让开发者能更加专注于主要业务,提升开发效率,以及维护成本。

总结

代理模式在业务代码上我个人认为是比较少见的,特别是动态代理基本上是没有见过。但是代理模式也是我们必须要理解的一种模式,因为学习好代理模式有助于我们去读一些源码,排查一些更深层次的问题,或者面对一些业务场景问题,也能有一个很大的提升,设计模式本身也就是为了解决问题而创建出来的。

理解完动态代理现在对我们来说AOP的实现原理也就不言而喻了。

详细的设计模式到这里就结束了,后面针对一些不常见设计模式我还是会给大家做一个总结吧。

我是敖丙,你知道的越多,你不知道的越多,我们下期见!!!


敖丙把自己的面试文章整理成了一本电子书,共 1630页!

干货满满,字字精髓。目录如下,还有我复习时总结的面试题以及简历模板,现在免费送给大家。

链接:https://pan.baidu.com/s/1ZQEKJBgtYle3v-1LimcSwg 密码:wjk6

我是敖丙,你知道的越多,你不知道的越多,感谢各位人才的:点赞收藏评论,我们下期见!


文章持续更新,可以微信搜一搜「 三太子敖丙 」第一时间阅读,回复【资料】有我准备的一线大厂面试资料和简历模板,本文 GitHub https://github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。

《设计模式系列》- 代理模式相关推荐

  1. Python设计模式-代理模式

    Python设计模式-代理模式 基于Python3.5.2,代码如下 #coding:utf-8info_struct = dict() info_struct["addr"] = ...

  2. Java设计模式(代理模式-模板方法模式-命令模式)

    Java设计模式Ⅴ 1.代理模式 1.1 代理模式概述 1.2 静态代理 1.2.1 静态代理概述 1.2.2 代码理解 1.3 动态代理之JDK代理 1.3.1 动态代理之JDK代理概述 1.3.2 ...

  3. 设计模式——代理模式

    设计模式--代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能 ...

  4. 23种设计模式----------代理模式(一)

    代理模式也叫委托模式. 代理模式定义:对其他对象提供一种代理从而控制对这个对象的访问.就是,代理类 代理 被代理类,来执行被代理类里的方法. 一般情况下,代理模式化有三个角色. 1,抽象的主题类(或者 ...

  5. java设计模式代理模式_Java中的代理设计模式

    java设计模式代理模式 代理对象或代理对象为另一个对象提供占位符,以控制对该对象的访问. 代理充当原始对象的轻量级版本或简化版本. 它支持与原始对象相同的操作,但可以将那些请求委托给原始对象以实现它 ...

  6. 第四章 Caché 设计模式 代理模式

    文章目录 第四章 Caché 设计模式 代理模式 定义 类型 使用场景 优点 缺点 结构图 完整示例 抽象主题类 真实主题类 代理类 对象类 调用 思考 第四章 Caché 设计模式 代理模式 定义 ...

  7. Android常见设计模式——代理模式(Proxy Pattern)(二)

    文章目录 1. 前言 2. 远程代理(Remote Proxy) 3. 后记 1. 前言 在上篇Android常见设计模式--代理模式(Proxy Pattern)中基本上知道了什么是代理模式,以及对 ...

  8. sheng的学习笔记-设计模式-代理模式

    原理图: 代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问.这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介. 至少在以下集中情况下可以用 ...

  9. 设计模式-代理模式(Proxy Pattern)

    设计模式-代理模式(Proxy Pattern) 文章目录 设计模式-代理模式(Proxy Pattern) 一.定义 二.概念解释 三.场景 四.实现 1.类图 2.代码实现 五.小结 六.动态代理 ...

  10. 简说设计模式——代理模式

    一.什么是代理模式 关于代理模式,我们听到的见到的最多的可能就是静态代理.动态代理之类的,当然还有大家都知道的Spring Aop,这里我们先不谈这些个代理,先说个简单的例子.游戏代练应该都听说过,许 ...

最新文章

  1. h5 和native 交互那些事儿
  2. Opengl-基本章节的学习成果
  3. asp.net 中文编码问题
  4. 利用koa实现mongodb数据库的增删改查
  5. SQLErrorCodeSQLExceptionTranslator
  6. 美国Thinkfun的“编程三剑客”少儿编程从入门到精通
  7. python10086查询系统_Python获取移动性能指标
  8. C++工作笔记-作用域的巧妙使用,释放堆区创建的资源
  9. “男友家里存款只有20万,我该和他结婚么?”数据告诉你多少家庭才能有20万的存款...
  10. 【DB2】NULLS LAST与NULLS FIRST
  11. ZigBee On Windows Mobile-ZigBee模块的设计制作
  12. 基于DEAP库的python进化算法--遗传算法实践--背包问题
  13. linux设置ipsan_linux 配置IPSAN存储
  14. 流媒体播放器EasyPlayer.js如何实现动态设置解码H.265音频?
  15. 200+ML知识速查卡;『图解算法数据结构』配套代码;『剑指Offer』解题代码;『Python3』高频面试题目集;前沿论文 | ShowMeAI资讯日报
  16. MySQL讲义第50讲——select 查询之查询练习(八):查询每门课程成绩前三名的学生信息
  17. 三星android pie更新,三星Android Pie更新路线图公布 Galaxy Note9需等明年二月
  18. 华为nova3 计算机,华为nova 3评测:是迷妹选择,还是实力圈粉?
  19. Debezium实战-第7章 其它-Debezium UI
  20. MySql命令行基本语法

热门文章

  1. 太极阳必须要root吗_root手机教程
  2. 织梦服务器有什么文件,对于织梦CMS各目录内文件的说明详解
  3. 爬猫眼电影top100
  4. JSON Schema定义 +Networknt validator格式校验
  5. FPGA视觉从入门到放弃——懒人的支持向量机
  6. 【算法题目】【DFS】岛屿数量 岛屿中面积最大值
  7. Linux下的超级终端(minicom)
  8. IBMT60笔记本指纹识别设置
  9. 物联网——下一块大蛋糕
  10. 2009 国庆 十一 放假 安排