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

news/2025/2/13 3:14:19/

🏆本文收录于「滚雪球学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.ppmy.cn/news/1117755.html

相关文章

Linux开发工具之编辑器-vim

vim简单来说就是一款文本编辑器,用于写代码,更是一款多模式编辑器 vim的基本概念 vim有许多种模式,但是铁三角是以下三种模式:命令模式,插入模式,底行模式 1 正常/普通/命令模式(默认打开&…

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

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

深入理解JVM虚拟机第九篇:详细介绍JVM中的双亲委派机制工作原理和机制

文章目录 一:双亲委派机制 1:工作原理 2:代码展示 3:异常代码展示

拓扑关系如何管理?

在设备对接涂鸦的云端过程中,一部分设备由于自身资源或硬件配置,无法直接连接云端。而是需要通过网关进行中转,由网关代理实现和云端进行数据交互,间接实现设备接入云端。这样的设备也称为子设备。 要想实现网关代理子设备接入云…

网络基础面试题

1. ISO/OSI的七层模型 ISO国际标准化组织 OSI开放系统互连 TCP和UDP都会进行差错校验,TCP会告诉A包发错了,但UDP不会告诉A发错了会把包丢弃。 静态路由不需要路由器做任何的计算,对路由器的消耗是最小的,效率最高但是缺点是…

无涯教程-JavaScript - ASIN函数

描述 ASIN函数返回给定数字的反正弦或反正弦,并返回以弧度表示的Angular,介于-π/2和π/2之间。 语法 ASIN (number)争论 Argument描述Required/OptionalNumberThe sine of the angle you want and must be from -1 to 1.Required Notes 如果您希望ASIN函数返回的Angular以…

2054. 两个最好的不重叠活动;1255. 得分最高的单词集合;858. 镜面反射

2054. 两个最好的不重叠活动 核心思想:枚举小堆。因为你最多可以参加两个时间不重叠活动,所以我们就枚举其中一个活动,用一个堆来维护右边界的最小值,因为我们的event是排序的,前面满足的max_r_v,后面的event也肯定满…

JRedis的基本操作,基本数据类型操作

Redis的基本数据类型: stringhashlistsetzset {public static void main(String[] args) {Jedis jedis new Jedis("127.0.0.1", 6379);// stringjedis.set("hello", "word");String hello jedis.get("hello");System.o…