☰
01-JDK动态代理
2026/10/11 1:53:02 网站建设 项目流程

1、定义

JDK动态代理是Java基于接口,在运行期间动态生成代理类字节码,创建代理对象,对目标对象方法做增强的技术。
核心限制:目标类必须实现接口,只能代理接口里定义的方法。

2、基础使用

2.1、处理流程

使用Proxy创建代理类,调用newProxyInstance方法创建代理对象;
使用InvocationHandler调用处理器接口,代理对象执行任何方式时,都会回调这个接口的invoke()方法;
完整流程:
1、目标对象必须实现接口
2、自定义类实现InvocationHandler,持有目标对象引用,在Invoke方法中有三个参数 proxy代理对象、 method被调用方法, args被调用方法的入参;内部可以在目标方法调用前后进行前置增加、后置增加、异常捕获等
3、通过Proxy.newProxyInstance(classLoader, 接口数组, InvovationHandler)
4、运行期动态生成代理类字节码,内存中生成class,不产生磁盘.class文件,ClassLoader加载这个代理类。
5、返回代理对象;调用代理对象接口方法时,不会执行自身逻辑,直接转发到InvocationHandler.invoker()方法

重点:生成的代理类$Proxy0实现了目标的接口,继承自Proxy。Java是单继承,所以JDK代理不能代理没有接口的类。

2.2、代码实现

远程代码https://gitee.com/huyanqiu6666/design-pattern-01/
包名com.hyq.a.jdkproxy

2.2.1、抽象主题

定义统一的业务接口,真实对象,代理对象都需要实现该接口

package com.hyq.common; public interface IUserService { /** * 添加用户 * @param name 用户名称 * @param age 用户年龄 */ void addUser(String name, int age); }

2.2.2、真实主题

被代理的实际业务,实现真正的逻辑

package com.hyq.common; public class UserServiceImpl implements IUserService{ @Override public void addUser(String name, int age) { System.out.println("真实操作:addUser success " + name + " "+ age); } }

2.2.3、回调处理器InvocationHandler

内部持有真实目标对象,在这里编写前置、后置等增强逻辑,在通过反射委托调用真实对象的方法

package com.hyq.a.jdkproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * InvocationHandler 回调处理器 */ public class MyInvocationHandler implements InvocationHandler { // 持有真实目标对象 private Object target; public MyInvocationHandler(Object target) { this.target = target; } /** * 执行的方法 * @param proxy 生成的代理对象 * @param method 当前执行的方法 * @param args 方法入参 * @return 目标方法返回值 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("[JDK代理] 方法执行前,记录日志"); // 执行真实目标方法 Object result = method.invoke(target, args); System.out.println("[JDK代理] 方法执行后,记录日志"); return result; } }

2.2.4、代理(Proxy)

实现业务接口,内部持有回调处理器InvocationHandler,接收外部方法调用,不执行业务,直接转发给回调处理器

package com.hyq.a.jdkproxy; import com.hyq.common.IUserService; import com.hyq.common.UserServiceImpl; import java.lang.reflect.Proxy; /** * 01、jdk动态代理 */ public class Main { public static void main(String[] args) { // 创建目标对象 UserServiceImpl userService = new UserServiceImpl(); // 创建代理对象 IUserService userServiceProxy = (IUserService)Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), // 类加载器 UserServiceImpl.class.getInterfaces(), // 目标实现的接口 new MyInvocationHandler(userService)); // 回调处理-最终会调用到MyInvocationHandler.invoke // 调用目标方法加强 userServiceProxy.addUser("Bonnie", 18); System.out.println("代理类名称:"+ userServiceProxy.getClass().getName()); /** * 输出结果: * [JDK代理] 方法执行前,记录日志 * 真实操作:addUser successs Bonnie 18 * [JDK代理] 方法执行后,记录日志 * 代理类名称:com.sun.proxy.$Proxy0 */ } }

输出结果:

[JDK代理] 方法执行前,记录日志
真实操作:addUser success Bonnie 18
[JDK代理] 方法执行后,记录日志
代理类名称:com.sun.proxy.$Proxy0

2.3、查看运行过程中代理类

2.3.1、增加运行参数

运行参数中增加以下:

-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

增加的位置:

执行完成后生成对应的文件放在项目中:

2.3.2、代理类的代码

代理类会继承Proxy并且实现抽象主题,Proxy中持有InvocationHandler引用,从而在使用代理类调用目标方法的时候,将请求转发到自定义的InvocationHandler中,进而在不修改原代码的情况下增加目标方法。

注:这些都可以下载上面的代码,原代码中都可以查看到

package model.proxy.dyna; import model.proxy.IUserService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; /** * 生成的动态代理类(运行时动态生成) */ public class $Proxy0 extends Proxy implements IUserService { /** * static静态块提前缓存Method对象 */ // equals private static Method m1; // toString private static Method m2; // addUser 业务方法 private static Method m3; // hashCode private static Method m0; /** * 类加载时执行,预加载所有Method,缓存为静态变量 */ static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("model.proxy.IUserService").getMethod("addUser", Class.forName("java.lang.String"), Integer.TYPE); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(((Throwable)var2).getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(((Throwable)var3).getMessage()); } } /** * 构造方法, 接收InvocationHandler,传递给父类的Proxy * @param var1 */ public $Proxy0(InvocationHandler var1) { super(var1); } public final boolean equals(Object var1) { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void addUser(String var1, int var2) { try { // 全部转发给:MyInvocationHandler.invoker // 参数一:this=代理对象$Proxy0 // 参数二:m3=addUser方法 // 参数三:方法参数数组 super.h.invoke(this, m3, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final int hashCode() { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } }

3、总结

定义:为目标对象提供一个代理对象,由代理对象控制目标对象的访问,在不修改目标对象的情况下,对原方法做增强或控制。

职责划分四个角色:

1、抽象主题(接口):定义统一的业务接口,真实对象、代理对象都需要实现该接口 ​ 2、真实主题(实现类):被代理的实际业务,实现真正的业务逻辑 ​ 3、代理(Proxy):实现业务接口,内部持有回调处理器InvocationHandler;接收外部方法调用,不执行业务,直接转发给回调处理器 ​ 4、回调处理器InvocationHandler:内部持有真实目标对象,在这里编写前置、后置等增强逻辑,再通过反射委托调用真实对象的方 ​ 法

重点类API/类: 1、Proxy:JDK提供的工具类,动态代理入口,负责生成代理字节码,创建代理实例 2、Proxy.newProxyInstance:静态方法,核心入口。运行时动态生成代理类class,代理实现业务接口,内部持有InvocationHandler 回调处理器。 3、InvocationHandler:回调接口;代理对象收到方法调用,就会回调它的invoke(),完成增强,再委托给真实目标对象执行原始逻辑

调用顺序:1、创建动态代理对象:Proxy.newProxyInstance,持有回调处理器并转发处理 2、调用动态类的目标方法:proxy.addUser()首先代理类会拦截原始方法调用,把调用转发给回调处理器进行加强,在委托给真实对象

优点:

1、JDK原生内置,不需要引入第三方依赖 ​ 2、使用简单,API清晰 ​ 3、面向接口,符合面向接口编程思想 ​ 4、代理对序列化支持比较友好,$Proxy0继承Proxy实现Serializable ​ 5、字节码由JVM直接生成、加载、兼容性好

缺点:1、强制要求目标实现接口,不能代理普通类 2、目标方法执行依赖反射method.invoke(),存在反射性能开销 3、调试不友好,$Proxy0是运行时动态生成类,IDE看不到源码,排错需要开启JVM参数输出class文件

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询