Java 动态代理完全指南
目录
什么是代理模式
代理模式(Proxy Pattern)是 GoF 23 种经典设计模式之一。其核心思想是:为另一个对象提供一个替身或占位符以控制对这个对象的访问。
在代理模式中,我们创建具有原始对象相同功能的新对象(即代理对象),客户端通过代理对象间接访问真实对象,从而可以在不修改原始对象的前提下,扩展或控制其行为。
// 代理模式的核心角色
// 1. 抽象主题(Subject)—— 定义真实对象和代理对象的共同接口
// 2. 真实主题(RealSubject)—— 实际执行业务逻辑的对象
// 3. 代理(Proxy)—— 持有真实主题的引用,控制对其访问
静态代理 vs 动态代理
静态代理
静态代理在编译期就确定了代理关系,每个代理类都需要手动编写。
// 接口
public interface UserService {
void saveUser(String name);
void deleteUser(Long id);
}
// 真实对象
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String name) {
System.out.println("保存用户: " + name);
}
@Override
public void deleteUser(Long id) {
System.out.println("删除用户: " + id);
}
}
// 静态代理类 —— 需要手动编写,且必须实现相同接口
public class UserServiceStaticProxy implements UserService {
private final UserService target;
public UserServiceStaticProxy(UserService target) {
this.target = target;
}
@Override
public void saveUser(String name) {
System.out.println("[日志] 开始保存用户...");
target.saveUser(name);
System.out.println("[日志] 保存用户完成");
}
@Override
public void deleteUser(Long id) {
System.out.println("[日志] 开始删除用户...");
target.deleteUser(id);
System.out.println("[日志] 删除用户完成");
}
}
静态代理的痛点:
- 每个类都需要手动编写代理类,代码大量重复
- 如果接口新增方法,真实对象和代理类都需要同步修改
- 当系统规模扩大时,代理类的数量会爆炸式增长
动态代理
动态代理在运行期动态生成代理对象,无需为每个类手动编写代理类。Java 提供了两种主流的动态代理实现方式:
- JDK 动态代理 —— 基于接口,使用
java.lang.reflect.Proxy - CGLIB 动态代理 —— 基于类继承,使用字节码增强技术
JDK 动态代理
核心原理
JDK 动态代理通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现。其核心机制是:
- 在运行时动态创建实现了指定接口列表的代理类字节码
- 代理类持有
InvocationHandler引用 - 所有接口方法调用都会被转发到
InvocationHandler.invoke()方法
核心 API
InvocationHandler 接口
public interface InvocationHandler {
/**
* @param proxy 代理对象本身
* @param method 被调用的方法
* @param args 方法参数
* @return 方法调用返回值
*/
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
Proxy 类
public class Proxy {
// 核心静态方法:创建代理对象
public static Object newProxyInstance(
ClassLoader loader, // 类加载器
Class<?>[] interfaces, // 要实现的接口列表
InvocationHandler h // 调用处理器
);
}
完整示例
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 1. 定义业务接口
public interface Calculator {
int add(int a, int b);
int subtract(int a, int b);
}
// 2. 真实实现
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
System.out.println(" 执行加法: " + a + " + " + b);
return a + b;
}
@Override
public int subtract(int a, int b) {
System.out.println(" 执行减法: " + a + " - " + b);
return a - b;
}
}
// 3. 实现 InvocationHandler —— 日志增强
public class LogInvocationHandler implements InvocationHandler {
private final Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("[JDK代理] 调用方法: " + method.getName()
+ ",参数: " + java.util.Arrays.toString(args));
long start = System.nanoTime();
Object result = method.invoke(target, args);
long elapsed = System.nanoTime() - start;
System.out.println("[JDK代理] 方法结束: " + method.getName()
+ ",返回值: " + result + ",耗时: " + elapsed / 1_000_000 + "ms");
return result;
}
}
// 4. 客户端使用
public class Client {
public static void main(String[] args) {
// 保存生成的代理类字节码文件,便于调试
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Calculator target = new CalculatorImpl();
LogInvocationHandler handler = new LogInvocationHandler(target);
// 创建代理对象
Calculator proxy = (Calculator) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
System.out.println("代理对象 class: " + proxy.getClass().getName());
System.out.println("是否为代理类: " + Proxy.isProxyClass(proxy.getClass()));
// 通过代理调用方法
System.out.println("\n--- 调用 add ---");
int sum = proxy.add(10, 20);
System.out.println("结果: " + sum);
System.out.println("\n--- 调用 subtract ---");
int diff = proxy.subtract(50, 15);
System.out.println("结果: " + diff);
}
}
输出结果:
代理对象 class: com.sun.proxy.$Proxy0
是否为代理类: true
--- 调用 add ---
[JDK代理] 调用方法: add,参数: [10, 20]
执行加法: 10 + 20
[JDK代理] 方法结束: add,返回值: 30,耗时: 0ms
结果: 30
--- 调用 subtract ---
[JDK代理] 调用方法: subtract,参数: [50, 15]
执行减法: 50 - 15
[JDK代理] 方法结束: subtract,返回值: 35,耗时: 0ms
结果: 35
JDK 动态代理的底层机制
JDK 动态代理生成的代理类(如 $Proxy0)具有以下特征:
- 继承
java.lang.reflect.Proxy,因此不能继承其他类 - 实现指定的所有接口
- 构造函数接收
InvocationHandler参数 - 所有接口方法调用都委托给
InvocationHandler.invoke()
// 这是动态生成的代理类的简化伪代码
public final class $Proxy0 extends Proxy implements Calculator {
private static Method m1, m2, m3, m4, m0; // Object 方法 + 接口方法
public $Proxy0(InvocationHandler h) {
super(h);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Object.class);
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("Calculator").getMethod("add", int.class, int.class);
m4 = Class.forName("Calculator").getMethod("subtract", int.class, int.class);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException e) { /* ... */ }
}
@Override
public final int add(int a, int b) {
try {
return (int) super.h.invoke(this, m3, new Object[]{a, b});
} catch (Throwable e) { throw new UndeclaredThrowableException(e); }
}
@Override
public final int subtract(int a, int b) {
try {
return (int) super.h.invoke(this, m4, new Object[]{a, b});
} catch (Throwable e) { throw new UndeclaredThrowableException(e); }
}
// equals, hashCode, toString 也被重写并委托给 InvocationHandler
}
JDK 动态代理的局限
- 必须基于接口:被代理的类必须实现至少一个接口
- 无法代理类中的非接口方法:只能代理接口中声明的方法
- 性能相对较低(Java 8 之前):但由于 JDK 持续优化,现代 JDK 下的性能已经非常优秀
CGLIB 动态代理
什么是 CGLIB
CGLIB(Code Generation Library)是一个强大、高性能的字节码生成库。它通过继承目标类并动态生成子类来实现代理,因此可以代理没有接口的普通类。
Spring 等框架在目标类没有实现接口时,会自动回退到 CGLIB 代理。
核心 API
CGLIB 的核心类是 Enhancer 和 MethodInterceptor:
// 方法拦截器
public interface MethodInterceptor extends Callback {
Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}
// 增强器 —— 用于创建代理对象
public class Enhancer {
public void setSuperclass(Class<?> superclass); // 设置目标类
public void setCallback(Callback callback); // 设置回调
public void setCallbackFilter(CallbackFilter filter); // 设置回调过滤器
public Object create(); // 创建代理对象
}
完整示例
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 1. 目标类 —— 没有实现任何接口
public class OrderService {
public String createOrder(String product, int quantity) {
System.out.println(" 创建订单: " + product + " x " + quantity);
return "ORD-" + System.currentTimeMillis();
}
public void cancelOrder(String orderId) {
System.out.println(" 取消订单: " + orderId);
}
// final 方法 —— CGLIB 无法重写
public final void nonProxyMethod() {
System.out.println("这个方法不会被代理");
}
}
// 2. 实现 MethodInterceptor
public class CglibLogInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("[CGLIB代理] 调用方法: " + method.getName());
long start = System.nanoTime();
// 通过 MethodProxy 调用目标方法(注意:不能用 method.invoke,会出现无限递归)
Object result = proxy.invokeSuper(obj, args);
long elapsed = System.nanoTime() - start;
System.out.println("[CGLIB代理] 方法结束: " + method.getName()
+ ",耗时: " + elapsed / 1_000_000 + "ms");
return result;
}
}
// 3. 客户端使用
public class CglibClient {
public static void main(String[] args) {
System.setProperty("cglib.debugLocation", "/tmp/cglib_classes");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderService.class);
enhancer.setCallback(new CglibLogInterceptor());
OrderService proxy = (OrderService) enhancer.create();
System.out.println("代理对象 class: " + proxy.getClass().getName());
System.out.println("\n--- 创建订单 ---");
String orderId = proxy.createOrder("iPhone 15", 2);
System.out.println("订单号: " + orderId);
System.out.println("\n--- 取消订单 ---");
proxy.cancelOrder(orderId);
System.out.println("\n--- 调用 final 方法(不会被代理)---");
proxy.nonProxyMethod();
}
}
输出结果:
代理对象 class: com.example.OrderService$$EnhancerByCGLIB$$a1b2c3d4
--- 创建订单 ---
[CGLIB代理] 调用方法: createOrder
创建订单: iPhone 15 x 2
[CGLIB代理] 方法结束: createOrder,耗时: 3ms
订单号: ORD-1716112345678
--- 取消订单 ---
[CGLIB代理] 调用方法: cancelOrder
取消订单: ORD-1716112345678
[CGLIB代理] 方法结束: cancelOrder,耗时: 0ms
--- 调用 final 方法(不会被代理)---
这个方法不会被代理
CGLIB 的底层机制
CGLIB 通过字节码技术动态生成目标类的子类:
目标类: OrderService
|
| extends
↓
CGLIB 生成类: OrderService$$EnhancerByCGLIB$$xxx
生成的代理类中:
- 重写所有非 final 的
public和protected方法 - 在重写的方法中调用
MethodInterceptor.intercept() - 使用
FastClass机制加速方法调用(比标准 Java 反射快)
CGLIB 的局限
- 不能代理
final类(无法继承) - 不能代理
final方法(无法重写) - 不能代理
private方法(子类不可见) - 需要额外依赖
cglib.jar(或spring-core.jar内嵌的版本)
JDK 动态代理 vs CGLIB 对比
| 对比维度 | JDK 动态代理 | CGLIB 动态代理 |
|---|---|---|
| 实现方式 | 基于接口,实现 InvocationHandler |
基于类继承,使用字节码增强 |
| 依赖要求 | 目标类必须实现至少一个接口 | 目标类不能是 final,方法不能是 final |
| JDK 内置 | ✅ 是,无需额外依赖 | ❌ 需要引入 cglib 依赖 |
| 性能(创建阶段) | 较快 | 较慢(需生成字节码) |
| 性能(调用阶段) | 现代 JDK 下非常优秀 | 极高(FastClass 机制) |
| 方法限制 | 只能代理接口中的方法 | 可代理所有非 final / 非 private 方法 |
| 自省支持 | 通过 Proxy.isProxyClass() 可判断 |
需要额外检查 |
| 版本要求 | JDK 1.3+ 原生支持 | 需引入第三方库 |
| 适用场景 | 目标有接口定义 | 目标无接口或需要代理具体类 |
性能对比说明
在 JDK 8 之前,CGLIB 的调用性能显著优于 JDK 动态代理。但从 JDK 8 开始,Oracle 对 JDK 动态代理进行了大量优化(包括 lambda 形式的方法分派),两者在调用性能上的差距已微乎其微。
// 简单的性能测试思路
public class PerformanceBenchmark {
public static void main(String[] args) {
// 1. JDK 代理
Calculator jdkProxy = createJdkProxy();
// 2. CGLIB 代理
Calculator cglibProxy = createCglibProxy();
// 预热
for (int i = 0; i < 10000; i++) {
jdkProxy.add(1, 2);
cglibProxy.add(1, 2);
}
// 实际测试
long jdkTime = measure(jdkProxy);
long cglibTime = measure(cglibProxy);
System.out.println("JDK 代理耗时: " + jdkTime + "ns");
System.out.println("CGLIB 代理耗时: " + cglibTime + "ns");
}
}
AOP 原理与实践
什么是 AOP
AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,用于将横切关注点(cross-cutting concerns)从业务逻辑中分离出来。常见的横切关注点包括:日志记录、事务管理、权限检查、性能监控、缓存等。
AOP 的核心概念
| 概念 | 说明 | 类比 |
|---|---|---|
| Aspect(切面) | 横切关注点的模块化,通知+切点的组合 | 类似 Java 中的类 |
| Join Point(连接点) | 程序执行过程中可以织入通知的点 | 方法调用、异常抛出等 |
| Pointcut(切点) | 匹配连接点的表达式 | 筛选哪些方法需要增强 |
| Advice(通知) | 在特定连接点执行的动作 | Before, After, Around 等 |
| Target(目标对象) | 被代理的真实对象 | 业务类实例 |
| Weaving(织入) | 将切面应用到目标对象的过程 | 编译期、类加载期、运行期 |
动态代理在 AOP 中的角色
动态代理是 AOP 的核心实现技术。在运行时,AOP 框架通过动态代理将切面逻辑织入到目标对象中。
┌─────────────────────────────────────────────────┐
│ 客户端调用 │
└────────────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ 动态代理对象 (Proxy) │
│ ┌───────────────────────────────────────────┐ │
│ │ Before Advice (前置通知) │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ 真实对象方法调用 │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ │ After Returning Advice (返回通知) │ │
│ │ (或 After Throwing Advice 异常通知) │ │
│ └───────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
使用动态代理模拟 AOP
下面我们使用 JDK 动态代理实现一个简易的 AOP 框架:
import java.lang.annotation.*;
import java.lang.reflect.*;
// ---- 注解定义 ----
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Before {}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface AfterReturning {}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Around {}
// ---- 切面定义 ----
// 日志切面 —— 包含各种增强逻辑
public class LoggingAspect {
@Before
public void beforeLog(JoinPoint jp) {
System.out.println("[Before] 准备调用 " + jp.getMethod().getName());
}
@AfterReturning
public void afterLog(JoinPoint jp, Object result) {
System.out.println("[AfterReturning] " + jp.getMethod().getName()
+ " 返回: " + result);
}
@Around
public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[Around] 环绕开始: " + pjp.getMethod().getName());
long start = System.nanoTime();
Object result = pjp.proceed();
long elapsed = System.nanoTime() - start;
System.out.println("[Around] 环绕结束,耗时: " + elapsed / 1_000_000 + "ms");
return result;
}
}
// 连接点信息
class JoinPoint {
private final Method method;
private final Object target;
private final Object[] args;
public JoinPoint(Method method, Object target, Object[] args) {
this.method = method;
this.target = target;
this.args = args;
}
public Method getMethod() { return method; }
public Object getTarget() { return target; }
public Object[] getArgs() { return args; }
}
class ProceedingJoinPoint extends JoinPoint {
public ProceedingJoinPoint(Method method, Object target, Object[] args) {
super(method, target, args);
}
public Object proceed() throws Throwable {
return getMethod().invoke(getTarget(), getArgs());
}
}
// ---- 简易 AOP 代理工厂 ----
class SimpleAopFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target, Object aspect) {
// 解析切面中的通知方法
// ... 这里简化处理,直接使用 LoggingAspect
InvocationHandler handler = (proxy, method, args) -> {
LoggingAspect aspectInstance = (LoggingAspect) aspect;
// 构建连接点
JoinPoint jp = new JoinPoint(method, target, args);
// 查找 @Around 方法并执行环绕
Method aroundMethod = findAnnotatedMethod(aspectInstance.getClass(), Around.class);
if (aroundMethod != null) {
ProceedingJoinPoint pjp = new ProceedingJoinPoint(method, target, args);
return aroundMethod.invoke(aspectInstance, pjp);
}
// 查找 @Before 方法
Method beforeMethod = findAnnotatedMethod(aspectInstance.getClass(), Before.class);
if (beforeMethod != null) {
beforeMethod.invoke(aspectInstance, jp);
}
// 执行真实方法
Object result;
try {
result = method.invoke(target, args);
} catch (Exception e) {
throw e.getCause();
}
// 查找 @AfterReturning 方法
Method afterMethod = findAnnotatedMethod(aspectInstance.getClass(), AfterReturning.class);
if (afterMethod != null) {
afterMethod.invoke(aspectInstance, jp, result);
}
return result;
};
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
}
private static Method findAnnotatedMethod(Class<?> clazz, Class<? extends Annotation> annotation) {
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(annotation)) {
return method;
}
}
return null;
}
}
// ---- 测试 ----
public interface UserService {
void register(String username);
String login(String username, String password);
}
public class UserServiceImpl implements UserService {
@Override
public void register(String username) {
System.out.println(" 业务层: 注册用户 " + username);
}
@Override
public String login(String username, String password) {
System.out.println(" 业务层: 用户 " + username + " 登录");
return "token-" + username.hashCode();
}
}
public class AopDemo {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = SimpleAopFactory.createProxy(target, new LoggingAspect());
System.out.println("=== 注册用户 ===");
proxy.register("张三");
System.out.println("\n=== 用户登录 ===");
String token = proxy.login("张三", "123456");
System.out.println("Token: " + token);
}
}
Spring AOP 与动态代理
Spring AOP 是动态代理最经典的应用。Spring 底层根据目标对象是否实现接口,自动选择代理方式:
目标对象有无接口?
├── 有接口 ──→ JDK 动态代理(默认)
│
└── 无接口 ──→ CGLIB 动态代理(回退)
Spring 中的显式配置:
<!-- 强制使用 CGLIB -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Spring Boot application.properties -->
spring.aop.proxy-target-class=true
Spring 使用示例:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("[Spring AOP] 方法: " + joinPoint.getSignature().getName());
}
@Around("serviceLayer()")
public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long elapsed = System.currentTimeMillis() - start;
System.out.println("[Spring AOP] " + pjp.getSignature().getName() + " 耗时: " + elapsed + "ms");
return result;
}
@AfterReturning(value = "serviceLayer()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("[Spring AOP] 返回: " + result);
}
}
实战案例详解
案例一:通用日志记录框架
import java.lang.reflect.*;
import java.util.*;
// 日志级别
enum LogLevel { INFO, DEBUG, WARN, ERROR }
// 日志配置
class LogConfig {
boolean logParams = true;
boolean logResult = true;
boolean logDuration = true;
LogLevel level = LogLevel.INFO;
Set<String> excludeMethods = new HashSet<>();
public LogConfig excludeMethod(String method) {
excludeMethods.add(method);
return this;
}
}
// 通用日志代理
public class LogProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createLogProxy(T target, LogConfig config) {
InvocationHandler handler = (proxy, method, args) -> {
if (config.excludeMethods.contains(method.getName())) {
return method.invoke(target, args);
}
StringBuilder sb = new StringBuilder();
sb.append("[LOG-").append(config.level).append("] ");
sb.append(target.getClass().getSimpleName()).append(".");
sb.append(method.getName()).append("(");
if (config.logParams && args != null) {
sb.append(String.join(", ",
Arrays.stream(args).map(String::valueOf).toArray(String[]::new)));
}
sb.append(")");
long start = System.currentTimeMillis();
try {
Object result = method.invoke(target, args);
if (config.logResult) {
sb.append(" ⇒ ").append(result);
}
if (config.logDuration) {
sb.append(" [").append(System.currentTimeMillis() - start).append("ms]");
}
System.out.println(sb);
return result;
} catch (Exception e) {
sb.append(" ✗ ERROR: ").append(e.getCause().getMessage());
System.err.println(sb);
throw e.getCause();
}
};
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
}
}
// 使用示例
public class LogProxyDemo {
public static void main(String[] args) {
Calculator target = new CalculatorImpl();
LogConfig config = new LogConfig()
.excludeMethod("toString");
// 等价于 .excludeMethod("equals").excludeMethod("hashCode")
Calculator proxy = LogProxyFactory.createLogProxy(target, config);
proxy.add(5, 3);
proxy.subtract(10, 4);
// 被排除的方法不会输出日志
proxy.toString();
}
}
案例二:声明式事务代理(简化版)
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.sql.*;
// 事务注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Transactional {}
// 事务管理器
class TransactionManager {
private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();
public static Connection getConnection() throws SQLException {
Connection conn = connectionHolder.get();
if (conn == null) {
conn = DriverManager.getConnection("jdbc:h2:mem:testdb");
connectionHolder.set(conn);
}
return conn;
}
public static void beginTransaction() throws SQLException {
Connection conn = getConnection();
conn.setAutoCommit(false);
System.out.println("[事务] 开始事务");
}
public static void commit() throws SQLException {
getConnection().commit();
System.out.println("[事务] 提交事务");
}
public static void rollback() {
try {
getConnection().rollback();
System.out.println("[事务] 回滚事务");
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close() {
try {
getConnection().close();
connectionHolder.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 事务代理工厂
class TransactionProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createTransactionProxy(T target) {
InvocationHandler handler = (proxy, method, args) -> {
boolean hasTransaction = method.isAnnotationPresent(Transactional.class);
if (hasTransaction) {
TransactionManager.beginTransaction();
}
try {
Object result = method.invoke(target, args);
if (hasTransaction) {
TransactionManager.commit();
}
return result;
} catch (Exception e) {
if (hasTransaction) {
TransactionManager.rollback();
}
throw e.getCause();
} finally {
if (hasTransaction) {
TransactionManager.close();
}
}
};
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
}
}
// 业务接口
interface AccountService {
void transfer(String from, String to, double amount);
double getBalance(String account);
}
// 业务实现
class AccountServiceImpl implements AccountService {
@Override
@Transactional
public void transfer(String from, String to, double amount) {
System.out.println(" 转账: " + from + " → " + to + " 金额: " + amount);
// 模拟数据库操作
// debit(from, amount);
// credit(to, amount);
}
@Override
public double getBalance(String account) {
System.out.println(" 查询余额: " + account);
return 1000.0;
}
}
public class TransactionDemo {
public static void main(String[] args) {
AccountService proxy = TransactionProxyFactory
.createTransactionProxy(new AccountServiceImpl());
System.out.println("=== 转账操作 ===");
proxy.transfer("Alice", "Bob", 500.0);
System.out.println("\n=== 查询操作(无事务)===");
double balance = proxy.getBalance("Alice");
System.out.println("余额: " + balance);
}
}
案例三:重试机制代理
import java.lang.annotation.*;
import java.lang.reflect.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Retryable {
int maxAttempts() default 3;
long delayMs() default 100;
}
public class RetryProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createRetryProxy(T target) {
InvocationHandler handler = (proxy, method, args) -> {
Retryable retry = method.getAnnotation(Retryable.class);
if (retry == null) {
return method.invoke(target, args);
}
int maxAttempts = retry.maxAttempts();
long delayMs = retry.delayMs();
Throwable lastException = null;
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return method.invoke(target, args);
} catch (Exception e) {
lastException = e.getCause();
System.err.println("[重试] " + method.getName()
+ " 第" + attempt + "次失败: " + lastException.getMessage());
if (attempt < maxAttempts) {
Thread.sleep(delayMs * attempt); // 指数退避
}
}
}
throw new RuntimeException("重试" + maxAttempts + "次均失败",
lastException);
};
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
}
}
// 使用
interface PaymentService {
boolean pay(String orderId, double amount);
}
class UnstablePaymentService implements PaymentService {
private int callCount = 0;
@Override
@Retryable(maxAttempts = 3, delayMs = 200)
public boolean pay(String orderId, double amount) {
callCount++;
if (callCount < 3) {
throw new RuntimeException("网络异常,超时未响应");
}
System.out.println(" 支付成功: " + orderId + " " + amount);
return true;
}
}
public class RetryDemo {
public static void main(String[] args) {
PaymentService proxy = RetryProxyFactory
.createRetryProxy(new UnstablePaymentService());
proxy.pay("ORD-001", 2999.0);
}
}
常见问题与注意事项
1. ClassCastException 无法转换
原因:JDK 动态代理要求目标类实现接口,代理对象只能转换为接口类型。
// ❌ 错误用法
CalculatorImpl impl = new CalculatorImpl();
CalculatorImpl proxy = (CalculatorImpl) Proxy.newProxyInstance(
impl.getClass().getClassLoader(),
impl.getClass().getInterfaces(),
handler
);
// 抛出 ClassCastException
// ✅ 正确用法
Calculator proxy = (Calculator) Proxy.newProxyInstance(
impl.getClass().getClassLoader(),
impl.getClass().getInterfaces(),
handler
);
2. InvocationHandler 中的无限递归
// ❌ 错误:在 invoke 中调用 proxy 的方法
InvocationHandler handler = (proxy, method, args) -> {
System.out.println(proxy.toString()); // 递归调用!
return method.invoke(target, args);
};
// ✅ 正确:通过 target 调用
InvocationHandler handler = (proxy, method, args) -> {
System.out.println(target.toString()); // 直接调用目标对象
return method.invoke(target, args);
};
3. equals / hashCode / toString 的代理行为
JDK 动态代理会将这些方法也转发给 InvocationHandler。如果需要比较代理对象本身,需要特殊处理:
InvocationHandler handler = (proxy, method, args) -> {
// 对于 Object 方法,可以选择直接转发到 InvocationHandler 的默认实现
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args); // 调用 handler 自身的 equals/hashCode
}
return method.invoke(target, args);
};
4. CGLIB 中的 BeanGenerator 和 MethodProxy
使用 CGLIB 时,intercept 方法中必须使用 proxy.invokeSuper() 而非 method.invoke(),否则会导致无限递归。
// ✅ 正确
Object result = proxy.invokeSuper(obj, args);
// ❌ 错误 —— 导致无限递归
Object result = method.invoke(obj, args);
// obj 是代理对象,method 又是被拦截的方法,会再次进入 intercept
5. Spring Boot 2.x+ 默认使用 CGLIB
从 Spring Boot 2.0 开始,Spring 默认使用 CGLIB 代理(即使目标类实现了接口),无需额外配置 proxy-target-class=true。
6. 现代 Java 的替代方案
Java 17+ 提供了 java.lang.reflect.InvocationHandler 的替代方案:
- MethodHandle(Java 7+):更轻量的方法调用
- java.lang.reflect.code(实验性,Java 22+):新一代反射 API
但在日常业务开发中,JDK 动态代理和 CGLIB 仍然是主流选择。
总结
-
代理模式是 Java 中实现方法增强的核心设计模式,静态代理在大型系统中难以维护。
-
JDK 动态代理基于接口实现,内置在 JDK 中,无需额外依赖。通过
Proxy.newProxyInstance()创建代理对象,所有方法调用委托给InvocationHandler.invoke()。 -
CGLIB 动态代理基于类继承实现,可以代理没有接口的类。通过字节码技术生成子类,性能优异但需要额外依赖。
-
选择原则:
- 目标有接口 → 优先使用 JDK 动态代理(无额外依赖)
- 目标无接口 / 需要代理具体类 → 使用 CGLIB
-
AOP 的核心实现就是动态代理。Spring AOP 根据目标类是否实现接口自动选择代理方式,将日志、事务、安全等横切关注点从业务代码中剥离。
-
实际开发中,我们通常不需要手动编写动态代理代码,而是使用 Spring AOP、AspectJ 等成熟的框架。但理解其底层原理对于诊断问题、优化性能、深入理解框架至关重要。
“动态代理是 Java 反射机制最强大的应用之一,也是理解 Spring 等主流框架底层原理的钥匙。”