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

JAVA的动态代理

       Java 动态代理是 Java 语言中一项强大的特性,它允许在运行时动态地创建符合一组接口的代理类。这种机制广泛应用于各种框架和工具中,如 Spring AOP、Hibernate 数据查询、Mockito 测试框架等。通过动态代理,可以在不修改原有代码的前提下,为对象添加新的功能或行为,比如日志记录、事务管理、性能监控等。

动态代理的基本概念

1. 代理模式

       代理模式是一种设计模式,其目的是为某个对象提供一个代理以控制对该对象的访问。在代理模式中,代理类和委托类实现相同的接口,代理类负责处理请求并可能在请求前后执行额外的操作。代理模式按照职责(使用场景)可以分为几种类型,如远程代理、虚拟代理、Copy-on-Write 代理等。

2. 动态代理 vs 静态代理
  • 静态代理:代理类是在编译时就已经确定的,代理类和被代理类之间的关系是固定的。这意味着每次需要代理一个新的类时,都需要手动编写一个新的代理类。
  • 动态代理:代理类是在程序运行时动态生成的。这种方式更加灵活,可以方便地对多个类或接口进行代理,而无需为每个类单独编写代理类。

Java 动态代理的实现

Java 动态代理主要依赖于两个核心组件:

  • java.lang.reflect.Proxy 类:用于创建代理对象。
  • java.lang.reflect.InvocationHandler 接口:用于处理代理对象上的方法调用。
创建动态代理的基本步骤
  1. 定义接口:需要为代理类定义一个或多个接口。
  2. 实现 InvocationHandler 接口:创建一个实现了 InvocationHandler 接口的类,并重写 invoke 方法。在这个方法中,可以添加前置处理、目标方法调用以及后置处理的逻辑。
  3. 创建代理实例:使用 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();}
}

动态代理的应用场景

  1. 日志记录:在方法调用前后记录日志信息,便于调试和追踪。

    @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;
    }
  2. 事务管理:在调用业务逻辑之前开启事务,在完成业务逻辑之后提交或回滚事务。

    @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;}
    }
  3. 权限校验:在访问特定方法之前进行权限检查。

    @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
    }
  4. 性能监控:测量方法执行的时间,用于性能优化。

    @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设置了点击事件后&#xff0c;又给item中的按钮设置了点击事件&#xff0c;此时item的点击事件会失效。 解决 给item的布局xml中设置以下属性 android:descendantFocusability"blocksDescendants"<LinearLayout xmlns:android"http://sc…...

vue + elementui 全局Loading效果

注&#xff1a;在request请求和响应封装的文件里引入loading&#xff0c;发请求时打开loading&#xff0c;响应时关闭loading&#xff0c;这样每个接口调用时都会有loading效果 &#xff08;1&#xff09; 首先确保项目中安装了element-ui这个依赖包 npm i element-ui -S&…...

深度了解flink(十) JobManager(4) ResourceManager HA

ResourceManager&#xff08;ZK模式&#xff09;的高可用启动流程 ResourceManager启动流程在DefaultDispatcherResourceManagerComponentFactory#create中 public DispatcherResourceManagerComponent create(Configuration configuration,ResourceID resourceId,Executor i…...

【万兴科技-注册_登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…...

Android启动流程_Zygote阶段

前言 上一篇文档中我们描述了 Android 启动中的 init 启动部分&#xff0c;本片文档将会继续 Android 启动流程的逻辑&#xff0c;继续梳理 Zygote 部分功能。 说明框架 对于 Zygote 进程&#xff0c;要从以下框架说明&#xff1a; 第一点&#xff0c;编译&#xff0c;zygo…...

2022NOIP比赛总结

种花 1.本题是一道前缀和优化加上枚举的问题。先考虑 C 因为 F 是 C 下边随便加一个点&#xff0c;所以只要求出 C 就求出了 F 。 注意到&#xff0c;并没有要求上下行一样&#xff0c;唯一的要求是 C 的两个横要隔一行&#xff0c;这就是问题的突破点&#xff0c;这题很明显…...

Leetcode 排序链表

这段代码的算法思想是 归并排序&#xff0c;一种适合链表的排序方法。它通过递归地将链表拆分成两部分&#xff0c;分别排序&#xff0c;然后合并已排序的部分&#xff0c;从而达到整体排序的目的。以下是代码的中文解释&#xff1a; 算法步骤&#xff1a; 找到链表的中点&…...

哈希函数简介

哈希函数是一种将任意大小的数据输入&#xff08;通常称为“消息”&#xff09;转换为固定大小的输出&#xff08;称为“哈希值”或“摘要”&#xff09;的算法。 主要特点&#xff1a; 1、输出固定长度 无论输入数据的大小如何&#xff0c;哈希函数的输出总是固定长度。例如…...

nginx------正向代理,反向代理生产,以及能否不使用代理详解

在生产环境中&#xff0c;选择使用正向代理还是反向代理取决于具体的应用场景和需求。下面详细解释这两种代理的用处以及为什么在不同情况下会选择它们。 正向代理 (Forward Proxy) 用途 匿名访问&#xff1a; 隐藏客户端的真实 IP 地址&#xff0c;提供隐私保护。常用于绕过…...

iptables限制docker端口禁止某台主机访问(使用DOCKER链和raw表的PREROUTING链)

背景&#xff1a; 在Linux上docker映射了端口&#xff0c;想着对服务端口进行限制指定IP访问&#xff0c;发现在filter表的INPUT链限制无效 环境&#xff1a; 主机192.168.56.132上的docker容器部署了nginx并将容器80端口映射到主机8000端口 [rootlocalhost ~]# docker ps …...

【VM实战】VMware迁移到VirtualBox

VMware 虚拟机开机卸载VMware Tools 调整虚拟磁盘 对于Windows 10及以上的虚拟机&#xff0c;一般VMware默认都会选Nvme固态硬盘。在导出前必须将其改为SATA&#xff0c;否则VirtualBox导入会报Appliance Import错误 (E_INVALIDARG 0x80070057) 先删掉当前盘的挂载&#xff…...

Android WebView加载不到cookie

以下配置根据需求酌情添加&#xff0c;建议逐个试验&#xff0c;cookie操作不是内存操作&#xff0c;建议修改配置后卸载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. 摆动序列 题目链接&#xff1a;https://leetcode.cn/problems/wiggle-subsequence/description/ 这个题目自己首先想到的是动态规划解题&#xff0c;贪心解法真的非常妙&#xff0c;参考下面题解&#xff1a;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.…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...