菜单

Administrator
发布于 2026-05-18 / 1 阅读
0
0

Java 动态代理

Java 动态代理完全指南

目录

  1. 什么是代理模式
  2. 静态代理 vs 动态代理
  3. JDK 动态代理
  4. CGLIB 动态代理
  5. JDK 动态代理 vs CGLIB 对比
  6. AOP 原理与实践
  7. 实战案例详解
  8. 常见问题与注意事项
  9. 总结

什么是代理模式

代理模式(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 提供了两种主流的动态代理实现方式:

  1. JDK 动态代理 —— 基于接口,使用 java.lang.reflect.Proxy
  2. CGLIB 动态代理 —— 基于类继承,使用字节码增强技术

JDK 动态代理

核心原理

JDK 动态代理通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现。其核心机制是:

  1. 在运行时动态创建实现了指定接口列表的代理类字节码
  2. 代理类持有 InvocationHandler 引用
  3. 所有接口方法调用都会被转发到 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)具有以下特征:

  1. 继承 java.lang.reflect.Proxy,因此不能继承其他类
  2. 实现指定的所有接口
  3. 构造函数接收 InvocationHandler 参数
  4. 所有接口方法调用都委托给 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 的核心类是 EnhancerMethodInterceptor

// 方法拦截器
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

生成的代理类中:

  • 重写所有非 finalpublicprotected 方法
  • 在重写的方法中调用 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 中的 BeanGeneratorMethodProxy

使用 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 仍然是主流选择。


总结

  1. 代理模式是 Java 中实现方法增强的核心设计模式,静态代理在大型系统中难以维护。

  2. JDK 动态代理基于接口实现,内置在 JDK 中,无需额外依赖。通过 Proxy.newProxyInstance() 创建代理对象,所有方法调用委托给 InvocationHandler.invoke()

  3. CGLIB 动态代理基于类继承实现,可以代理没有接口的类。通过字节码技术生成子类,性能优异但需要额外依赖。

  4. 选择原则

    • 目标有接口 → 优先使用 JDK 动态代理(无额外依赖)
    • 目标无接口 / 需要代理具体类 → 使用 CGLIB
  5. AOP 的核心实现就是动态代理。Spring AOP 根据目标类是否实现接口自动选择代理方式,将日志、事务、安全等横切关注点从业务代码中剥离。

  6. 实际开发中,我们通常不需要手动编写动态代理代码,而是使用 Spring AOP、AspectJ 等成熟的框架。但理解其底层原理对于诊断问题、优化性能、深入理解框架至关重要。


“动态代理是 Java 反射机制最强大的应用之一,也是理解 Spring 等主流框架底层原理的钥匙。”


评论