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

代理模式:控制访问的设计模式

代理模式:控制访问的设计模式

什么是代理模式?

代理模式是一种常见的设计模式,它允许通过代理对象来控制对真实对象的访问。代理模式的主要目的是在不改变原始对象的情况下,提供额外的功能或控制访问。

为什么要使用代理模式?

代理模式有以下几个主要的应用场景:

  • 访问控制:代理模式可以限制对真实对象的直接访问,只有通过代理对象才能访问真实对象。这样可以实现对真实对象的访问控制,例如权限验证、身份验证等。
  • 增加额外功能:代理模式可以在不修改真实对象的情况下,为其增加额外的功能。代理对象可以在调用真实对象的方法前后执行一些额外的操作,例如日志记录、性能监控、缓存等。
  • 远程访问:代理模式可以实现远程访问,即通过代理对象访问位于不同地址空间的真实对象。这对于分布式系统或跨网络的应用程序非常有用。

代理模式的两种分类

静态代理

静态代理是在编译时就已经确定代理对象和真实对象的关系。代理对象和真实对象实现相同的接口或继承相同的父类,代理对象持有真实对象的引用,并在调用真实对象的方法前后执行一些额外的操作。

优点: 简单易懂

缺点: 需要为每个真实对象编写一个代理类,当真实对象较多时,会导致代码冗余

案例: 一个简单的日志记录功能

假设我们有一个 UserService 接口和一个实现类 UserServiceImpl,它提供了用户管理的一些基本操作方法,如添加用户、删除用户等。现在我们需要在每个方法执行前后记录日志,例如:在方法执行前,打印 “Before” 的日志;在方法执行后,打印 “After” 的日志。

public class UserServiceProxy implements UserService {private UserServiceImpl userService;public UserServiceProxy(UserServiceImpl userService) {this.userService = userService;}@Overridepublic void addUser(User user) {System.out.println("Before adding user");userService.addUser(user);System.out.println("After adding user");}@Overridepublic void deleteUser(int userId) {System.out.println("Before deleting user");userService.deleteUser(userId);System.out.println("After deleting user");}// 其他方法同样的方式实现
}

接下来,我们可以使用代理类来代替真实对象进行操作。

public class Main {public static void main(String[] args) {UserService userService = new UserServiceImpl();UserServiceProxy proxy = new UserServiceProxy(userService);User user = new User("John");proxy.addUser(user);proxy.deleteUser(1);}
}

动态代理

动态代理是一种在运行时动态生成代理类的代理模式。它可以在不修改原始类的情况下,为原始类提供额外的功能或控制访问。在Java中,有两种常见的动态代理方式:JDK动态代理和CGLIB动态代理

JDK动态代理

JDK动态代理是通过Java的反射机制实现的。它要求被代理的类必须实现一个接口。JDK动态代理提供了一个Proxy类和一个InvocationHandler接口,通过这两个类可以动态生成代理类。

案例: 简单的日志记录功能

定义一个接口 UserService,它提供了用户管理的一些基本操作方法。

public interface UserService {void addUser();void deleteUser();
}

真实的用户服务类UserServiceImpl,它实现了 UserService 接口。

public class UserServiceImpl implements UserService {@Overridepublic void addUser() {System.out.println("Adding user...");}@Overridepublic void deleteUser() {System.out.println("Deleting user with ID...");}
}

创建一个实现 InvocationHandler 接口的代理处理器类 LogInvocationHandler,它负责在方法执行前后添加日志记录的功能。

public class LogInvocationHandler implements InvocationHandler {private Object target;public LogInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before " + method.getName());Object result = method.invoke(target, args);System.out.println("After " + method.getName());return result;}
}

最后,我们可以使用 Proxy 类的 newProxyInstance 方法来创建代理对象。

public class Main {public static void main(String[] args) {UserServiceImpl userService = new UserServiceImpl();LogInvocationHandler handler = new LogInvocationHandler(userService);UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),handler);proxy.addUser();proxy.deleteUser();}
}

CGLIB动态代理

CGLIB动态代理是通过继承被代理类来实现的,它不要求被代理的类实现接口。CGLIB动态代理使用了字节码生成库来生成代理类。

案例:简单的日志记录功能

定义一个类 UserService,它提供了用户管理的一些基本操作方法。

public class UserService {public void addUser() {System.out.println("Adding user...");}public void deleteUser() {System.out.println("Deleting user with ID... ");}
}

一个代理类 LogProxy,它继承了被代理类 UserService

public class LogProxy extends UserService implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before " + method.getName());Object result = proxy.invokeSuper(obj, args);System.out.println("After " + method.getName());return result;}
}

最后,我们可以使用 Enhancer 类来创建代理对象。

public class Main {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class);enhancer.setCallback(new LogProxy());UserService proxy = (UserService) enhancer.create();proxy.addUser();proxy.deleteUser();}
}
jdk动态代理和cglib动态代理的区别
  1. 实现方式:jdk动态代理是通过反射实现的,而cglib动态代理是通过继承目标类来实现的。
  2. 目标类限制:jdk动态代理要求目标类必须要实现接口,而cglib动态代理则没有这个限制。
  3. 性能:jdk动态代理相对于cglib动态代理来说,因为实现方式不同,生成的代理类的效率会低一些
  4. 对象类型:jdk动态代理只能代理实现了接口的类,cglib通过继承实现,不能代理 final 类
  5. 依赖库:jdk动态代理是Java自带的库,不需要额外的依赖,而cglib动态代理需要依赖cglib库

总结

代理模式是一种非常有用的设计模式,它可以实现访问控制、增加额外功能和远程访问。静态代理在编译时确定代理对象和真实对象的关系,而动态代理在运行时动态生成代理对象。动态代理又分为jdk动态代理和cglib动态代理,分别基于接口和类来实现代理功能。根据具体的需求和场景,选择适合的代理模式可以提高代码的可维护性和灵活性。
区别:

  • 与适配器模式的区别适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
  • 与装饰器模式的区别装饰器模式为了增强功能,而代理模式是为了加以控制。

相关文章:

代理模式:控制访问的设计模式

代理模式:控制访问的设计模式 什么是代理模式? 代理模式是一种常见的设计模式,它允许通过代理对象来控制对真实对象的访问。代理模式的主要目的是在不改变原始对象的情况下,提供额外的功能或控制访问。 为什么要使用代理模式&a…...

2020/7/30

Educational Codeforces Round 143 (Rated for Div. 2)\C_Tea_Tasting.cpp //题意:有n种茶,n个人,第i种茶有 a[i]的量,第i个人一次能喝 b[i], 第i个人从第i种茶开始往前喝,求每个人最多能喝多少茶。 //思路&#xff…...

图形编辑器开发:是否要像 Figma 一样上 wasm

大家好,我是前端西瓜哥。 wasm 拿来做 Web 端的图形编辑器貌似是不错的选择。 因为图形处理会有相当多无法利用到 WebGL GPU 加速的 CPU 密集的计算。比如对一条复杂贝塞尔曲线进行三角化,对多个图形进行复杂图形的布尔运算。 图形编辑器性能天花板 F…...

Linux学成之路(基础篇0(二十三)MySQL服务(主从MySQL服务和读写分离——补充)

目录 一、MySQL Replication概述 优点 异步复制(Asynchronous repication) 全同步复制(Fully synchronous replication) 半同步复制(Semisynchronous replication) 三、MySQL支持的复制 四、部署主从…...

spring启动流程 (6完结) springmvc启动流程

SpringMVC的启动入口在SpringServletContainerInitializer类,它是ServletContainerInitializer实现类(Servlet3.0新特性)。在实现方法中使用WebApplicationInitializer创建ApplicationContext、创建注册DispatcherServlet、初始化ApplicationContext等。 SpringMVC…...

设计模式-中介者模式在Java中使用示例-客户信息管理

场景 欲开发客户信息管理窗口界面,界面组件之间存在较为复杂的交互关系:如果删除一个客户, 要在客户列表(List)中删掉对应的项,客户选择组合框(ComboBox)中客户名称也将减少一个; 如果增加一个客户信息,…...

14443-1-doc

介绍 ISO/IEC 14443 是描述 ISO/IEC 7810 中定义的身份证参数以及此类卡在国际交换中的使用的一系列国际标准之一。 ISO/IEC 14443 的这一部分描述了感应卡的物理特性。 ISO/IEC 14443 的这一部分并不排除在卡上纳入其他标准技术,例如资料性附录 A 中引用的技术。非…...

SpringBoot的三层架构以及IOCDI

目录 一、IOC&DI入门 二、三层架构 数据库访问层 业务逻辑层 控制层 一、IOC&DI入门 在软件开发中,IOC(Inversion of Control)和DI(Dependency Injection)是密切相关的概念。 IOC(控制反转&a…...

RabbitMQ部署指南

RabbitMQ部署指南 1.单机部署 我们在Centos7虚拟机中使用Docker来安装。 1.1.下载镜像 方式一:在线拉取 docker pull rabbitmq:3-management方式二:从本地加载 已经提供了镜像包: 上传到虚拟机中后,使用命令加载镜像即可&…...

【Golang】Golang进阶系列教程--Go 语言切片是如何扩容的?

文章目录 前言声明和初始化扩容时机源码分析go1.17go1.18内存对齐 总结 前言 在 Go 语言中,有一个很常用的数据结构,那就是切片(Slice)。 切片是一个拥有相同类型元素的可变长度的序列,它是基于数组类型做的一层封装…...

【数据结构】顺序表(SeqList)(增、删、查、改)详解

一、顺序表的概念和结构 1、顺序表的概念: 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。 2、顺序表的结构: (1)静态顺序表:使…...

[golang gin框架] 42.Gin商城项目-微服务实战之后台Rbac微服务角色增删改查微服务

一.重构后台Rbac用户登录微服务功能 上一节讲解了后台Rbac微服务用户登录功能以及Gorm数据库配置单独抽离,Consul配置单独抽离,这一节讲解后台Rbac微服务角色增删改查微服务功能,Rbac微服务角色增删改查微服务和后台Rbac用户登录微服务是属于…...

项目篇:Echo论坛系统项目

一、登录注册模块 1、注册功能 1.1、注册流程图 1.2、注册代码 /*** 用户注册* param user* return Map<String, Object> 返回错误提示消息&#xff0c;如果返回的 map 为空&#xff0c;则说明注册成功*/public Map<String, Object> register(User user) {Map&l…...

数据可视化(2)

1.柱状图 #柱状图 #bar(x,height,width,*,aligncenter,**kwargs) #height柱子的高度&#xff0c;即y轴上的数据 #width数组的宽度&#xff0c;默认值0.8 #*表示后面的参数为匿名关键字&#xff0c;必须传入参数 #kwargs关键字参数x[1,2,3,4,5] height[random.randint(10,100)f…...

MD-MTSP:斑马优化算法ZOA求解多仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、斑马优化算法ZOA 斑马优化算法&#xff08;Zebra Optimization Algorithm&#xff0c;ZOA&#xff09;Eva Trojovsk等人于2022年提出&#xff0c;其模拟斑马的觅食和对捕食者攻击的防御行为。斑马优化算法&#xff08;Zebra Optimization Algorithm&#xff0c;ZOA&#x…...

【笔试强训选择题】Day32.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01; 文章目录 前言 一、Da…...

抖音seo账号矩阵系统源码如何开发布局?

目录 一、 抖音SEO账号矩阵系统源码的开发布局步骤如下&#xff1a; 二。 开发部署源码 三、 开发部署功能设计 1. 短视频AI智能创作 2. 托管式账号管理: 3. 数据分析 4. 智能营销获客 四。 抖音seo源码开发部署交付技术文档包含 五。 开发代码展示&#xff1a; 一、 抖…...

vue项目cdn打包优化

0.用vue ui可以查看项目打包后的情况。 1.定义包的排除 let externals {axios: axios,element-ui: ELEMENT,echarts: echarts,} configureWebpack: {externals: externals }2.配置cdn包资源 // 配置 let cdn {css: [// element-ui csshttps://unpkg.com/element-ui/lib/th…...

Android 之 MediaPlayer 播放音频与视频

本节引言&#xff1a; 本节带来的是Android多媒体中的——MediaPlayer&#xff0c;我们可以通过这个API来播放音频和视频 该类是Androd多媒体框架中的一个重要组件&#xff0c;通过该类&#xff0c;我们可以以最小的步骤来获取&#xff0c;解码 和播放音视频。它支持三种不同的…...

React中事件处理器的基本使用

在React中&#xff0c;为了提高性能&#xff0c;跨浏览器兼容性和开发体验&#xff0c;React实现了一套自己的事件机制&#xff0c;利用事件委托和合成事件的方式统一管理事件订阅和分发。 为了让组件能够响应用户的交互行为&#xff0c;React提供了一系列的事件处理器&#xf…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙

Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...