JAVA设计模式-代理模式

一.概念

        在软件开发中,也有一种设计模式可以提供与代购网站类似的功能。由于某些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称之为“代理”的第三者来实现间接访问,该方案对应的设计模式被称为代理模式。 ​ 代理模式是一种应用很广泛的结构型设计模式,而且变化形式非常多,常见的代理形式包括远程代理、保护代理、虚拟代理、缓冲代理、智能引用代理等,后面将学习这些不同的代理形式。 ​ 给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。 ​ 代理模式是一种对象结构型模式。在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。

二.代理模式的作用

1.中介隔离作用

        在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

2.开闭原则,增加功能

        代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

三.代理模式的角色

1.Subject(抽象主题角色)

         它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。

2.Proxy(代理主题角色)

        它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。

3.RealSubject(真实主题角色)

        它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

四.代理模式的优缺点

1.优点

职责清晰。 高扩展性。 智能化。

2.缺点

(1)由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。

(2)实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

五.使用场景

远程代理。

虚拟代理。

Copy-on-Write 代理。

保护(Protect or Access)代理。

Cache代理。

防火墙(Firewall)代理。

同步化(Synchronization)代理。

智能引用(Smart Reference)代理。

六.注意事项

1.和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。

2.和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

七.静态代理

        1.概念

        其实代理的一般模式就是静态代理的实现模式:首先创建一个接口(JDK代理都是面向接口的),然后创建具体实现类来实现这个接口,在创建一个代理类同样实现这个接口,不同之处在于,具体实现类的方法中需要将接口中定义的方法的业务逻辑功能实现,而代理类中的方法只要调用具体类中的对应方法即可,这样我们在需要使用接口中的某个方法的功能时直接调用代理类的方法即可,将具体的实现类隐藏在底层。

        2.实现

公用接口 - 代理类和真实类的公用接口

public interface Subject {void request();
}

真实类的请求 - 实现公有的接口

public class RealSubject implements Subject{@Overridepublic void request() {System.out.println("真实请求");}
}

创建代理类 - 代理请求,引入了真实类对象,对方法进行了增强

public class Proxy implements Subject{//真实请求接口private Subject realSubject;@Overridepublic void request() {if (realSubject == null) {realSubject = new RealSubject();}System.out.println("前置增强");//转发真实请求realSubject.request();System.out.println("后置增强");}
}

测试

public class ProxyDemo {public static void main(String[] args){Proxy proxy = new Proxy();proxy.request();}
}
3.静态代理总结

优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

八.JDK动态代理

1.概念

        在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。

2.编写动态处理器

动态代理需要实现InvocationHandler接口

/*** 动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。* 该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类*/
public class DynamicProxyHandler implements InvocationHandler {// 目标对象private Object targetObject;/*** 绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。* @param targetObject* @return*/public Object newProxyInstance(Object targetObject){this.targetObject=targetObject;//该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例//第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器//第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口//第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法//根据传入的目标返回一个代理对象return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);}/*** 关联的这个实现类的方法被调用时将被执行* InvocationHandler接口的方法* @param proxy 代理对象* @param method 原对象被调用的方法* @param args 表示方法的参数** @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("前置增强");//调用目标方法Object result = method.invoke(targetObject, args);System.out.println("后置增强");return result;}
}
3.测试
public class DynamicProxyDemo {public static void main(String[] args){DynamicProxyHandler dynamicProxyHandler=new DynamicProxyHandler();UserManager userManager=(UserManager)dynamicProxyHandler.newProxyInstance(new UserManagerImpl());UserManager userManager=new UserManagerImpl();userManager.addUser("1111", "张三");}
}

4.动态代理总结

         虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。有很多条理由,人们可以否定对 class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘。如此种种,不得不说是一个小小的遗憾。但是,不完美并不等于不伟大,伟大是一种本质,Java动态代理就是佐例。

九.CGLIB动态代理

1.概念

        JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

2.创建CGLIB代理
public class CglibProxy implements MethodInterceptor {// 目标对象private Object targetObject;public Object getInstance(final Object target) {this.target = target;//CGLIB enhancer增强类对象Enhancer enhancer = new Enhancer();//设置代理类enhancer.setSuperclass(this.targetObject.getClass());//定义代理逻辑对象为当前对象   ,对象要继承MethodInterceptor。并实现逻辑方法interceptenhancer.setCallback(this);return enhancer.create();}/*** 代理逻辑方法* @param proxy 代理对象* @param method 方法* @param args 方法参数* @param methodProxy 方法代理* @return* @throws Throwable*/public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("前置增强");//真的是代理对象,不像JDk代理,还是真实对象Object result = methodProxy.invokeSuper(proxy, args);System.out.println("后置增强");return result;}
}

        这里用了CGLIB的加强者Enhancer,通过设置超类的方法setSuperclass,然后通过setCallback方法设置哪个类为它的代理类。其中,参数为this就意味着是当前对象,那就要求用this这个对象实现接口Methodlnterceptor的方法intercept,然后返回代理对象。那么此时当前类的intercept方法就是其代理逻辑方法,其参数内容见代码注解,我们在反射真实对象方法前后进行了打印,CGLIB是通过如下代码完成的。

Object result=methodProxy.invokeSuper(proxy,args);
3.测试
public class CglibProxyDemo {public static void main(String arg[]) {CglibProxy cglib = new CglibProxy();Dog dog = (Dog) cglib.getInstance(Dog.class);//调用代理对象的isAnimal()方法dog.isAnimal(); }
}
4.CGLIB代理总结

        CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

十.两者对比

1.使用对比

JDK动态代理是面向接口的。

CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败)。

2.性能对比

传言的 cglib 比 jdk动态代理高出 10 倍

我们经过测试并不是这样,或者说以前是这样,大家可以测试下,我把结果贴出来

JDKProxy创建耗时:1

JDKProxy调用耗时:2

CjLibProxy创建耗时:110

CjLibProxy调用耗时:21

创建耗时JDK比CGLIB快了将近100倍调用也比CGLIB快了将近十倍

所以使用代理的时候最好使用面向接口的JDK的动态代理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/92461.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

12、Kubernetes中KubeProxy实现之iptables和ipvs

目录 一、概述 二、iptables 代理模式 三、iptables案例分析 四、ipvs案例分析 一、概述 iptables和ipvs其实都是依赖的一个共同的Linux内核模块:Netfilter。Netfilter是Linux 2.4.x引入的一个子系统,它作为一个通用的、抽象的框架,提供…

【刷题笔记10.2】LeetCode: 罗马数字转整数

LeetCode: 罗马数字转整数 一、题目描述 二、分析 方法一: 将给定字符串s中的"IV", “IX”, “XL”, “XC”, “CD”, “CM” 全部替换为其他字符如:a, b, c, d, e, f 这种,然后就可以遍历累加了。 s s.replace("IV",…

华为云云耀云服务器L实例评测 | 实例评测使用之体验评测:华为云云耀云服务器管理、控制、访问评测

华为云云耀云服务器L实例评测 | 实例评测使用之体验评测:华为云云耀云服务器管理、控制、访问评测 介绍华为云云耀云服务器 华为云云耀云服务器 (目前已经全新升级为 华为云云耀云服务器L实例) 华为云云耀云服务器是什么华为云云耀…

Redis 如何实现数据不丢失的?

Redis 实现数据不丢失的关键在于使用了多种持久化机制,以确保数据在内存和磁盘之间的持久性。以下是 Redis 实现数据不丢失的主要方法: 快照(Snapshot)持久化: Redis 使用快照持久化来定期将内存中的数据写入磁盘。快照是一个数据库状态的副本,包含了所有键和与其相关联的…

Linux命令(91)之mv

linux命令之mv 1.mv介绍 linux命令mv是用来移动文件或目录,并且也可以用来更改文件或目录的名字 2.mv用法 mv [参数] src dest mv常用参数 参数说明-f强制移动,不提示 3.实例 3.1.重命名文件1.txt为ztj.txt 命令: mv 1.txt ztj.txt …

理解Go中的数据类型

引言 数据类型指定了编写程序时特定变量存储的值的类型。数据类型还决定了可以对数据执行哪些操作。 在本文中,我们将介绍Go的重要数据类型。这不是对数据类型的详尽研究,但将帮助您熟悉Go中可用的选项。理解一些基本的数据类型能让你写出更清晰、性能…

深度学习笔记_1、定义神经网络

1、使用了PyTorch的nn.Module类来定义神经网络模型;使用nn.Linear来创建全连接层。(CPU) import torch.nn as nn import torch.nn.functional as F from torchsummary import summary# 定义神经网络模型 class Net(nn.Module):def __init__(self):super(Net, self).__init__()…

小程序编译器性能优化之路

作者 | 马可 导读 小程序编译器是百度开发者工具中的编译构建模块,用来将小程序代码转换成运行时代码。旧版编译器由于业务发展,存在编译慢、内存占用高的问题,我们对编译器做了一次大规模的重构,采用自研架构,做了多线…

excel将文件夹下面的表格文件指定名称的sheet批量导出到指定文件中,并按照文件名保存在新文件的不同sheet中

excel将文件夹下面的表格文件指定名称的sheet批量导出到指定文件中,并按照文件名保存在新文件的不同sheet中 import pandas as pd import ositems os.listdir("./") sheetname"" for item in items:if item.__contains__(xls):dfpd.read_exc…

px4的gazebo仿真相机模型报错解决办法,返回值256

👉事情起因:我想做关于PX4无人机的摄像头仿真,根据PX4的官网文件 Tools/sitl_gazebo文件夹里面有对应的模型可以使用,我就想在mavros_posix_sitl文件里面修改vehicle参数,比如直接将vehicle“iris_stereo_camera”。然…

【前段基础入门之】=>CSS 中对颜色数值的四种表达方式!

导语: 在通过 CSS 设置元素样式的时候,对于颜色的定义,有以下四种表达方式。 文章目录 方式一:【颜色名】方式二:rgb 或 rgba方式三::HEX 或 HEXA方式四:HSL 或 HSLA 方式一&#xf…

系统集成项目管理总结(笔记)

系统集成项目管理总结 基础知识 第一章 信息化知识 第二章 信息系统服务管理 第三章 系统集成专业技术 第四章 项目管理一般知识 第五章 立项管理 第六章 整体管理 第七章 范围管理 第八章 进度管理 第九章 成本管理 第十章 质量管理 第十一章 人力资源管理 第十二…

【Nuxt】04 Nuxt2-SEO: sitemap.xml、seo优化、robots.txt

1 SiteMap设置 环境准备 注意生成sitemap依赖于nuxtjs/sitemap,并且需要用axios进行请求,不要使用nuxtjs/axios,不然会报错 sitemap.xml配置 在nuxt.config.js中配置下面的内容 npm install nuxtjs/sitemap npm install axios在static/s…

用于YOLO格式分割的咖啡叶病害数据集。

下载链接:https://download.csdn.net/download/qq_40840797/88389334 数据集,一共1164张照片 随机选取几张照片及对应的目标标签 因为健康,所以标签为空

c++设计模式:单例模式

单例模式 单例模式属于创建类型的一种常用的软件设计模式,通过单例模式的方法创建的类在当前进程中只有一个实例。 应用场景 配置管理 日志记录 线程池 连接池 内存池 对象池 消息队列 实现步骤 1.将类的构造方法定义为私有方法(为了只实例化一个单例&#xff…

github搜索技巧

指定语言 language:java 比如我要找用java写的含有blog的内容 搜索项目名称包含关键词的内容 vue in:name 其他如项目描述跟项目文档,如下 组合使用 vue in:name,description,readme 根据Star 或者fork的数量来查找 总结 springboot vue stars:>1000 p…

【网络安全】2023年堡垒机品牌大全

随着大家网络安全意识的增加,随着国家等保政策的严格执行,越来越多的企业开始采购堡垒机。这里就给大家总结了部分堡垒机品牌,让大家参考参考。 2023年堡垒机品牌大全 1、行云堡垒 2、JumpServer 3、安恒 4、骞云 5、齐治 6、阿里云 …

编码规范、git 规范

1、eslint配置,让代码变得更加规范,就是定义一些规则,开发人员要去遵守 该文件也是推荐在根目录下使用 2、prettier 就是格式化开发人员的代码 1、vscode 安装 prettier 插件 在项目根目录下创建一个 .prettierrc文件 然后去prettier 官网…

k8s部署单点的mysql实例

k8s部署单点的mysql实例 文章目录 前言一、基础环境准备二、准备yaml文件三、yaml文件中的特殊配置3.1 时区的配置3.2 管理员密码的配置3.3 lostfound配置3.4 探针配置 总结 前言 记录一下根据kubernetes.io的范本部署一个mysql实例的过程 一、基础环境准备 准备一个k8s集群…

前端开发与后端开发:探索编程世界的两个街区

一、引言 编程世界就像一座大城市,前端开发和后端开发就像城市的两个不同街区。在这个城市中,每个街区都有其独特的魅力和吸引力。作为初学者,我们站在这个城市的交叉口,不知道应该选择哪个街区。而作为过来者,你已经…