JAVA的动态代理
Java 动态代理是 Java 语言中一项强大的特性,它允许在运行时动态地创建符合一组接口的代理类。这种机制广泛应用于各种框架和工具中,如 Spring AOP、Hibernate 数据查询、Mockito 测试框架等。通过动态代理,可以在不修改原有代码的前提下,为对象添加新的功能或行为,比如日志记录、事务管理、性能监控等。
动态代理的基本概念
1. 代理模式
代理模式是一种设计模式,其目的是为某个对象提供一个代理以控制对该对象的访问。在代理模式中,代理类和委托类实现相同的接口,代理类负责处理请求并可能在请求前后执行额外的操作。代理模式按照职责(使用场景)可以分为几种类型,如远程代理、虚拟代理、Copy-on-Write 代理等。
2. 动态代理 vs 静态代理
- 静态代理:代理类是在编译时就已经确定的,代理类和被代理类之间的关系是固定的。这意味着每次需要代理一个新的类时,都需要手动编写一个新的代理类。
- 动态代理:代理类是在程序运行时动态生成的。这种方式更加灵活,可以方便地对多个类或接口进行代理,而无需为每个类单独编写代理类。
Java 动态代理的实现
Java 动态代理主要依赖于两个核心组件:
java.lang.reflect.Proxy类:用于创建代理对象。java.lang.reflect.InvocationHandler接口:用于处理代理对象上的方法调用。
创建动态代理的基本步骤
- 定义接口:需要为代理类定义一个或多个接口。
- 实现
InvocationHandler接口:创建一个实现了InvocationHandler接口的类,并重写invoke方法。在这个方法中,可以添加前置处理、目标方法调用以及后置处理的逻辑。 - 创建代理实例:使用
Proxy.newProxyInstance()方法创建代理实例。这个方法需要三个参数:类加载器、一组接口以及InvocationHandler实例。
示例代码
假设我们有一个简单的接口 Hello 和它的实现类 HelloImpl,接下来我们将创建一个动态代理来增强 Hello 接口的功能。
1. 定义接口
public interface Hello {void sayHello();
}
2. 实现类
public class HelloImpl implements Hello {@Overridepublic void sayHello() {System.out.println("Hello, world!");}
}
3. 实现 InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class MyInvocationHandler implements InvocationHandler {private final Object target;public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 前置处理System.out.println("Before method call");// 调用实际方法Object result = method.invoke(target, args);// 后置处理System.out.println("After method call");return result;}
}
4. 创建代理实例并调用方法
import java.lang.reflect.Proxy;public class Test {public static void main(String[] args) {// 创建目标对象Hello hello = new HelloImpl();// 创建 InvocationHandlerMyInvocationHandler handler = new MyInvocationHandler(hello);// 创建代理实例Hello proxyHello = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(),new Class[]{Hello.class},handler);// 通过代理调用方法proxyHello.sayHello();}
}
动态代理的应用场景
-
日志记录:在方法调用前后记录日志信息,便于调试和追踪。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Calling method: " + method.getName());long startTime = System.currentTimeMillis();Object result = method.invoke(target, args);long endTime = System.currentTimeMillis();System.out.println("Method " + method.getName() + " took " + (endTime - startTime) + " ms to execute");return result; } -
事务管理:在调用业务逻辑之前开启事务,在完成业务逻辑之后提交或回滚事务。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 开启事务System.out.println("Starting transaction");Object result = method.invoke(target, args);// 提交事务System.out.println("Committing transaction");return result;} catch (Exception e) {// 回滚事务System.out.println("Rolling back transaction");throw e;} } -
权限校验:在访问特定方法之前进行权限检查。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (!hasPermission(method)) {throw new SecurityException("Access denied for method: " + method.getName());}Object result = method.invoke(target, args);return result; }private boolean hasPermission(Method method) {// 检查权限的逻辑return true; // 示例中总是返回 true } -
性能监控:测量方法执行的时间,用于性能优化。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long startTime = System.currentTimeMillis();Object result = method.invoke(target, args);long endTime = System.currentTimeMillis();System.out.println("Method " + method.getName() + " took " + (endTime - startTime) + " ms to execute");return result; }
5. 缓存:在方法调用前检查缓存,如果缓存中有结果则直接返回,避免重复计算。
6. 远程调用:实现远程方法调用,如 RMI(Remote Method Invocation)。
注意事项
- 异常处理:在
invoke方法中,你可能需要捕获并处理异常,以确保代理类的健壮性。 - 线程安全:如果你的代理类需要在多线程环境中使用,确保
MyInvocationHandler是线程安全的。
动态代理的优缺点
优点
- 灵活性高:可以在运行时动态生成代理类,无需提前编写代理类。
- 扩展性强:可以通过代理类为多个接口或类添加相同的行为,而无需修改原有代码。
- 代码复用:可以复用同一个
InvocationHandler实现,为多个类提供相同的行为增强。
缺点
- 性能开销:由于涉及到反射机制,动态代理在性能上可能会比静态代理稍差。
- 接口限制:JDK 动态代理只能代理实现了接口的类,对于没有实现接口的类,需要使用其他工具如 CGLib。
总结
Java 动态代理是一种强大的机制,通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口,可以在运行时动态地创建代理对象,为对象添加新的功能或行为。这种机制广泛应用于各种框架和工具中,为开发者提供了极大的灵活性和扩展性。通过理解和掌握动态代理,可以更好地利用这一特性来解决实际开发中的问题。
相关文章:
JAVA的动态代理
Java 动态代理是 Java 语言中一项强大的特性,它允许在运行时动态地创建符合一组接口的代理类。这种机制广泛应用于各种框架和工具中,如 Spring AOP、Hibernate 数据查询、Mockito 测试框架等。通过动态代理,可以在不修改原有代码的前提下&…...
「图文详解」Pycharm 远程服务器Debug
首先声明一点,社区版的无法使用,需要使用 专业版Pycharm 才可以使用,至于密钥可以去TB购入,价格低廉、有效期长 相信很多小伙伴会面临本地电脑显存不够,但是服务器代码又无法直观的调试,只能靠打日志的方法…...
Golang反射在实际开发中的应用场景
Golang反射在实际开发中的应用场景 当然可以,以下是一些使用Go语言反射的实际开发场景: 1. 通用处理函数 当你需要编写一个函数,它可以处理不同类型的参数时,反射可以让你在运行时检查和操作这些参数。 示例代码: …...
【二叉树】C非递归算法实现二叉树的先序、中序、后序遍历
引言: 遍历二叉树:指按某条搜索路径巡访二叉树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。 除了层次遍历外,二叉树有三个重要的遍历方法:先序遍历、中序遍历、后序遍历。 1、递归算法实现先序、中序、后…...
Android——事件冲突处理
当我们给列表的item设置了点击事件后,又给item中的按钮设置了点击事件,此时item的点击事件会失效。 解决 给item的布局xml中设置以下属性 android:descendantFocusability"blocksDescendants"<LinearLayout xmlns:android"http://sc…...
vue + elementui 全局Loading效果
注:在request请求和响应封装的文件里引入loading,发请求时打开loading,响应时关闭loading,这样每个接口调用时都会有loading效果 (1) 首先确保项目中安装了element-ui这个依赖包 npm i element-ui -S&…...
深度了解flink(十) JobManager(4) ResourceManager HA
ResourceManager(ZK模式)的高可用启动流程 ResourceManager启动流程在DefaultDispatcherResourceManagerComponentFactory#create中 public DispatcherResourceManagerComponent create(Configuration configuration,ResourceID resourceId,Executor i…...
【万兴科技-注册_登录安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…...
Android启动流程_Zygote阶段
前言 上一篇文档中我们描述了 Android 启动中的 init 启动部分,本片文档将会继续 Android 启动流程的逻辑,继续梳理 Zygote 部分功能。 说明框架 对于 Zygote 进程,要从以下框架说明: 第一点,编译,zygo…...
2022NOIP比赛总结
种花 1.本题是一道前缀和优化加上枚举的问题。先考虑 C 因为 F 是 C 下边随便加一个点,所以只要求出 C 就求出了 F 。 注意到,并没有要求上下行一样,唯一的要求是 C 的两个横要隔一行,这就是问题的突破点,这题很明显…...
Leetcode 排序链表
这段代码的算法思想是 归并排序,一种适合链表的排序方法。它通过递归地将链表拆分成两部分,分别排序,然后合并已排序的部分,从而达到整体排序的目的。以下是代码的中文解释: 算法步骤: 找到链表的中点&…...
哈希函数简介
哈希函数是一种将任意大小的数据输入(通常称为“消息”)转换为固定大小的输出(称为“哈希值”或“摘要”)的算法。 主要特点: 1、输出固定长度 无论输入数据的大小如何,哈希函数的输出总是固定长度。例如…...
nginx------正向代理,反向代理生产,以及能否不使用代理详解
在生产环境中,选择使用正向代理还是反向代理取决于具体的应用场景和需求。下面详细解释这两种代理的用处以及为什么在不同情况下会选择它们。 正向代理 (Forward Proxy) 用途 匿名访问: 隐藏客户端的真实 IP 地址,提供隐私保护。常用于绕过…...
iptables限制docker端口禁止某台主机访问(使用DOCKER链和raw表的PREROUTING链)
背景: 在Linux上docker映射了端口,想着对服务端口进行限制指定IP访问,发现在filter表的INPUT链限制无效 环境: 主机192.168.56.132上的docker容器部署了nginx并将容器80端口映射到主机8000端口 [rootlocalhost ~]# docker ps …...
【VM实战】VMware迁移到VirtualBox
VMware 虚拟机开机卸载VMware Tools 调整虚拟磁盘 对于Windows 10及以上的虚拟机,一般VMware默认都会选Nvme固态硬盘。在导出前必须将其改为SATA,否则VirtualBox导入会报Appliance Import错误 (E_INVALIDARG 0x80070057) 先删掉当前盘的挂载ÿ…...
Android WebView加载不到cookie
以下配置根据需求酌情添加,建议逐个试验,cookie操作不是内存操作,建议修改配置后卸载app再重新运行防止缓存影响测试结果。 1.设置应用程序的 WebView 实例是否应发送并接受 Cookie CookieManager cookieManager CookieManager.getInstanc…...
c++qt
1.显示画布 #include "code.h" #include <QtWidgets/QApplication> #include<iostream> #include<vector> #include <QWindow> #include <QGraphicsView> #include <QGraphicsScene>using namespace std;//1.空格 2.墙 3.入口…...
零跑汽车嵌入式面试题汇总及参考答案
C++ 的三大特性是什么? C++ 的三大特性分别是封装、继承和多态。 封装 概念:封装是把数据和操作数据的函数绑定在一起,对数据的访问进行限制。通过将数据成员声明为私有或保护,只允许通过公共的成员函数来访问和修改数据,从而隐藏了类的内部实现细节。这有助于提高代码的安…...
LC:贪心题解
文章目录 376. 摆动序列 376. 摆动序列 题目链接:https://leetcode.cn/problems/wiggle-subsequence/description/ 这个题目自己首先想到的是动态规划解题,贪心解法真的非常妙,参考下面题解:https://leetcode.cn/problems/wiggle…...
ubuntu交叉编译dbus库给arm平台使用
1.下载dbus库源码 https://www.freedesktop.org/wiki/Software/dbus 克隆源码: https://gitlab.freedesktop.org/dbus/dbus/-/tree/dbus-1.12?ref_type=heads 下载1.12.20版本: 指定pkgconfig环境变量: export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$PWD/../expat-2.3.…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果 *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...
