滚雪球学Java(25):动态代理

news2024/9/27 23:29:00

🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!!


前言

在Java开发中,经常会使用代理模式来实现一些特殊的需求,例如AOP编程、RPC调用等。Java中的代理模式主要有静态代理和动态代理两种,其中静态代理需要手动编写一个代理类,而动态代理则可以在运行时动态生成代理类,更加灵活方便。

本文将介绍Java中的动态代理,包括动态代理的实现原理、使用方法、以及动态代理实现中的常见问题和注意事项。

摘要

本文将分以下几部分介绍Java中的动态代理:

  1. 动态代理的概念和实现原理。
  2. Java中的动态代理实现。
  3. 动态代理的常见问题和注意事项。

内容

1. 动态代理的概念和实现原理

代理模式是一种常用的设计模式,它可以在不改变原有代码的前提下,实现对原有代码的扩展。代理模式主要有静态代理和动态代理两种。

在Java中,静态代理需要手动编写一个代理类,其中代理类需要实现与被代理对象相同的接口方法,并在方法中调用被代理对象的对应方法。而动态代理则是在运行时动态生成代理类,更加灵活方便。

Java中的动态代理主要使用了Java反射机制,通过反射动态生成代理类并调用其中的方法。动态代理需要实现一个代理接口,在运行时使用反射动态生成一个代理类,该代理类实现了代理接口,并在其中调用了InvocationHandler中的invoke()方法。

在Java中,动态代理主要由两个类来实现:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。其中,Proxy类用于生成动态代理类对象,而InvocationHandler接口则用于实现具体的代理逻辑。在使用动态代理时,需要创建一个InvocationHandler对象,并将其作为参数传递给Proxy.newProxyInstance()方法,该方法将返回一个代理对象。

2. Java中的动态代理实现

Java中的动态代理主要使用了java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler两个类。在使用动态代理时,需要创建一个InvocationHandler对象,并将其作为参数传递给Proxy.newProxyInstance()方法,该方法将返回一个代理对象。

下面是一个简单的示例,展示了如何使用动态代理:

interface Subject {
    void request();
}

class RealSubject implements Subject {
    public void request() {
        System.out.println("RealSubject request()");
    }
}

class DynamicProxy implements InvocationHandler {
    private Object subject;

    public DynamicProxy(Object subject) {
        this.subject = subject;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = method.invoke(subject, args);
        System.out.println("after");
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        InvocationHandler handler = new DynamicProxy(realSubject);
        Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(), handler);
        subject.request();
    }
}

在这个例子中,我们定义了一个Subject接口和一个RealSubject类。DynamicProxy类是我们定义的代理类,用于实现代理逻辑。在实现代理逻辑时,我们使用了InvocationHandler接口,并在其中实现了before()和after()方法,用于在代理的方法执行前后进行处理。

在main()方法中,我们首先创建了一个RealSubject对象,然后创建了一个DynamicProxy对象,将RealSubject对象传递给它。接着,我们使用Proxy.newProxyInstance()方法创建了一个代理对象,并将该代理对象强制转换为Subject类型。最后,我们调用了代理对象的request()方法,该方法会自动调用DynamicProxy中的invoke()方法,从而实现了代理逻辑。

3. 动态代理的常见问题和注意事项

动态代理虽然使用灵活方便,但在实现时也需要注意一些问题:

  1. 基于接口的代理:在Java中,动态代理只能基于接口实现。如果需要基于实现类实现动态代理,则需要使用字节码工具类,例如ASM和CGLIB。
  2. 方法调用循环问题:在动态代理中,如果代理对象调用了被代理对象的方法,将会导致invoke()方法被重复调用,从而导致死循环。为了避免这个问题,我们可以在invoke()方法中判断是否为代理对象,以避免调用被代理对象的方法。
  3. hashCode和equals方法的问题:在动态代理中,由于代理类和被代理类是两个不同的类,因此它们的hashCode和equals方法会有不同的实现。如果需要在代理对象中使用hashCode和equals方法,需要特别处理。

测试用例

interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);
}

class CalculatorImpl implements Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    public int subtract(int a, int b) {
        return a - b;
    }
}

class CalculatorProxy implements InvocationHandler {
    private Object target;

    public CalculatorProxy(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = method.invoke(target, args);
        System.out.println("after");
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl();
        InvocationHandler handler = new CalculatorProxy(calculator);
        Calculator proxy = (Calculator) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
                calculator.getClass().getInterfaces(), handler);
        System.out.println("add(1, 2) = " + proxy.add(1, 2));
        System.out.println("subtract(5, 3) = " + proxy.subtract(5, 3));
    }
}

该测试用例演示了如何使用动态代理实现一个简单的计算器。其中,Calculator接口定义了add()和subtract()两个方法,CalculatorImpl类是真正的计算器实现类,而CalculatorProxy类是代理类,用于实现代理逻辑。

在测试用例中,我们首先创建了一个CalculatorImpl对象,然后创建了一个CalculatorProxy对象,将CalculatorImpl对象传递给它。接着,我们使用Proxy.newProxyInstance()方法创建了一个代理对象,并将该代理对象强制转换为Calculator类型。最后,我们调用了代理对象的add()和subtract()方法,并输出了它们的返回值。

全文小结

本文介绍了Java中的动态代理,包括动态代理的概念和实现原理、Java中的动态代理实现、以及动态代理实现中的常见问题和注意事项。动态代理可以在不改变原有代码的前提下,实现对原有代码的扩展,非常灵活方便。在使用动态代理时,需要注意基于接口的代理、方法调用循环问题以及hashCode和equals方法的问题。

附录源码

  如上涉及所有源码均已上传同步在Gitee,提供给同学们一对一参考学习,辅助你更迅速的掌握。

☀️建议/推荐你


  无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学Java」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门Java编程,就像滚雪球一样,越滚越大,指数级提升。

📣关于我


我是bug菌,CSDN | 掘金 | infoQ | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计15w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1026236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据挖掘note(1)

数据挖掘一般分为机器学习和统计学习,大数据学的课程一般是关于机器学习,我们学的浅,主要关于统计学习,示意图如下所示: 这是一个大数据时代,但是数据挖掘的利用率不足0.5%,可见数据挖掘的空间巨…

全国各地演讲口才培训机构信息那么多需要如何选择?

演讲口才是一项非常重要的能力,它不仅可以帮助我们在职场中更好地表达自己,还可以在公共场合中更好地展示自己的个性和魅力。因此,越来越多的人开始关注演讲口才的培训,而全国各地也涌现出了众多的演讲口才培训机构。 选择适合自己…

ReactNative中升级IOS 17版本Crash解决

ReactNative中升级IOS 17版本Crash解决 ReactNative中升级IOS 17版本Crash解决一、问题描述二、原因分析三、解决方案决策3.1 设置宽高为非零值3.2 使用新的UIGraphicsImageRenderer替换就版本的UIGraphicsBeginImageContext 四、可能使用到该API的三方库4.1 react-native-fast…

RK3568开发板SG90 舵机模块的功能实现-迅为电子

1 模块说明 SG90 舵机模块如下图所示: 常见的舵机转向角度有 0-90 度,0-180 度,0-360 度,可以用在垃圾桶项目开盖用,智能小车的全比例转向,摄像头云台,机械臂等。 2 接线说明 SG90 舵机模块上三条线&…

猫头虎博主成为创作者的第1024天纪念日

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…

Unity制作曲线进度条

unity制作曲线进度条 大家好,我是阿赵。   在使用Unity引擎做进度条的时候,有时会遇到一个问题,如果进度条不是简单的横向、纵向或者圆形,而是任意的不规则形状,那该怎么办呢?比如这样的: 一…

【JDK 8 -收集和统计】7.1 collector 收集器

一、collect()方法 二、两个重载方法 2.1 方法一 2.2 方法二(常用) 三、Collector 的 作用 四、Collectors 的作用 > Collectors.toList() > Collectors.toMap() > Collectors.toSet() > Collectors.toColl…

基于深度强化学习的四旋翼无人机航线跟随

源自:指挥与控制学报 作者:杨志鹏 李波 甘志刚 梁诗阳 “人工智能技术与咨询” 发布 摘 要 针对无人机在空中执行航线跟随任务时无法对未知环境作出合理应对措施等问题, 提出了一种基于深度强化学习的四 旋翼无人机航线跟随方法. 通过无人机受力…

牛客: BM17 二分查找-I

牛客: BM17 二分查找-I 文章目录 牛客: BM17 二分查找-I题目描述题解思路题解代码 题目描述 题解思路 一个左端点, 一个右端点, 循环直到左右端点交叉, 取左右端点的中点 若中点的值大于目标值, 意味着目标值只可能在中点的左边, 所以右端点变为中点的前一个位置 若中点的值…

机器学习第九课--随机森林

一.什么是集成模型 对于几乎所有的分类问题(图像识别除外,因为对于图像识别问题,目前深度学习是标配),集成模型很多时候是我们的首选。比如构建一个评分卡系统,业界的标配是GBDT或者XGBoost等集成模型,主要因为它的效…

让开源数据开发平台助力提质增效!

用低代码技术平台,可以提高办公协作效率,可以让数据资源变得更有意义和价值,也可以为企业做出更理想的发展决策。作为开源数据开发平台服务商,流辰信息谨守研发初心,一直在低代码技术平台领域努力耕耘,为行…

[Linux入门]---yum软件安装及vim编辑器配置

文章目录 1.Linux软件安装包2.如何安装软件注意事项下载rzsz查看rzsz软件包安装or卸载软件原理 3.简单配置配置文件常用配置选项(测试)使用插件使用链接配置 1.Linux软件安装包 Linux的三种软件安装方法: ①源代码安装。 在Linux系统下载程序…

【C++】动态内存管理 ③ ( C++ 对象的动态创建和释放 | new 运算符 为类对象 分配内存 | delete 运算符 释放对象内存 )

文章目录 一、C 对象的动态创建和释放1、C 语言 对象的动态创建和释放 的方式2、C 语言 对象的动态创建和释放 的方式 二、代码示例 - 对象的动态创建和释放 一、C 对象的动态创建和释放 使用 C 语言中的 malloc 函数 可以为 类对象 分配内存 ; 使用 free 函数可以释放上述分配…

Redis的介绍以及简单使用

Redis(Remote Dictionary Server)是一个开源的内存数据存储系统,它以键值对的形式将数据存在内存中,并提供灵活、高性能的数据访问方式。Redis具有高速读写能力和丰富的数据结构支持,可以广泛应用于缓存、消息队列、实…

二叉搜索树(BST,Binary Search Tree)

文章目录 1. 二叉搜索树1.1 二叉搜索树概念1.2 二叉搜索树的查找1.3 二叉搜索树的插入1.4 二叉搜索树的删除 2 二叉搜索树的实现3 二叉搜索树的应用3.1二叉搜索树的性能分析 1. 二叉搜索树 1.1 二叉搜索树概念 二叉搜索树又称二叉排序树,它或者是一棵空树&#xf…

Hive行转列[一行拆分成多行/一列拆分成多列]

场景: hive有张表armmttxn_tmp,其中有一个字段lot_number,该字段以逗号分隔开多个值,每个值又以冒号来分割料号和数量,如:A3220089:-40,A3220090:-40,A3220091:-40,A3220083:-40,A3220087:-40,A3220086:-4…

LIN - 基础

LIN - 基础 概念CAN/LIN 物理层数据链路层帧格式 概念 原理 基于UART数据格式、主从结构 串行通讯 拓扑 LIN网络中有且只有一个主节点,其他都是从节点 优缺点 功能简单 实时性低 成本低 单主多从,不存在冲突,无需仲裁 主任务/从任务 主任务&…

电脑出现找不到msvcp120.dll无法继续执行代码,不用担心多种方法帮你搞定

今天,我想和大家分享的文章是“msvcp120.dll丢失的修复方法”。我相信,对于我们很多人来说,电脑是我们学习、娱乐的重要工具,而当电脑出现问题时,我们往往束手无策。因此,我希望通过今天的文章,…

笔记-搭建和使用docker-registry私有镜像仓库

笔记-搭建和使用docker-registry私有镜像仓库 拉取/安装registry镜像 和 对应的ui镜像 如果有网络可以直接拉取镜像 docker pull registry docker pull hyper/docker-registry-web没有网络可以使用我导出好的离线镜像tar包, 下载地址https://wwzt.lanzoul.com/i3im1194z12d …

积分值和面积、对称性

积分的基本含义要从积分符号说起,积分号含有加号的意思, ∫ a b f ( x ) d x \int ^b_af(x)dx ∫ab​f(x)dx可以理解为:区间[a,b]无限细分为无穷多个dx,无穷多个f(x)乘以dx的累积和。根据上面的描述,面积可以理解为 ∫ a b ∣ f (…