跳到主要内容
Java 代理模式完全指南:从静态到动态 | 极客日志
Java java
Java 代理模式完全指南:从静态到动态 综述由AI生成 深入讲解 Java 代理模式,涵盖静态代理与动态代理的核心原理及实现。静态代理需手动编写代理类,耦合度高;动态代理在运行时生成字节码,分为基于接口的 JDK 动态代理和基于继承的 CGLIB 动态代理。文章通过 PaymentService 和 DataQueryService 等完整代码示例,演示了 InvocationHandler 和 MethodInterceptor 的使用流程,对比了两种动态代理的实现差异、性能表现及适用场景,并给出了 Spring AOP 中的实际应用建议。掌握代理模式是理解 Spring AOP、RPC 框架的基础。
剑仙 发布于 2026/3/28 更新于 2026/5/28 25 浏览一、理解代理模式本质
代理模式的核心思想是引入一个中间层 ——即代理对象,让它代替真实对象处理外部请求。这样做的好处是:无需改动原有代码,就能在目标方法执行前后插入额外逻辑 。
生活化比喻:想象你是一位明星(真实对象),所有粉丝来信都由经纪人(代理对象)先过目筛选。经纪人可以在转交信件前进行内容审查、分类整理,甚至直接拒绝某些来信,而你只需要处理经过筛选后的邮件。
代理模式的价值:
解耦:调用方无需直接依赖目标对象
增强:在不修改源码的前提下扩展功能
控制:对访问权限、执行流程进行管控
二、静态代理:手动编码的代理方式
2.1 核心特点
静态代理要求开发者手工编写代理类 ,在编译阶段代理类的代码就已完全确定。从 JVM 视角看,编译后会产出三个独立的 .class 文件:接口、实现类、代理类。
主要缺陷:
高度耦合:接口每新增一个方法,代理类必须同步修改
重复劳动:N 个目标类需要编写 N 个代理类
维护困难:业务复杂时代码量呈爆炸式增长
2.2 完整实现示例
步骤一:定义业务接口
public interface MessageService {
String deliver (String content) ;
}
步骤二:实现真实业务
public class MessageServiceImpl implements MessageService {
@Override
public String deliver (String content) {
System.out.println("正在发送消息:" + content);
return content;
}
}
步骤三:手工编写代理类
public class MessageServiceProxy implements MessageService {
private final MessageService realService;
public MessageServiceProxy (MessageService service) {
.realService = service;
}
String {
System.out.println( + content.length());
realService.deliver(content);
System.out.println( + result);
result;
}
}
this
@Override
public
deliver
(String content)
"[代理] 开始处理消息,内容长度:"
String
result
=
"[代理] 消息处理完成,返回结果:"
return
public class Client {
public static void main (String[] args) {
MessageService real = new MessageServiceImpl ();
MessageService proxy = new MessageServiceProxy (real);
proxy.deliver("Hello, Proxy Pattern!" );
}
}
[代理] 开始处理消息,内容长度:22
正在发送消息:Hello, Proxy Pattern !
[代理] 消息处理完成,返回结果:Hello, Proxy Pattern !
三、动态代理:运行时生成代理类 动态代理将代理类的生成时机推迟到程序运行阶段 ,JVM 会在内存中动态构建代理类的字节码并加载。这彻底解决了静态代理"一个目标类对应一个代理类"的痛点。
Spring AOP 的底层基石
RPC 框架(如 Dubbo)的核心实现
事务管理、日志记录、权限校验的通用解决方案
Java 生态中主流的动态代理技术分为 JDK 内置实现 和 CGLIB 第三方库 两种。
四、JDK 动态代理:官方标准方案
4.1 技术架构 组件 类型 职责 java.lang.reflect.Proxy类 工厂类,负责生成代理对象 java.lang.reflect.InvocationHandler接口 回调接口,定义方法拦截逻辑
Proxy.newProxyInstance() 方法签名:
public static Object newProxyInstance (
ClassLoader loader, // 类加载器:用于加载动态生成的代理类
Class<?>[] interfaces, // 接口数组:代理类需要实现的接口列表
InvocationHandler h // 处理器:方法调用的实际处理者
)
InvocationHandler.invoke() 方法:
public Object invoke (
Object proxy, // 代理对象实例(慎用,避免循环调用)
Method method, // 被调用的方法反射对象
Object[] args // 方法参数数组
)
4.2 实现流程
public interface PaymentService {
boolean pay (String orderId, BigDecimal amount) ;
}
public class PaymentServiceImpl implements PaymentService {
@Override
public boolean pay (String orderId, BigDecimal amount) {
System.out.printf("执行支付:订单 [%s], 金额 [¥%s]%n" , orderId, amount);
return true ;
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
public class PerformanceInvocationHandler implements InvocationHandler {
private final Object targetObject;
public PerformanceInvocationHandler (Object target) {
this .targetObject = target;
}
@Override
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
System.out.printf("[监控] 方法:%s, 参数:%s%n" , method.getName(), Arrays.toString(args));
Object result = method.invoke(targetObject, args);
long cost = System.currentTimeMillis() - startTime;
System.out.printf("[监控] 执行耗时:%dms, 返回值:%s%n" , cost, result);
return result;
}
}
import java.lang.reflect.Proxy;
public class ServiceProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy (T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new PerformanceInvocationHandler (target)
);
}
}
public class Application {
public static void main (String[] args) {
PaymentService realService = new PaymentServiceImpl ();
PaymentService proxy = ServiceProxyFactory.createProxy(realService);
proxy.pay("ORD-2024-001" , new BigDecimal ("199.99" ));
}
}
[监控] 方法:pay, 参数:[ORD-2024-001, 199.99]
执行支付:订单 [ORD-2024-001] , 金额 [¥199.99]
[监控] 执行耗时:12ms , 返回值:true
五、CGLIB 动态代理:无接口的继承方案
5.1 技术背景 JDK 动态代理有一个先天限制:只能代理实现了接口的类 。对于没有接口的遗留类或第三方类,CGLIB(Code Generation Library)提供了基于继承 的解决方案。
在运行时动态生成目标类的子类
通过方法重写(Override)插入增强逻辑
使用 ASM 字节码操作库生成类
组件 类型 职责 net.sf.cglib.proxy.Enhancer类 配置并生成代理类 net.sf.cglib.proxy.MethodInterceptor接口 定义方法拦截逻辑 net.sf.cglib.proxy.MethodProxy类 高性能方法调用(避免反射)
5.2 依赖配置 <dependency >
<groupId > cglib</groupId >
<artifactId > cglib</artifactId >
<version > 3.3.0</version >
</dependency >
5.3 完整实现 public class DataQueryService {
public String queryFromDatabase (String sql) {
System.out.println("执行数据库查询:" + sql);
try {
Thread.sleep(100 );
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Result of: " + sql;
}
public final String querySystemConfig (String key) {
return "SystemConfig:" + key;
}
}
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class CacheMethodInterceptor implements MethodInterceptor {
private final Map<String, Object> cache = new HashMap <>();
@Override
public Object intercept (Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String cacheKey = method.getName() + ":" + args[0 ];
if (cache.containsKey(cacheKey)) {
System.out.println("[缓存] 命中:" + cacheKey);
return cache.get(cacheKey);
}
System.out.println("[缓存] 未命中,执行真实方法:" + method.getName());
Object result = proxy.invokeSuper(obj, args);
cache.put(cacheKey, result);
System.out.println("[缓存] 已存储:" + cacheKey);
return result;
}
}
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyGenerator {
@SuppressWarnings("unchecked")
public static <T> T createProxy (Class<T> targetClass) {
Enhancer enhancer = new Enhancer ();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(new CacheMethodInterceptor ());
return (T) enhancer.create();
}
}
public class CglibDemo {
public static void main (String[] args) {
DataQueryService proxy = CglibProxyGenerator.createProxy(DataQueryService.class);
String result1 = proxy.queryFromDatabase("SELECT * FROM users" );
System.out.println("结果 1: " + result1);
System.out.println("---" );
String result2 = proxy.queryFromDatabase("SELECT * FROM users" );
System.out.println("结果 2: " + result2);
System.out.println("---" );
String config = proxy.querySystemConfig("timeout" );
System.out.println("配置:" + config);
}
}
[缓存 ] 未命中,执行真实方法:queryFromDatabase
执行数据库查询:SELECT * FROM users
[缓存 ] 已存储:queryFromDatabase:SELECT * FROM users
结果 1: Result of: SELECT * FROM users
---
[缓存 ] 命中:queryFromDatabase:SELECT * FROM users
结果 2: Result of: SELECT * FROM users
---
配置:SystemConfig:timeout
重要提示: MethodProxy.invokeSuper() 是 CGLIB 的关键,它通过 FastClass 机制直接调用父类方法,避免了反射的性能损耗。如果误用 method.invoke(obj, args) 会导致循环调用(因为 obj 是代理对象,会再次触发拦截器)。
六、两种动态代理深度对比
6.1 技术实现差异 对比维度 JDK 动态代理 CGLIB 动态代理 实现原理 实现接口(生成接口的实现类) 继承类(生成目标类的子类) 强制要求 目标类必须实现接口 目标类不能是 final 类 方法限制 接口定义的所有方法 非 final、非 private 方法 核心类 Proxy、InvocationHandlerEnhancer、MethodInterceptor方法调用 Java 反射(相对较慢) FastClass 机制(方法索引,更快) 包归属 JDK 内置(java.lang.reflect) 第三方库(net.sf.cglib) 生成类名 com.sun.proxy.$Proxy0Target$$EnhancerByCGLIB$$hash
6.2 性能与适用场景
JDK 动态代理 :随着 JDK 版本升级(尤其是 JDK 8 之后),反射性能已大幅优化,在大多数场景下与 CGLIB 差距不大
CGLIB :首次生成代理类较慢(需要生成字节码),但调用阶段通过 FastClass 机制略快
if (目标类实现了接口) {
} else {
}
@EnableAspectJAutoProxy(proxyTargetClass = true)
七、静态代理与动态代理全局对比 特性 静态代理 动态代理 代理类生成时机 编译期(预先编写 .java 文件) 运行期(内存中生成字节码) 实现方式 手动编写代理类,需与目标类实现同一接口,一对一绑定 无需手动编写代理类,通过 Handler/Interceptor 封装增强逻辑,一对多复用 代码维护成本 高(接口变更需同步修改代理类) 低(与接口解耦,逻辑复用) 灵活性 低(一对一绑定) 高(一个处理器服务多个目标类) 编码工作量 代码量大(目标类越多,代理类越多),维护成本高;接口新增方法时,目标类与代理类需同步修改 代码量极少(通用增强逻辑可复用),维护性好;与接口解耦,接口变更不影响代理逻辑 学习曲线 平缓(纯 Java 语法) 较陡(需理解反射/字节码) 典型应用 设计模式学习、简单装饰场景 框架开发(AOP、RPC、ORM)
八、总结与实践建议
8.1 知识要点回顾
代理模式本质 :通过中间层控制访问,实现功能增强
静态代理 :手工编码,编译期确定,适合简单固定场景
JDK 动态代理 :基于接口,使用 Proxy + InvocationHandler,官方标准方案
CGLIB 动态代理 :基于继承,使用 Enhancer + MethodInterceptor,解决无接口类的代理问题
8.2 实际开发建议
日常业务开发 :直接使用 Spring AOP(底层已封装两种代理,自动选择)
框架工具开发 :根据目标类特性选择 JDK 或 CGLIB 原生 API
性能敏感场景 :考虑使用字节码生成库(如 ByteBuddy、Javassist)获得更细粒度控制
调试技巧 :设置系统属性保存生成的代理类文件,便于分析排查问题
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" );
System.setProperty("cglib.debugLocation" , "/path/to/output" );
掌握代理模式不仅是理解 Spring AOP、MyBatis、Dubbo 等框架的基础,更是写出高扩展性、低耦合代码的重要技能。建议通过手写 RPC 框架或简易 AOP 来加深理解。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online