【Spring篇】JDK动态代理
目录
什么是代理?
代理模式
动态代理
Java中常用的代理模式
问题来了,如何动态生成代理类?
动态代理底层实现
什么是代理?
顾名思义,代替某个对象去处理一些问题,谓之代理,那么何为动态?即让JVM虚拟机去完成而非程序员去完成(与静态对比),连起来就是让虚拟机去动态的创建一个对象去代替另一个对象完成某些业务需求;
呢么其中就涉及到了两个对象,代理类和目标类;代理类又被前辈归纳成代理模式,下面看代理模式;
代理模式
代理模式是GoF23种设计模式之一。属于结构型设计模式。
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。
代理模式中的角色:
- 代理类(代理主题)
- 目标类(真实主题)
- 代理类和目标类的公共接口(抽象主题):客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。
为什么要有个公共接口呢?
呢你代理类要代替目标类完成某些操作,你是不是就需要拥有目标类该有的功能,如何拥有这个类拥有的功能呢?一、代理类继承目标类(但是继承太死板,耦合度太高,而且当需求量上去后,你是不是要写n个子类,不明智!!!)二、通过实现共同接口,实现必要方法,nice!!!,完美解决;
知道基本概念后,我们完善一下,用更加官方的话来表示。。。
动态代理
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。
Java中常用的代理模式
- jdk动态代理;
- cglib静态代理;
- Javassist动态代理技术;
我们主要分析JDK代理技术
JDK动态代理(理解):使用java反射包中的类和接口实现动态代理的功能,反射包java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy
| 下面我们通过一个实际业务需求来分析动态代理 |
假设某项目已上线,并且运行正常,只是客户反馈系统有一些地方运行较慢,要求项目组对系统进行优化。于是项目负责人就下达了这个需求。首先需要搞清楚是哪些业务方法耗时较长,于是让我们统计每个业务方法所耗费的时长。你坑定不能直接在已经上线并且运行很好的项目的源代码上操作啊,于是乎我们采用静态代理方法,代码如下:
// 目标接口
public interface OrderService {void add();void update();void delete();
}
// 目标类
public class OrderServiceImpl implements OrderService {@Overridepublic void add() {System.out.println("添加用户");}@Overridepublic void update() {System.out.println("修改用户");}@Overridepublic void delete() {System.out.println("删除用户");}
}
// 代理类
public class OrderServiceProxy implements OrderService {@Overridepublic void add() {long begin = System.currentTimeMillis();OrderService.add();long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}@Overridepublic void update() {long begin = System.currentTimeMillis();OrderService.update();long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}@Overridepublic void delete() {long begin = System.currentTimeMillis();OrderService.delete();long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}
}
在上面的静态代理方法中,代理类和目标类都实现同一个接口,在代理类中维护目标类对象,并完成对目标类对象方法的增强,这种方式虽然遵循开闭原则,但是代理类和目标类至少是“一对一”的绑定关系,如果需要被代理的目标类个数越多,代理类就会越多,会产生大量重复的代码,也不利于后期的维护。
于是乎我们使用动态代理技术:目前代码是这样的,需要我们动态生成一个代理类:
// 目标接口
public interfaceOrderService {void add();void update();void delete();
}
// 目标类
public class OrderServiceImpl implements OrderService {@Overridepublic void add() {System.out.println("添加用户");}@Overridepublic void update() {System.out.println("修改用户");}@Overridepublic void delete() {System.out.println("删除用户");}
}
问题来了,如何动态生成代理类?
回归到文章中心题目,JDK动态代理技术
在JDK中,有一个Proxy类,Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态的生成实现类。这个类有一个静态方法:newProxyInstance()方法。这个方法的目的就是给我们的目标对象返回一个代理对象。
我们可以看到其中newProxyInstance()方法有三个参数,下面分析三个参数:
- 第一个参数:类加载器。在内存中生成了字节码,要想执行这个字节码,也是需要先把这个字节码加载到内存当中的。所以要指定使用哪个类加载器加载。
- 第二个参数:接口类型。代理类和目标类实现相同的接口,所以要通过这个参数告诉JDK动态代理生成的类要实现哪些接口。
- 第三个参数:调用处理器。这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。显然这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。
所以接下来我们要写一下java.lang.reflect.InvocationHandler接口的实现类,并且实现接口中的方法,代码如下:
public class TimerInvocationHandler implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;}
}
InvocationHandler接口中有一个方法invoke,这个invoke方法上有三个参数:
- 第一个参数:Object proxy。代理对象。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。
- 第二个参数:Method method。目标方法。
- 第三个参数:Object[] args。目标方法调用时要传的参数。
我们将来肯定是要调用“目标方法”的,但要调用目标方法的话,需要“目标对象”的存在,“目标对象”从哪儿来呢?我们可以给TimerInvocationHandler提供一个构造方法,可以通过这个构造方法传过来“目标对象”,代码如下:
public class TimerInvocationHandler implements InvocationHandler {// 目标对象private Object target;// 通过构造方法来传目标对象public TimerInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 目标执行之前增强。long begin = System.currentTimeMillis();// 调用目标对象的目标方法Object retValue = method.invoke(target, args);// 目标执行之后增强。long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");// 一定要记得返回哦。return retValue;}
}
接下来我们开始调用:
// 创建目标对象OrderService target = new OrderServiceImpl();// 创建代理对象OrderService orderServiceProxy = (OrderService) ProxyUtil.newProxyInstance(target);// 调用代理对象的代理方法orderServiceProxy.add();orderServiceProxy.update();orderServiceProxy.delete();
成功;
动态代理底层实现
现在我们思考一个问题:在使用动态代理之前,我们有一个目标类、一个公共接口;我们如何通过二者和proxy类返回一个代理类对象呢?
我们知道创建一个对象就先要有这个类才能new对象,那现在代理类都还不存在,该怎么去构造代理对象呢?
JDK动态代理的目的就是通过接口来生成代理类以及代理类的对象,我们知道接口是不能直接通过new关键字创建对象的。那么JDK动态代理是怎么创建出代理类以及代理类对象的呢?
相关文章:

【Spring篇】JDK动态代理
目录 什么是代理? 代理模式 动态代理 Java中常用的代理模式 问题来了,如何动态生成代理类? 动态代理底层实现 什么是代理? 顾名思义,代替某个对象去处理一些问题,谓之代理,那么何为动态&a…...

【从零开始实现意图识别】中文对话意图识别详解
前言 意图识别(Intent Recognition)是自然语言处理(NLP)中的一个重要任务,它旨在确定用户输入的语句中所表达的意图或目的。简单来说,意图识别就是对用户的话语进行语义理解,以便更好地回答用户…...
腾讯云点播小程序端上传 SDK
云点播是专门应对上传大视频文件的。 腾讯云点播文档:https://cloud.tencent.com/document/product/266/18177 这个文档比较简单,实在不行,把demo下载下来,一看就明白了,然后再揉一下挪到自己的项目里。完事。 getSign…...

【MATLAB源码-第88期】基于matlab的灰狼优化算法(GWO)的栅格路径规划,输出做短路径图和适应度曲线
操作环境: MATLAB 2022a 1、算法描述 灰狼优化算法(Grey Wolf Optimizer, GWO)是一种模仿灰狼捕食行为的优化算法。灰狼是群居动物,有着严格的社会等级结构。在灰狼群体中,通常有三个等级:首领ÿ…...

electron使用electron-builder macOS windows 打包 签名 更新 上架
0. 前言 0.1 项目工程 看清目录结构,以便您阅读后续内容 0.2 参考资料 (1)macOS开发 证书等配置/打包后导出及上架 https://www.jianshu.com/p/c9c71f2f6eac首先需要为Mac App创建App ID: 填写信息如下—Description为"P…...

autojs项目搭建和入门实践
Auto.js 是一款无需root权限的javascript自动化软件,它可以帮助用户在手机上自动执行各种任务,比如自动填写表单、自动点击按钮、自动切换应用等,并且可以通过图形用户界面来管理和编辑脚本。 软件环境 操作系统:win10 VSCODE&…...

uni-app 跨端开发注意事项
文章目录 前言H5正常但App异常的可能性标题二H5正常但小程序异常的可能性小程序正常但App异常的可能性小程序或App正常,但H5异常的可能性App正常,小程序、H5异常的可能性使用 Vue.js 的注意区别于传统 web 开发的注意H5 开发注意微信小程序开发注意支付宝…...

在 vscode 中的json文件写注释,不报错的解决办法
打开 vscode 的「设置」,搜索:files: associations,然后添加 *.json jsonc最后...

基于uniapp的 电子书小程序——需求整理
前言 想开发一个很简单的 电子书阅读小程序,要怎么做的。下面从功能、数据库设计这一块来说一下。说不一定能从某个角度提供一些思路 开发语言 springcloud uniapp 小程序(vue2)mysql 说明 电子书的主题是电子书,我们在日常…...
Hutool HttpRequest 首次请求正常 第二次被系统拦截
Hutool HttpRequest 首次请求正常 第二次被系统拦截 功能描述异常现象错误代码 异常排查问题跟踪问题总结处理方案最终修改后的代码 功能描述 需要请求第三方某个接口,获取接口中的数据。 异常现象 使用main 方法 通过Hutool 工具类发出请求,获取数据…...

github国内访问小解(windows)
git 下载安装 使用 github 前必须确保电脑上已经安装了 Git,可以从 Git 官方网站去下载。 官方的网站在国内访问会比较慢,这里可以选择国内镜像:https://registry.npmmirror.com/binary.html?pathgit-for-windows/ github 之旅 确认电脑已…...

NX二次开发UF_CSYS_set_wcs_display 函数介绍
文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan UF_CSYS_set_wcs_display Defined in: uf_csys.h int UF_CSYS_set_wcs_display(int display_status ) overview 概述 Set display of work coordinate system. 展示工作坐标系。 …...

DNS 区域传输 (AXFR)
漏洞描述 docker环境搭建 使用 AXFR 协议的 DNS 区域传输是跨 DNS 服务器复制 DNS 记录的最简单机制。为了避免在多个 DNS 服务器上编辑信息,可以在一台服务器上编辑信息,并使用 AXFR 将信息复制到其他服务器。但是,如果您不保护您的服务器&…...

Ubuntu 安装 JMeter:轻松上手
Apache JMeter 是一个开源的负载测试工具,可以用于测试静态和动态资源,确定服务器的性能和稳定性。在本文中,我们将讨论如何下载和安装 JMeter。 安装 Java(已安装 Java 的此步骤可跳过) 要下载 Java,请遵…...
在工业生产环境下,服务器没有互联网,如何通过代理自己的电脑上互联网?
服务器主机是CentOS7操作系统.,服务器的局域网是10.0.6.x网段。我的笔记本的以太网口的局域网ip是也是10.0.6.x,由于这个10.0.6.x的整个局域网是没有拨号上网的所有无法访问互联网。 但是,如果笔记本脸上wifi,wifi的网段是192.168…...
【brpc学习实践六】backup request场景案例
应用场景 有时为了保证可用性,需要同时访问两路服务,哪个先返回就取哪个。在brpc中,这有多种做法,根据server是否挂在同一个命名服务内有所区别。 当后端server可以挂在一个命名服务内时 Channel开启backup request。这个Channel会先向其中一个server发送请求,如果在Ch…...
el-table导出为excel表格
目录 1.下载依赖 2.引入插件 3.定义函数 4.设置table的id 1.下载依赖 npm intall --save xlsx npm intall --save file-saver2.引入插件 import FileSaver from "file-saver"; import XLSX from "xlsx";3.定义函数 exportExcel() {let fix document…...

CVE-2022-0543(Redis 沙盒逃逸漏洞)
简介 CVE-2022-0543是一个与Redis相关的安全漏洞。在Redis中,用户连接后可以通过eval命令执行Lua脚本,但在沙箱环境中脚本无法执行命令或读取文件。然而,攻击者可以利用Lua沙箱中遗留的变量package的loadlib函数来加载动态链接库liblua5.1.s…...
查swap内存使用
查询linux的swap被什么使用了 查询centos的swap被什么进程使用了 swap内存被什么程序占用,什么程序使用了swap分区,占用swap内存的进程 查系统使用swap内存前10个进程: for i in $( cd /proc;ls |grep "^[0-9]"|awk $0 >10…...

Element UI的Tabs 标签页位置导航栏去除线条
在实际开发中,我们调整了相关样式,导致导航栏的相关样式跟随不上,如下图所示: 因为我跳转了前边文字的样式并以在导航栏添加了相关头像,导致右边的线条定位出现问题,我在想,要不我继续调整右边…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...

三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...