代理模式:控制访问的设计模式
代理模式:控制访问的设计模式
什么是代理模式?
代理模式是一种常见的设计模式,它允许通过代理对象来控制对真实对象的访问。代理模式的主要目的是在不改变原始对象的情况下,提供额外的功能或控制访问。
为什么要使用代理模式?
代理模式有以下几个主要的应用场景:
访问控制
:代理模式可以限制对真实对象的直接访问,只有通过代理对象才能访问真实对象。这样可以实现对真实对象的访问控制,例如权限验证、身份验证等。增加额外功能
:代理模式可以在不修改真实对象的情况下,为其增加额外的功能。代理对象可以在调用真实对象的方法前后执行一些额外的操作,例如日志记录、性能监控、缓存等。远程访问
:代理模式可以实现远程访问,即通过代理对象访问位于不同地址空间的真实对象。这对于分布式系统或跨网络的应用程序非常有用。
代理模式的两种分类
静态代理
静态代理是在编译时就已经确定代理对象和真实对象的关系
。代理对象和真实对象实现相同的接口或继承相同的父类,代理对象持有真实对象的引用,并在调用真实对象的方法前后执行一些额外的操作。
优点: 简单易懂
缺点: 需要为每个真实对象编写一个代理类
,当真实对象较多时,会导致代码冗余
案例: 一个简单的日志记录功能
假设我们有一个 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动态代理的区别
- 实现方式:jdk动态代理是通过
反射
实现的,而cglib动态代理是通过继承目标类
来实现的。 - 目标类限制:jdk动态代理要求目标类
必须要实现接口
,而cglib动态代理则没有这个限制。 - 性能:jdk动态代理相对于cglib动态代理来说,因为实现方式不同,
生成的代理类的效率会低一些
。 - 对象类型:jdk动态代理
只能代理实现了接口的类
,cglib通过继承实现,不能代理 final 类
。 - 依赖库: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种茶开始往前喝,求每个人最多能喝多少茶。 //思路ÿ…...

图形编辑器开发:是否要像 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> 返回错误提示消息,如果返回的 map 为空,则说明注册成功*/public Map<String, Object> register(User user) {Map&l…...

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

MD-MTSP:斑马优化算法ZOA求解多仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)
一、斑马优化算法ZOA 斑马优化算法(Zebra Optimization Algorithm,ZOA)Eva Trojovsk等人于2022年提出,其模拟斑马的觅食和对捕食者攻击的防御行为。斑马优化算法(Zebra Optimization Algorithm,ZOA&#x…...

【笔试强训选择题】Day32.习题(错题)解析
作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训选择题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!! 文章目录 前言 一、Da…...

抖音seo账号矩阵系统源码如何开发布局?
目录 一、 抖音SEO账号矩阵系统源码的开发布局步骤如下: 二。 开发部署源码 三、 开发部署功能设计 1. 短视频AI智能创作 2. 托管式账号管理: 3. 数据分析 4. 智能营销获客 四。 抖音seo源码开发部署交付技术文档包含 五。 开发代码展示: 一、 抖…...
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 播放音频与视频
本节引言: 本节带来的是Android多媒体中的——MediaPlayer,我们可以通过这个API来播放音频和视频 该类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码 和播放音视频。它支持三种不同的…...
React中事件处理器的基本使用
在React中,为了提高性能,跨浏览器兼容性和开发体验,React实现了一套自己的事件机制,利用事件委托和合成事件的方式统一管理事件订阅和分发。 为了让组件能够响应用户的交互行为,React提供了一系列的事件处理器…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...