当前位置: 首页 > news >正文

代理模式详解

1.代理模式的作用

能通过代理对象间接实现对目标对象的访问,在不改变源代码的情况下对目标对象的方法进行增强。

什么是通过代理对象间接实现对目标对象的访问?

举个生活中的例子:例如你买车是通过4s店(代理对象),而不是直接去车工厂(目标对象),你只需访问4s店。

那什么不改变源代码的情况下对目标对象的方法进行增强?

还是4s店和车工厂的例子:

你去4s店买车,4s店除了卖车给你,它还有一系列的服务,例如:推荐保险,上车牌等等;

但是你去工厂就只能买车,没有其他的附加服务;也就是说4s店(代理对象)在车工厂(目标对象)的卖车基础上进行了一系列增强,但是车厂还是一样的卖车,并没有被改变。

2.代理模式的分类

代理模式分为静态代理和动态代理。

2.1静态代理

2.1.1静态代理的实现

实现步骤

1.代理类与目标类实现同一接口

2.代理对象有目标对象的引用,在同名方法中调用目标对象的方法,并在前后根据需求进行增强。

实现代码

接口代码

public interface AInterface {void say();
}

目标类代码

public class AInterfaceImpl implements AInterface{@Overridepublic void rap() {System.out.println("rap");}@Overridepublic void basketball() {System.out.println("篮球");}
}

代理类代码

public class AStaticProxy implements AInterface{//目标对象private AInterface aInterface;//注入目标对象public AStaticProxy(AInterface aInterface) {this.aInterface = aInterface;}@Overridepublic void rap() {System.out.println("前置增强");aInterface.rap();System.out.println("后置增强");}@Overridepublic void basketball() {System.out.println("前置增强");aInterface.basketball();System.out.println("后置增强");}
}

2.2.1静态代理的缺点

1.只能代理某一类接口

2.若进行多次增强,可能代理类暴增

3.修改接口,要同时维护目标类和代理类

4.若是代理类的增强逻辑一致,可能造成大量重复代码

5.增强代码和目标方法硬编码

2.2动态代理

针对静态代理的缺点,我们可能观察到,许多静态代理的缺点都来自于代理类;

如果我们将代理类干掉,就能解决静态代理的大部分缺点。

可是在静态代理中我们通过代理类的构造方法才能得到代理对象,若是没有代理类,怎么创建代理对象呢?

我们先观察一下到底静态代理是如何通过代理类得到代理对象的,这就涉及到java代码的运行原理了。

           编译                         类加载                             反射得到构造方法

代理类-------> 代理类.class---------->   JVM中的Class------------------------>创建得到代理对象

那么动态代理没有代理类,只能通过接口.class,很明显会在得到构造方法这里卡住,因为接口没有构造方法。

那么有没有办法能够既有接口的方法信息又有构造方法呢?

答案是:通过Proxy类的getProxyClass(classLoader,Interface)方法,我们只需传入将接口的信息作为参数传入,就能得到一个既有接口信息又有构造方法的Class对象,通过该Class得到构造方法,我们就能创建代理对象了。

那为什么Proxy类的getProxyClass方法那么神奇?

这里尚且不做深入了解,你可以理解为它就是 想办法把接口Class里面的方法信息拼接一个构造方法得到新的Class。

2.2.1动态代理的实现

实现步骤

1.编写一个工具类,里面提供一个静态方法getProxy(Object target,InvocationHandler i)

2.在getProxy()方法中,通过Proxy类的静态方法getProxyClass(classLoader,Interface),传入目标对象接口的参数,然后得到新的代理Class对象

3.通过代理Class对象调用getDeclaredConstructor(InvocationHandler.class)方法,传入Invocationhandler的Class对象得到构造方法对象

4.通过构造方法对象的newInstance(InvocationHandler)传入Invocationhandler对象创建得到代理对象

实现代码

接口代码

public interface AInterface {void say();
}

目标类代码

public class AInterfaceImpl implements AInterface{@Overridepublic void rap() {System.out.println("rap");}@Overridepublic void basketball() {System.out.println("篮球");}
}

代理工具类的代码

public class MyProxy {/*** 获取代理对象* @param target 目标对象* @param invocationHandler 代理处理器* @return* @throws Exception*/public static Object getProxy(Object target, InvocationHandler invocationHandler) throws Exception{//获得目标对象的Class对象Class clazz = target.getClass();//根据目标对象的Class对象得到代理对象的ClassClass proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces());//根据代理对象Class获得代理对象的构造器Constructor declaredConstructor = proxyClass.getDeclaredConstructor(InvocationHandler.class);//根据代理对象的构造器创建代理对象Object o = declaredConstructor.newInstance(invocationHandler);//返回得到的代理对象return o;}
}

得到的代理对象的结构是怎么样的?

代理对象大致结构:
public class MProxy implements AInterface{private InvocationHandler ih;public MProxy(InvocationHandler ih) {this.ih = ih;}@Overridepublic void rap() {ih.invoke();}@Overridepublic void basketball() {ih.invoke();}
}

2.2.2动态代理的缺点

只剩下增强代码和目标方法硬编码

静态代理的缺点有:

1.只能代理某一类接口

2.若进行多次增强,可能代理类暴增

3.修改接口,要同时维护目标类和代理类

4.若是代理类的增强逻辑一致,可能造成大量重复代码

5.增强代码和目标方法硬编码

动态代理如何优化的

对于静态代理的缺点1,动态代理只传入目标对象,目标对象可以是任意接口的,所以解决了

对于静态代理的缺点2、3、4,动态代理没有代理类,也解决了

但是对于静态代理的缺点5增强代码和目标方法硬编码,在动态代理的invoke方法

中还是没能避免,这个缺点在后来的aop(面向切面编程才得以解决),它将方法的逻辑进行拆分,无侵入式的对方法进行增强。

2.2.3动态代理的拓展

本文章的动态代理是JDK根据接口实现的,此外还有第三方的动态代理,如cglib,它实现的原理同JDK不同,它是基于继承来实现的,子类继承父类重写父类方法实现的增强

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 畅玩游戏新选择 :游戏本 Windows10 64位 专业版!
  • oracle备份和恢复exp/imp-----从全库备份中恢复用户库解题思路
  • ubantu22.04安装OceanBase 数据库
  • 数据结构2—顺序表(附源码)
  • react中的context就是vue中的provide/inject吗?
  • 全国区块链职业技能大赛第八套区块链产品需求分析与方案设计
  • Go操作Redis详解
  • 校验deb、rpm、apt、yum安装文件完整性测试
  • Web 性能入门指南-3.5 优化单页应用程序 (SPA)
  • Golang | Leetcode Golang题解之第242题有效的字母异位词
  • Linux下开放指定端口
  • 【人工智能】Transformers之Pipeline(四):零样本音频分类(zero-shot-audio-classification)
  • 双向长短期记忆网络(BiLSTM)及其Python和MATLAB实现
  • Six common classification algorithms in machine learning
  • 来自Transformers的双向编码器表示(BERT) 通俗解释
  • 03Go 类型总结
  • Angular2开发踩坑系列-生产环境编译
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • ES6之路之模块详解
  • Java精华积累:初学者都应该搞懂的问题
  • MySQL数据库运维之数据恢复
  • php的插入排序,通过双层for循环
  • Puppeteer:浏览器控制器
  • react-native 安卓真机环境搭建
  • Redis 中的布隆过滤器
  • scrapy学习之路4(itemloder的使用)
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 蓝海存储开关机注意事项总结
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 通信类
  • 我这样减少了26.5M Java内存!
  • 字符串匹配基础上
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • # Redis 入门到精通(七)-- redis 删除策略
  • (1)常见O(n^2)排序算法解析
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (2.2w字)前端单元测试之Jest详解篇
  • (3)nginx 配置(nginx.conf)
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (5)STL算法之复制
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (SERIES12)DM性能优化
  • (笔试题)合法字符串
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (十三)Flink SQL
  • (一)appium-desktop定位元素原理
  • (转)Android学习笔记 --- android任务栈和启动模式