代理模式:控制访问的设计模式
代理模式:控制访问的设计模式
什么是代理模式?
代理模式是一种常见的设计模式,它允许通过代理对象来控制对真实对象的访问。代理模式的主要目的是在不改变原始对象的情况下,提供额外的功能或控制访问。
为什么要使用代理模式?
代理模式有以下几个主要的应用场景:
访问控制
:代理模式可以限制对真实对象的直接访问,只有通过代理对象才能访问真实对象。这样可以实现对真实对象的访问控制,例如权限验证、身份验证等。增加额外功能
:代理模式可以在不修改真实对象的情况下,为其增加额外的功能。代理对象可以在调用真实对象的方法前后执行一些额外的操作,例如日志记录、性能监控、缓存等。远程访问
:代理模式可以实现远程访问,即通过代理对象访问位于不同地址空间的真实对象。这对于分布式系统或跨网络的应用程序非常有用。
代理模式的两种分类
静态代理
静态代理是在编译时就已经确定代理对象和真实对象的关系
。代理对象和真实对象实现相同的接口或继承相同的父类,代理对象持有真实对象的引用,并在调用真实对象的方法前后执行一些额外的操作。
优点: 简单易懂
缺点: 需要为每个真实对象编写一个代理类
,当真实对象较多时,会导致代码冗余
案例: 一个简单的日志记录功能
假设我们有一个 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提供了一系列的事件处理器…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...