设计模式——代理模式(Proxy Pattern)

概述

       代理模式是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在访问对象和目标对象之间起到中介作用。代理对象也可以在不修改目标对象的前提下,提供额外的功能操作,拓展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。

       Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。

       代理(Proxy)模式分为三种角色:

    抽象角色(Subject): 通过接口或抽象类声明真实角色和代理对象实现的业务方法。

    真实角色(Real Subject): 实现了抽象角色中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。

    代理角色(Proxy) : 提供了与真实角色相同的接口,其内部含有对真实角色的引用,它可以访问、控制或扩展真实角色的功能。

代理模式结构图如下所示:

静态代理

       静态代理就是指我们在给一个类扩展功能的时候,我们需要去书写一个静态的类,相当于在之前的类上套了一层,这样我们就可以在不改变之前的类的前提下去对原有功能进行扩展,静态代理需要代理对象和目标对象实现一样的接口。实现步骤如下:

  1. 定义一个接口及其实现类;
  2. 创建一个代理类同样实现这个接口
  3. 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。

这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。

抽象主题类声明了真实主题类和代理类的公共方法,它可以是接口、抽象类或具体类,客户端针对抽象主题类编程,一致性地对待真实主题和代理主题,典型的抽象主题类代码如下:

真实主题类继承了抽象主题类,提供了业务方法的具体实现,其典型代码如下:

代理类也是抽象主题类的子类,它维持一个对真实主题对象的引用,调用在真实主题中实现的业务方法,在调用时可以在原有业务方法的基础上附加一些新的方法来对功能进行扩充或约束,最简单的代理类实现代码如下:

可以从上面代码看到,我们访问的是ProxyStation对象,也就是说ProxyStation是作为访问对象和目标对象的中介的,同时也对saleTickets方法进行了增强与扩展(代理点收取加收5%手续费)。

       静态代理的优点是实现简单,容易理解,只要确保目标对象和代理对象实现共同的接口或继承相同的父类就可以在不修改目标对象的前提下进行扩展。

而缺点也比较明显,那就是代理类和目标类必须有共同接口(父类),并且需要为每一个目标类维护一个代理类,当需要代理的类很多时会创建出大量代理类。一旦接口或父类的方法有变动,目标对象和代理对象都需要作出调整。

动态代理

       代理类在代码运行时创建的代理称之为动态代理,相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类(CGLIB 动态代理机制)。从 JVM 角度来说,动态代理中代理类并不是预先在Java代码中定义好的,而是运行时由JVM动态生成,并且可以代理多个目标对象。动态代理是在 运行时 动态生成类字节码,并加载到 JVM 中的。说到动态代理,Spring AOP、RPC 框架应该是两个不得不提的,它们的实现都依赖了动态代理。动态代理在我们日常开发中使用的相对较少,但是在框架中的几乎是必用的一门技术。学会了动态代理之后,对于我们理解和学习各种框架的原理也非常有帮助。就 Java 来说,动态代理的实现方式有很多种,比如 JDK 动态代理、CGLIB 动态代理 等等。

JDK动态代理

JDK动态代理是Java JDK自带的一个动态代理实现, 位于java.lang.reflect包下。在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。Proxy类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象。

这个方法一共有 3 个参数:

  1. loader :类加载器,用于加载代理对象。
  2. interfaces : 被代理类实现的一些接口;
  3. h : 实现了 InvocationHandler 接口的对象;

要实现动态代理的话,还必须需要实现InvocationHandler 来自定义处理逻辑。
当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。

invoke() 方法有下面三个参数:

  1. proxy :动态生成的代理类
  2. method : 与代理类对象调用的方法相对应
  3. args : 当前 method 方法的参数

JDK动态代理使用步骤

1、定义一个接口及其实现类;

2、自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;

3、通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;

接口及实现类同静态代码,主要看下代理类:

优点:

  • 使用简单、维护成本低。
  • Java原生支持,不需要任何依赖。
  • 解决了静态代理存在的多数问题。

缺点:

  • 由于使用反射,性能会比较差。
  • 只支持接口实现,不支持继承, 不满足所有业务场景。
CGLIB动态代理

介绍JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。 为了解决这个问题,我们可以用 CGLIB动态代理机制来避免。CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。

CGLIB通过继承方式实现代理。很多知名的开源框架都使用到了CGLIB, 例如 Spring 中的 AOP模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

在 CGLIB 动态代理机制中MethodInterceptor 接口和 Enhancer 类是核心。你需要自定义 MethodInterceptor 并重写intercept 方法,intercept 用于拦截增强被代理类的方法。

    obj : 被代理的对象(需要增强的对象)

    method : 被拦截的方法(需要增强的方法)

    args : 方法入参

    proxy :用于调用原始方法

你可以通过 Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的intercept 方法。

CGLIB动态代理使用步骤:

  1. 定义一个类;
  2. 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
  3. 通过 Enhancer 类的 create()创建代理类。

不同于 JDK 动态代理不需要额外的依赖。CGLIB(Code Generation Library) 实际是属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖。

<dependency>

  <groupId>cglib</groupId>

  <artifactId>cglib</artifactId>

  <version>3.3.0</version>

</dependency>

应用场景:

    保护目标对象。

    增强目标对象。

优点:

    代理模式能将代理对象与真实被调用的目标对象分离。

    一定程度上降低了系统的耦合程度,易于扩展。

    代理可以起到保护目标对象的作用。

    增强目标对象的职责。

缺点:

    代理模式会造成系统设计中类的数目增加。

    在客户端和目标对象之间增加了一个代理对象,请求处理速度变慢。

    增加了系统的复杂度。

JDK 动态代理和 CGLIB 动态代理对比

JDK动态代理的特点:

    需要实现InvocationHandler接口, 并重写invoke方法。

    被代理类需要实现接口, 它不支持继承。

    JDK 动态代理类不需要事先定义好, 而是在运行期间动态生成。

    JDK 动态代理不需要实现和被代理类一样的接口, 所以可以绑定多个被代理类。

    主要实现原理为反射, 它通过反射在运行期间动态生成代理类, 并且通过反射调用被代理类的实际业务方法。

cglib的特点:

    cglib动态代理中使用的是FastClass机制。

    cglib生成字节码的底层原理是使用ASM字节码框架。

    cglib动态代理需创建3份字节码,所以在第一次使用时会比较耗性能,但是后续使用较JDK动态代理方式更高效,适合单例bean场景。

    cglib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

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

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

相关文章

【Docker】进阶之路:(十二)Docker Composer

【Docker】进阶之路&#xff1a;&#xff08;十二&#xff09;Docker Composer Docker Compose 简介安装 Docker Compose模板文件语法docker-compose.yml 语法说明imagecommandlinksexternal_linksportsexposevolumesvolunes_fromenvironmentenv_fileextendsnetpiddnscap_add,c…

7+PPI+机器学习+实验,非肿瘤结合建模筛选生物标志物,可升级

今天给同学们分享一篇生信文章“Identification of diagnostic biomarkers and therapeutic targets in peripheral immune landscape from coronary artery disease”&#xff0c;这篇文章发表在J Transl Med期刊上&#xff0c;影响因子为7.4。 结果解读&#xff1a; 外周血中…

构建外卖小程序:技术代码实践

在这个数字化的时代&#xff0c;外卖小程序已经成为餐饮业的一项重要工具。在本文中&#xff0c;我们将通过一些简单而实用的技术代码&#xff0c;向您展示如何构建一个基本的外卖小程序。我们将使用微信小程序平台作为例子&#xff0c;但这些原理同样适用于其他小程序平台。 …

Android其他组件(单选框)

一、单选框&#xff08;RadioGroup&#xff09; 单选框&#xff08;RadioGroup&#xff09;需要配合单选按钮&#xff08;RadioButton&#xff09;使用&#xff0c;同一个单选框中的单选按钮只能被选中一个&#xff0c;默认是一个都不选中。 RadioGroup的常见属性&#xff08…

盈科数智视频管理平台简介

平台介绍 盈科数智视频管理平台是多种视频终端联网的标准管理平台&#xff0c;具有接入能力强、基础功能完善、运行实时监测、视频稳定传输、不同厂家的设备接入等能力&#xff0c;为智慧社区、智慧城市、安防监控提供视联网接入场景。 主要功能介绍 支持GB/T 28181,支持国标…

c#面试基础语法——as和is的区别

as as关键字用于将一个对象转换为指定类型&#xff08;或其派生类型&#xff09;。如果转换成功&#xff0c;则返回转换后的对象&#xff1b;如果对象不能转换为指定类型&#xff0c;则返回null。当使用as进行类型转换时&#xff0c;如果目标类型与原始对象类型不兼容&#xff…

华为配置本地端口镜像示例(1:1)

图1 配置本地端口镜像组网图 组网需求 如图1所示&#xff0c;某公司行政部通过Switch与外部Internet通信&#xff0c;监控设备Server与Switch直连。 现在希望通过Server对行政部访问Internet的流量进行监控 配置思路 在Switch进行如下配置&#xff0c;实现Server对所有行政…

VS的python没有pandas(VS连接mysql数据库)

import pandas as pd from sqlalchemy import create_engine# 初始化数据库连接 engine create_engine(mysqlpymysql://root:556localhost:3306/仓库)sql_chaSELECT * FROM 库房 print(sql_cha) df_read pd.read_sql_query(sql_cha, engine); print(df_read);VS连接mysql如上…

【通俗易懂】基于fabric8io操作k8s集群实战(pod、deployment、service、volume)

目录 前言一、基于fabric8io操作pod1.1 yaml创建pod1.2 fabric8io创建pod案例 二、基于fabric8io创建Service&#xff08;含Deployment&#xff09;2.1 yaml创建Service和Deployment2.2 fabric8io创建service案例 三、基于fabric8io操作Volume3.1 yaml配置挂载存储卷3.2 基于fa…

微信小程序封装自定义导航栏

在 app.json 文件中设置导航栏的样式&#xff1a; "window": {"navigationStyle": "custom" }, "usingComponents": {"nav-bar": "/components/navbar/navbar"}在app.js中获取设备顶部窗口的高度 App({onLaunch…

AUTOSAR从入门到精通-面向服务的中间件SOME/IP(五)

目录 前言 几个高频面试题目 SOME/IP与SOA的关系 some/ip、http、ftp、dns、rpc区别

Java服务网关的实现与应用:Spring Cloud Gateway、Zuul和Kong比较

引言&#xff1a; 随着微服务架构的兴起&#xff0c;服务网关作为系统的入口和出口&#xff0c;扮演着至关重要的角色。它能够集中处理请求、路由和过滤&#xff0c;提供统一的访问接口&#xff0c;简化了系统的复杂性。在Java领域&#xff0c;有多种服务网关的实现方案可供选择…

【超图】SuperMap iClient3D for WebGL/WebGPU ——暴雪

作者&#xff1a;taco 时隔多年北京又开始降下了特大暴雪。身为打工人的你有没有居家办公呢&#xff1f;反正小编我是没有。既然没有借着暴雪的功劳居家办公&#xff0c;那就接着雪来输出一篇博客好了。基于SuperMap iClient3D for WebGL/WebGPU 实现暴雪仿真效果。 先来看下效…

threejs ShapeGeometry 自定义贴图的uv坐标

问题描述&#xff1a; 由于一些原因&#xff0c;要绘制一个长方形&#xff0c;但是这个长方形并不是 PlaneGeometry&#xff0c;而是一个ShapeGeometry。但是同样的贴图&#xff0c;同样的形状&#xff0c;贴图贴在PlaneGeometry上时可以正常显示&#xff0c;但是贴在ShapeGeo…

了解如何在linux使用podman管理容器

本章主要介绍使用 podman 管理容器。 了解什么是容器&#xff0c;容器和镜像的关系 安装和配置podman 拉取和删除镜像 给镜像打标签 导出和导入镜像 创建和删除镜像 数据卷的使用 管理容器的命令 使用普通用户管理容器 使用普通用户管理容器 对于初学者来说&#xff0c;不太容…

squid SSL https

在 Squid 服务中配置 SSL Bumping 建议在 Squid 服务中配置 SSL Bumping 以处理加密连接。如果未配置 SSL Bumping&#xff0c;则代理服务器无法干预加密连接建立过程。在这种情况下&#xff0c;Kaspersky Web Traffic Security&#xff08;反病毒和反网络钓鱼&#xff09;的保…

pytorch:to()、device()、cuda()将Tensor或模型移动到指定的设备上

将Tensor或模型移动到指定的设备上&#xff1a;tensor.to(‘cuda:0’) 最开始读取数据时的tensor变量copy一份到device所指定的GPU上去&#xff0c;之后的运算都在GPU上进行在做高维特征运算的时候&#xff0c;采用GPU无疑是比用CPU效率更高&#xff0c;如果两个数据中一个加了…

word四级目录序号不随上级目录序号变化问题解决方法

一、word中的几个元素简介 1、word中的列表 如下图所示&#xff0c;代表word的列表&#xff1a; 2、word中的标题 如下图所示&#xff0c;代表word的标题&#xff1a; 3、word中的编号/序号 如下图所示&#xff0c;代表word的编号/序号&#xff1a; 4、word中的目录 如下图…

Stable diffusion 简介

Stable diffusion 是 CompVis、Stability AI、LAION、Runway 等公司研发的一个文生图模型&#xff0c;将 AI 图像生成提高到了全新高度&#xff0c;其效果和影响不亚于 Open AI 发布 ChatGPT。Stable diffusion 没有单独发布论文&#xff0c;而是基于 CVPR 2022 Oral —— 潜扩…

面试经典150题(20)

leetcode 150道题 计划花两个月时候刷完&#xff0c;今天&#xff08;第八天&#xff09;完成了1道(20)150&#xff1a; 这个题花了我快 2个小时。。。 20&#xff1a;&#xff08;6. N 字形变换&#xff09;题目描述&#xff1a; 将一个给定字符串 s 根据给定的行数 numRow…