Spring动态代理详解

Spring动态代理详解:从原理到实战

前言

动态代理是Spring框架实现AOP、事务管理、声明式缓存等核心功能的基石。理解动态代理的原理和使用方式,对于深入掌握Spring框架至关重要。本文将从代理模式的基本概念出发,详细讲解JDK动态代理和CGLIB代理的实现原理,以及Spring如何选择和使用这两种代理方式。

一、代理模式概述

1.1 什么是代理模式

代理模式是一种结构型设计模式,它为目标对象提供一个代理对象,由代理对象控制对目标对象的访问。

arduino 复制代码
代理模式结构
┌─────────────────────────────────────────┐
│  Client(客户端)                        │
│     │                                    │
│     ▼                                    │
│  ┌───────────┐                          │
│  │   Proxy   │ ← 代理对象                │
│  └─────┬─────┘                          │
│        │                                 │
│   代理逻辑:                             │
│   - 前置处理(日志、权限等)             │
│   - 调用目标方法                         │
│   - 后置处理(结果处理等)               │
│        │                                 │
│        ▼                                 │
│  ┌───────────┐                          │
│  │  Target   │ ← 目标对象                │
│  └───────────┘                          │
└─────────────────────────────────────────┘

1.2 静态代理

java 复制代码
/**
 * 接口
 */
public interface UserService {
    User findById(Long id);
    void save(User user);
}

/**
 * 目标类
 */
public class UserServiceImpl implements UserService {

    @Override
    public User findById(Long id) {
        System.out.println("查询用户: " + id);
        return new User(id, "User" + id);
    }

    @Override
    public void save(User user) {
        System.out.println("保存用户: " + user.getUsername());
    }
}

/**
 * 静态代理类
 */
public class UserServiceProxy implements UserService {

    private UserService target;

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public User findById(Long id) {
        // 前置增强
        System.out.println("[静态代理] 方法开始: findById");
        long startTime = System.currentTimeMillis();

        // 调用目标方法
        User result = target.findById(id);

        // 后置增强
        long endTime = System.currentTimeMillis();
        System.out.println("[静态代理] 方法结束,耗时: " + (endTime - startTime) + "ms");

        return result;
    }

    @Override
    public void save(User user) {
        System.out.println("[静态代理] 方法开始: save");
        target.save(user);
        System.out.println("[静态代理] 方法结束");
    }
}

/**
 * 使用静态代理
 */
public class StaticProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        UserService target = new UserServiceImpl();

        // 创建代理对象
        UserService proxy = new UserServiceProxy(target);

        // 通过代理调用
        proxy.findById(1L);
        proxy.save(new User(null, "张三"));
    }
}

静态代理的问题:

  • 每个目标类都需要一个代理类
  • 接口增加方法,代理类也要修改
  • 代码重复,维护困难

1.3 动态代理

动态代理在运行时动态创建代理类,无需手动编写代理类代码。

复制代码
静态代理 vs 动态代理
┌─────────────────────┬──────────────────────┐
│  静态代理            │  动态代理             │
├─────────────────────┼──────────────────────┤
│  编译时创建代理类    │  运行时创建代理类      │
│  手动编写代理类      │  自动生成代理类        │
│  每个类一个代理      │  一套代理逻辑复用      │
│  维护困难            │  灵活易扩展           │
└─────────────────────┴──────────────────────┘

二、JDK动态代理

2.1 JDK动态代理原理

JDK动态代理基于接口实现,通过java.lang.reflect.Proxy类和InvocationHandler接口来创建代理对象。

scss 复制代码
JDK动态代理原理
┌─────────────────────────────────────────┐
│  1. 运行时生成代理类($Proxy0)          │
│  2. 代理类实现目标接口                   │
│  3. 代理类持有InvocationHandler引用      │
│  4. 调用代理方法时,转发给InvocationHandler
│  5. InvocationHandler中调用目标方法      │
└─────────────────────────────────────────┘

      ┌───────────────┐
      │  Interface    │
      └───────┬───────┘
              │
     ┌────────┴────────┐
     │                 │
┌────┴────┐      ┌─────┴─────┐
│$Proxy0  │      │  Target   │
│(代理类) │      │ (目标类)  │
└────┬────┘      └───────────┘
     │
     ▼
┌─────────────────┐
│InvocationHandler│
│ (调用处理器)     │
└─────────────────┘

2.2 JDK动态代理实现

java 复制代码
/**
 * 自定义InvocationHandler
 */
public class LogInvocationHandler implements InvocationHandler {

    private Object target;

    public LogInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取方法信息
        String methodName = method.getName();
        String className = target.getClass().getSimpleName();

        // 前置增强
        System.out.println("[JDK代理] " + className + "." + methodName + "() 开始执行");
        System.out.println("[JDK代理] 参数: " + Arrays.toString(args));
        long startTime = System.currentTimeMillis();

        Object result;
        try {
            // 调用目标方法
            result = method.invoke(target, args);

            // 后置增强
            long endTime = System.currentTimeMillis();
            System.out.println("[JDK代理] 返回值: " + result);
            System.out.println("[JDK代理] 执行成功,耗时: " + (endTime - startTime) + "ms");

        } catch (InvocationTargetException e) {
            // 异常增强
            System.out.println("[JDK代理] 执行异常: " + e.getTargetException().getMessage());
            throw e.getTargetException();
        }

        return result;
    }
}

/**
 * JDK动态代理工厂
 */
public class JdkProxyFactory {

    /**
     * 创建代理对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),    // 类加载器
            target.getClass().getInterfaces(),     // 目标类实现的接口
            new LogInvocationHandler(target)       // 调用处理器
        );
    }
}

/**
 * 使用JDK动态代理
 */
public class JdkProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        UserService target = new UserServiceImpl();

        // 创建代理对象
        UserService proxy = JdkProxyFactory.createProxy(target);

        // 通过代理调用
        proxy.findById(1L);
        System.out.println("---");
        proxy.save(new User(null, "李四"));

        // 打印代理类信息
        System.out.println("代理类: " + proxy.getClass().getName());
        System.out.println("代理类实现的接口: " + Arrays.toString(proxy.getClass().getInterfaces()));
    }
}

2.3 生成的代理类分析

java 复制代码
/**
 * JDK动态生成的代理类(反编译)
 */
public final class $Proxy0 extends Proxy implements UserService {

    private static Method m1;  // equals
    private static Method m2;  // toString
    private static Method m3;  // findById
    private static Method m4;  // save
    private static Method m0;  // hashCode

    static {
        try {
            m3 = Class.forName("com.example.UserService")
                .getMethod("findById", Long.class);
            m4 = Class.forName("com.example.UserService")
                .getMethod("save", User.class);
            // ...
        } catch (Exception e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }

    public $Proxy0(InvocationHandler h) {
        super(h);
    }

    @Override
    public User findById(Long id) {
        try {
            // 调用InvocationHandler的invoke方法
            return (User) super.h.invoke(this, m3, new Object[]{id});
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void save(User user) {
        try {
            super.h.invoke(this, m4, new Object[]{user});
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}

2.4 JDK代理的限制

java 复制代码
/**
 * JDK动态代理的限制
 */
public class JdkProxyLimitation {

    public static void main(String[] args) {
        // ❌ 错误:目标类没有实现接口
        OrderService orderService = new OrderService();

        // 以下代码会抛出异常
        // OrderService proxy = JdkProxyFactory.createProxy(orderService);
        // 因为OrderService没有实现任何接口

        System.out.println("JDK动态代理要求目标类必须实现接口");
    }
}

/**
 * 没有实现接口的类
 */
public class OrderService {
    public void createOrder() {
        System.out.println("创建订单");
    }
}

三、CGLIB动态代理

3.1 CGLIB代理原理

CGLIB(Code Generation Library)通过继承目标类的方式创建代理,不需要目标类实现接口。

php 复制代码
CGLIB代理原理
┌─────────────────────────────────────────┐
│  1. 运行时生成代理类(继承目标类)       │
│  2. 通过ASM字节码操作生成子类           │
│  3. 代理类重写父类的非final方法          │
│  4. 方法调用时,通过MethodInterceptor拦截│
│  5. MethodInterceptor中调用父类方法      │
└─────────────────────────────────────────┘

┌───────────────┐
│  Target       │
│  (目标类)     │
└───────┬───────┘
        │ extends
        │
┌───────┴───────┐
│ Target$$CGLIB │
│  (代理类)     │
└───────┬───────┘
        │
        ▼
┌─────────────────┐
│MethodInterceptor│
│  (方法拦截器)    │
└─────────────────┘

3.2 CGLIB代理实现

java 复制代码
/**
 * 自定义MethodInterceptor
 */
public class LogMethodInterceptor implements MethodInterceptor {

    /**
     * @param obj       代理对象
     * @param method    目标方法
     * @param args      方法参数
     * @param proxy     方法代理
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                           MethodProxy proxy) throws Throwable {

        // 获取方法信息
        String methodName = method.getName();
        String className = method.getDeclaringClass().getSimpleName();

        // 前置增强
        System.out.println("[CGLIB代理] " + className + "." + methodName + "() 开始执行");
        System.out.println("[CGLIB代理] 参数: " + Arrays.toString(args));
        long startTime = System.currentTimeMillis();

        Object result;
        try {
            // 调用父类方法(目标方法)
            result = proxy.invokeSuper(obj, args);

            // 后置增强
            long endTime = System.currentTimeMillis();
            System.out.println("[CGLIB代理] 返回值: " + result);
            System.out.println("[CGLIB代理] 执行成功,耗时: " + (endTime - startTime) + "ms");

        } catch (Throwable e) {
            // 异常增强
            System.out.println("[CGLIB代理] 执行异常: " + e.getMessage());
            throw e;
        }

        return result;
    }
}

/**
 * CGLIB代理工厂
 */
public class CglibProxyFactory {

    /**
     * 创建代理对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(Class<T> targetClass) {
        // 创建Enhancer对象
        Enhancer enhancer = new Enhancer();

        // 设置父类(目标类)
        enhancer.setSuperclass(targetClass);

        // 设置回调(方法拦截器)
        enhancer.setCallback(new LogMethodInterceptor());

        // 创建代理对象
        return (T) enhancer.create();
    }

    /**
     * 带构造参数创建代理对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(Class<T> targetClass,
                                    Class<?>[] argTypes, Object[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new LogMethodInterceptor());

        return (T) enhancer.create(argTypes, args);
    }
}

/**
 * 目标类(没有实现接口)
 */
public class OrderService {

    public Order createOrder(Long userId, BigDecimal amount) {
        System.out.println("创建订单: 用户=" + userId + ", 金额=" + amount);
        return new Order(System.currentTimeMillis(), userId, amount);
    }

    public void cancelOrder(Long orderId) {
        System.out.println("取消订单: " + orderId);
    }

    // final方法不能被代理
    public final void finalMethod() {
        System.out.println("这是final方法,不能被代理");
    }
}

/**
 * 使用CGLIB代理
 */
public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建代理对象(无需目标对象实例)
        OrderService proxy = CglibProxyFactory.createProxy(OrderService.class);

        // 通过代理调用
        proxy.createOrder(1L, new BigDecimal("99.99"));
        System.out.println("---");
        proxy.cancelOrder(1001L);
        System.out.println("---");

        // final方法不会被代理
        proxy.finalMethod();

        // 打印代理类信息
        System.out.println("代理类: " + proxy.getClass().getName());
        System.out.println("父类: " + proxy.getClass().getSuperclass().getName());
    }
}

3.3 CGLIB的高级用法

java 复制代码
/**
 * 多个回调(CallbackFilter)
 */
public class MultiCallbackDemo {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OrderService.class);

        // 定义多个回调
        Callback[] callbacks = new Callback[]{
            new LogMethodInterceptor(),           // 索引0:日志拦截器
            new PerformanceMethodInterceptor(),   // 索引1:性能拦截器
            NoOp.INSTANCE                         // 索引2:不做任何操作
        };

        enhancer.setCallbacks(callbacks);

        // 回调过滤器:决定每个方法使用哪个回调
        enhancer.setCallbackFilter(method -> {
            String methodName = method.getName();

            if (methodName.startsWith("create")) {
                return 0;  // 使用日志拦截器
            } else if (methodName.startsWith("find")) {
                return 1;  // 使用性能拦截器
            } else {
                return 2;  // 不拦截
            }
        });

        OrderService proxy = (OrderService) enhancer.create();
        proxy.createOrder(1L, new BigDecimal("100"));
    }
}

/**
 * 性能监控拦截器
 */
public class PerformanceMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                           MethodProxy proxy) throws Throwable {
        long startTime = System.currentTimeMillis();

        Object result = proxy.invokeSuper(obj, args);

        long duration = System.currentTimeMillis() - startTime;
        System.out.println("[性能监控] " + method.getName() + " 耗时: " + duration + "ms");

        return result;
    }
}

/**
 * 延迟加载(LazyLoader)
 */
public class LazyLoadDemo {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(HeavyObject.class);

        // 延迟加载回调
        enhancer.setCallback((LazyLoader) () -> {
            System.out.println("开始创建HeavyObject...");
            return new HeavyObject();
        });

        HeavyObject proxy = (HeavyObject) enhancer.create();
        System.out.println("代理对象已创建,但目标对象尚未创建");

        // 第一次调用方法时才创建目标对象
        proxy.doSomething();
    }
}

class HeavyObject {
    public HeavyObject() {
        // 模拟耗时初始化
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void doSomething() {
        System.out.println("HeavyObject.doSomething()");
    }
}

3.4 CGLIB的限制

java 复制代码
/**
 * CGLIB代理的限制
 */
public class CglibLimitation {

    public static void main(String[] args) {
        // ❌ 限制1:不能代理final类
        // FinalClass proxy = CglibProxyFactory.createProxy(FinalClass.class);
        // 会抛出异常:Cannot subclass final class

        // ❌ 限制2:不能代理final方法
        // final方法不会被拦截,直接调用原方法

        // ❌ 限制3:不能代理private方法
        // private方法不能被继承,无法代理
    }
}

// final类不能被CGLIB代理
final class FinalClass {
    public void method() {
        System.out.println("final类的方法");
    }
}

四、Spring中的代理选择

4.1 Spring代理选择策略

java 复制代码
/**
 * Spring代理选择策略
 */
@Configuration
@EnableAspectJAutoProxy  // 默认使用JDK代理
// @EnableAspectJAutoProxy(proxyTargetClass = true)  // 强制使用CGLIB
public class ProxyConfig {
}
ini 复制代码
Spring代理选择流程
┌─────────────────────────────────────────┐
│  目标类是否实现接口?                    │
│         │                                │
│    ┌────┴────┐                          │
│    │         │                          │
│   是         否                          │
│    │         │                          │
│    ▼         ▼                          │
│  JDK代理   CGLIB代理                     │
│  (默认)    (自动)                        │
│                                          │
│  proxyTargetClass = true ?              │
│    → 强制使用CGLIB代理                   │
└─────────────────────────────────────────┘

4.2 AopProxyFactory源码分析

java 复制代码
/**
 * Spring AOP代理工厂(简化版)
 */
public class DefaultAopProxyFactory implements AopProxyFactory {

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) {
        // 判断使用哪种代理
        if (config.isOptimize() ||                    // 优化模式
            config.isProxyTargetClass() ||           // 强制CGLIB
            hasNoUserSuppliedProxyInterfaces(config)) { // 没有接口

            Class<?> targetClass = config.getTargetClass();

            if (targetClass == null) {
                throw new AopConfigException("...");
            }

            // 如果是接口或已经是代理类,使用JDK代理
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }

            // 否则使用CGLIB代理
            return new ObjenesisCglibAopProxy(config);

        } else {
            // 默认使用JDK代理
            return new JdkDynamicAopProxy(config);
        }
    }

    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class<?>[] interfaces = config.getProxiedInterfaces();
        return interfaces.length == 0 ||
               (interfaces.length == 1 && SpringProxy.class.isAssignableFrom(interfaces[0]));
    }
}

4.3 配置代理方式

java 复制代码
/**
 * XML配置方式
 */
/*
<aop:aspectj-autoproxy proxy-target-class="true"/>
*/

/**
 * 注解配置方式
 */
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}

/**
 * application.properties配置
 */
// spring.aop.proxy-target-class=true

/**
 * 事务配置中指定代理方式
 */
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
public class TransactionConfig {
}

五、实战案例

5.1 案例1:通用日志代理

java 复制代码
/**
 * 通用日志代理工厂
 */
public class UniversalLogProxyFactory {

    /**
     * 创建代理对象(自动选择代理方式)
     */
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        Class<?> targetClass = target.getClass();

        // 判断是否有接口
        if (targetClass.getInterfaces().length > 0) {
            // 使用JDK代理
            return (T) Proxy.newProxyInstance(
                targetClass.getClassLoader(),
                targetClass.getInterfaces(),
                new UniversalLogHandler(target)
            );
        } else {
            // 使用CGLIB代理
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(targetClass);
            enhancer.setCallback(new UniversalLogInterceptor(target));
            return (T) enhancer.create();
        }
    }
}

/**
 * JDK代理处理器
 */
class UniversalLogHandler implements InvocationHandler {

    private Object target;

    public UniversalLogHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return LogHelper.logAndInvoke(target, method, args, () -> method.invoke(target, args));
    }
}

/**
 * CGLIB方法拦截器
 */
class UniversalLogInterceptor implements MethodInterceptor {

    private Object target;

    public UniversalLogInterceptor(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                           MethodProxy proxy) throws Throwable {
        return LogHelper.logAndInvoke(target, method, args, () -> proxy.invokeSuper(obj, args));
    }
}

/**
 * 日志辅助类
 */
class LogHelper {

    public static Object logAndInvoke(Object target, Method method, Object[] args,
                                     Callable<Object> invoker) throws Throwable {
        String className = target.getClass().getSimpleName();
        String methodName = method.getName();

        System.out.println("┌─────────────────────────────────────");
        System.out.println("│ 方法调用: " + className + "." + methodName);
        System.out.println("│ 参数: " + Arrays.toString(args));

        long startTime = System.currentTimeMillis();

        try {
            Object result = invoker.call();

            long duration = System.currentTimeMillis() - startTime;
            System.out.println("│ 返回值: " + result);
            System.out.println("│ 耗时: " + duration + "ms");
            System.out.println("└─────────────────────────────────────");

            return result;

        } catch (Exception e) {
            System.out.println("│ 异常: " + e.getMessage());
            System.out.println("└─────────────────────────────────────");
            throw e;
        }
    }
}

/**
 * 使用示例
 */
public class UniversalProxyDemo {
    public static void main(String[] args) {
        // 有接口的类 - 使用JDK代理
        UserService userProxy = UniversalLogProxyFactory.createProxy(new UserServiceImpl());
        userProxy.findById(1L);

        System.out.println();

        // 无接口的类 - 使用CGLIB代理
        OrderService orderProxy = UniversalLogProxyFactory.createProxy(new OrderService());
        orderProxy.createOrder(1L, new BigDecimal("100"));
    }
}

5.2 案例2:方法执行重试代理

java 复制代码
/**
 * 重试注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retryable {
    int maxAttempts() default 3;           // 最大重试次数
    long delay() default 1000;             // 重试间隔(毫秒)
    Class<? extends Throwable>[] value() default {};  // 需要重试的异常
}

/**
 * 重试代理工厂
 */
public class RetryProxyFactory {

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        Class<?> targetClass = target.getClass();

        if (targetClass.getInterfaces().length > 0) {
            return (T) Proxy.newProxyInstance(
                targetClass.getClassLoader(),
                targetClass.getInterfaces(),
                new RetryHandler(target)
            );
        } else {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(targetClass);
            enhancer.setCallback(new RetryInterceptor(target));
            return (T) enhancer.create();
        }
    }
}

/**
 * JDK重试处理器
 */
class RetryHandler implements InvocationHandler {

    private Object target;

    public RetryHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return RetryExecutor.executeWithRetry(target, method, args,
            () -> method.invoke(target, args));
    }
}

/**
 * CGLIB重试拦截器
 */
class RetryInterceptor implements MethodInterceptor {

    private Object target;

    public RetryInterceptor(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                           MethodProxy proxy) throws Throwable {
        return RetryExecutor.executeWithRetry(target, method, args,
            () -> proxy.invokeSuper(obj, args));
    }
}

/**
 * 重试执行器
 */
class RetryExecutor {

    public static Object executeWithRetry(Object target, Method method, Object[] args,
                                         Callable<Object> invoker) throws Throwable {
        // 获取方法上的@Retryable注解
        Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes());
        Retryable retryable = targetMethod.getAnnotation(Retryable.class);

        if (retryable == null) {
            // 没有注解,直接执行
            return invoker.call();
        }

        int maxAttempts = retryable.maxAttempts();
        long delay = retryable.delay();
        Class<? extends Throwable>[] retryExceptions = retryable.value();

        int attempt = 0;
        Throwable lastException = null;

        while (attempt < maxAttempts) {
            attempt++;
            try {
                System.out.println("[重试代理] 第" + attempt + "次尝试执行: " + method.getName());
                return invoker.call();

            } catch (Throwable e) {
                Throwable cause = e instanceof InvocationTargetException
                    ? ((InvocationTargetException) e).getTargetException() : e;

                // 检查是否需要重试
                boolean shouldRetry = retryExceptions.length == 0 ||
                    Arrays.stream(retryExceptions).anyMatch(ex -> ex.isInstance(cause));

                if (!shouldRetry || attempt >= maxAttempts) {
                    System.out.println("[重试代理] 重试失败: " + cause.getMessage());
                    throw cause;
                }

                lastException = cause;
                System.out.println("[重试代理] 执行失败," + delay + "ms后重试: " + cause.getMessage());

                try {
                    Thread.sleep(delay);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw cause;
                }
            }
        }

        throw lastException;
    }
}

/**
 * 远程服务(演示重试)
 */
public class RemoteService {

    private int callCount = 0;

    @Retryable(maxAttempts = 3, delay = 500)
    public String callRemoteApi(String param) {
        callCount++;
        System.out.println("调用远程API,第" + callCount + "次");

        // 模拟偶发失败
        if (callCount < 3) {
            throw new RuntimeException("网络超时");
        }

        return "调用成功: " + param;
    }
}

/**
 * 使用示例
 */
public class RetryProxyDemo {
    public static void main(String[] args) {
        RemoteService proxy = RetryProxyFactory.createProxy(new RemoteService());

        String result = proxy.callRemoteApi("test");
        System.out.println("最终结果: " + result);
    }
}

5.3 案例3:缓存代理

java 复制代码
/**
 * 缓存注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
    String key() default "";
    int expire() default 3600;  // 过期时间(秒)
}

/**
 * 缓存代理工厂
 */
public class CacheProxyFactory {

    private static final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CacheInterceptor(target));
        return (T) enhancer.create();
    }

    static class CacheEntry {
        Object value;
        long expireTime;

        CacheEntry(Object value, int expireSeconds) {
            this.value = value;
            this.expireTime = System.currentTimeMillis() + expireSeconds * 1000L;
        }

        boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
    }

    static class CacheInterceptor implements MethodInterceptor {

        private Object target;

        CacheInterceptor(Object target) {
            this.target = target;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args,
                               MethodProxy proxy) throws Throwable {

            // 获取@Cacheable注解
            Method targetMethod = target.getClass().getMethod(
                method.getName(), method.getParameterTypes());
            Cacheable cacheable = targetMethod.getAnnotation(Cacheable.class);

            if (cacheable == null) {
                return proxy.invokeSuper(obj, args);
            }

            // 构建缓存key
            String cacheKey = buildCacheKey(cacheable.key(), method, args);

            // 尝试从缓存获取
            CacheEntry entry = cache.get(cacheKey);
            if (entry != null && !entry.isExpired()) {
                System.out.println("[缓存代理] 缓存命中: " + cacheKey);
                return entry.value;
            }

            // 缓存未命中,执行方法
            System.out.println("[缓存代理] 缓存未命中: " + cacheKey);
            Object result = proxy.invokeSuper(obj, args);

            // 存入缓存
            if (result != null) {
                cache.put(cacheKey, new CacheEntry(result, cacheable.expire()));
                System.out.println("[缓存代理] 缓存写入: " + cacheKey);
            }

            return result;
        }

        private String buildCacheKey(String keyPattern, Method method, Object[] args) {
            if (keyPattern.isEmpty()) {
                // 默认key:类名.方法名(参数)
                return method.getDeclaringClass().getSimpleName() + "." +
                       method.getName() + "(" + Arrays.toString(args) + ")";
            }

            // 替换参数占位符
            String key = keyPattern;
            for (int i = 0; i < args.length; i++) {
                key = key.replace("#p" + i, String.valueOf(args[i]));
            }
            return key;
        }
    }
}

/**
 * 用户服务(带缓存)
 */
public class CachedUserService {

    @Cacheable(key = "user:#p0", expire = 60)
    public User findById(Long id) {
        System.out.println("从数据库查询用户: " + id);
        // 模拟数据库查询
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new User(id, "User" + id);
    }

    @Cacheable(expire = 300)
    public List<User> findAll() {
        System.out.println("从数据库查询所有用户");
        return Arrays.asList(
            new User(1L, "User1"),
            new User(2L, "User2")
        );
    }
}

/**
 * 使用示例
 */
public class CacheProxyDemo {
    public static void main(String[] args) {
        CachedUserService proxy = CacheProxyFactory.createProxy(new CachedUserService());

        // 第一次调用,缓存未命中
        User user1 = proxy.findById(1L);
        System.out.println("查询结果: " + user1);

        System.out.println();

        // 第二次调用,缓存命中
        User user2 = proxy.findById(1L);
        System.out.println("查询结果: " + user2);

        System.out.println();

        // 不同参数,缓存未命中
        User user3 = proxy.findById(2L);
        System.out.println("查询结果: " + user3);
    }
}

5.4 案例4:权限校验代理

java 复制代码
/**
 * 权限注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
    String[] value();
}

/**
 * 用户上下文(模拟)
 */
public class UserContext {

    private static final ThreadLocal<Set<String>> permissions = new ThreadLocal<>();

    public static void setPermissions(Set<String> perms) {
        permissions.set(perms);
    }

    public static Set<String> getPermissions() {
        return permissions.get();
    }

    public static boolean hasPermission(String permission) {
        Set<String> perms = permissions.get();
        return perms != null && perms.contains(permission);
    }

    public static void clear() {
        permissions.remove();
    }
}

/**
 * 权限代理工厂
 */
public class PermissionProxyFactory {

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new PermissionInterceptor(target));
        return (T) enhancer.create();
    }

    static class PermissionInterceptor implements MethodInterceptor {

        private Object target;

        PermissionInterceptor(Object target) {
            this.target = target;
        }

        @Override
        public Object intercept(Object obj, Method method, Object[] args,
                               MethodProxy proxy) throws Throwable {

            // 获取@RequiresPermission注解
            Method targetMethod = target.getClass().getMethod(
                method.getName(), method.getParameterTypes());
            RequiresPermission annotation = targetMethod.getAnnotation(RequiresPermission.class);

            if (annotation != null) {
                String[] required = annotation.value();

                // 检查权限
                for (String permission : required) {
                    if (!UserContext.hasPermission(permission)) {
                        throw new SecurityException("缺少权限: " + permission);
                    }
                }

                System.out.println("[权限代理] 权限校验通过: " + Arrays.toString(required));
            }

            return proxy.invokeSuper(obj, args);
        }
    }
}

/**
 * 管理服务
 */
public class AdminService {

    @RequiresPermission("user:delete")
    public void deleteUser(Long userId) {
        System.out.println("删除用户: " + userId);
    }

    @RequiresPermission({"user:create", "user:update"})
    public void createOrUpdateUser(User user) {
        System.out.println("创建或更新用户: " + user.getUsername());
    }

    public void queryUser(Long userId) {
        System.out.println("查询用户: " + userId);
    }
}

/**
 * 使用示例
 */
public class PermissionProxyDemo {
    public static void main(String[] args) {
        AdminService proxy = PermissionProxyFactory.createProxy(new AdminService());

        // 设置用户权限
        UserContext.setPermissions(new HashSet<>(Arrays.asList("user:query", "user:create")));

        try {
            // 无需权限的方法
            proxy.queryUser(1L);
            System.out.println();

            // 权限不足
            proxy.deleteUser(1L);

        } catch (SecurityException e) {
            System.out.println("权限校验失败: " + e.getMessage());
        }

        System.out.println();

        // 添加权限后重试
        UserContext.setPermissions(new HashSet<>(Arrays.asList("user:delete")));
        proxy.deleteUser(1L);

        UserContext.clear();
    }
}

六、性能对比

6.1 JDK代理 vs CGLIB性能测试

java 复制代码
/**
 * 性能测试
 */
public class ProxyPerformanceTest {

    private static final int ITERATIONS = 1000000;

    public static void main(String[] args) {
        // 预热
        warmup();

        // 测试直接调用
        testDirectCall();

        // 测试JDK代理
        testJdkProxy();

        // 测试CGLIB代理
        testCglibProxy();
    }

    private static void warmup() {
        UserService target = new UserServiceImpl();
        UserService jdkProxy = createJdkProxy(target);
        UserService cglibProxy = createCglibProxy(target);

        for (int i = 0; i < 10000; i++) {
            target.findById(1L);
            jdkProxy.findById(1L);
            cglibProxy.findById(1L);
        }
    }

    private static void testDirectCall() {
        UserService target = new UserServiceImpl();

        long start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            target.findById(1L);
        }
        long duration = System.currentTimeMillis() - start;

        System.out.println("直接调用耗时: " + duration + "ms");
    }

    private static void testJdkProxy() {
        UserService target = new UserServiceImpl();
        UserService proxy = createJdkProxy(target);

        long start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            proxy.findById(1L);
        }
        long duration = System.currentTimeMillis() - start;

        System.out.println("JDK代理耗时: " + duration + "ms");
    }

    private static void testCglibProxy() {
        UserService target = new UserServiceImpl();
        UserService proxy = createCglibProxy(target);

        long start = System.currentTimeMillis();
        for (int i = 0; i < ITERATIONS; i++) {
            proxy.findById(1L);
        }
        long duration = System.currentTimeMillis() - start;

        System.out.println("CGLIB代理耗时: " + duration + "ms");
    }

    @SuppressWarnings("unchecked")
    private static UserService createJdkProxy(UserService target) {
        return (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> method.invoke(target, args)
        );
    }

    @SuppressWarnings("unchecked")
    private static UserService createCglibProxy(UserService target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) ->
            proxy.invokeSuper(obj, args));
        return (UserService) enhancer.create();
    }
}

6.2 性能对比结果

markdown 复制代码
典型测试结果(100万次调用)
┌──────────────┬──────────┬─────────┐
│  调用方式     │  耗时     │  说明    │
├──────────────┼──────────┼─────────┤
│  直接调用     │  ~50ms   │  基准    │
│  JDK代理      │  ~150ms  │  约3倍   │
│  CGLIB代理    │  ~120ms  │  约2.4倍 │
└──────────────┴──────────┴─────────┘

说明:
1. 现代JVM对JDK代理有优化
2. CGLIB在方法调用上略快(使用FastClass)
3. CGLIB创建代理类较慢(需要生成字节码)
4. 实际业务中,代理开销通常可忽略

七、最佳实践

7.1 选择代理方式

java 复制代码
/**
 * 代理方式选择建议
 */

/*
1. 优先使用JDK代理
   - 面向接口编程
   - Spring默认选择
   - 更符合设计原则

2. 以下情况使用CGLIB代理
   - 目标类没有实现接口
   - 需要代理非接口方法
   - 性能敏感场景

3. Spring Boot 2.x默认使用CGLIB
   - spring.aop.proxy-target-class=true
*/

@Configuration
public class ProxyRecommendation {

    // 推荐:面向接口编程
    @Bean
    public UserService userService() {
        return new UserServiceImpl();
    }

    // 不推荐:直接注入实现类
    @Bean
    public UserServiceImpl userServiceImpl() {
        return new UserServiceImpl();
    }
}

7.2 避免常见问题

java 复制代码
/**
 * 代理常见问题及解决方案
 */
@Service
public class ProxyPitfalls {

    /**
     * 问题1:同类方法调用不走代理
     */
    @Transactional
    public void outerMethod() {
        // ❌ 直接调用,不走代理
        this.innerMethod();
    }

    @Transactional
    public void innerMethod() {
        // 事务不生效
    }

    // 解决方案1:注入自己
    @Autowired
    private ProxyPitfalls self;

    public void outerMethodFixed1() {
        self.innerMethod();  // ✓ 走代理
    }

    // 解决方案2:使用AopContext
    public void outerMethodFixed2() {
        ((ProxyPitfalls) AopContext.currentProxy()).innerMethod();  // ✓
    }

    /**
     * 问题2:final方法不能被代理
     */
    public final void finalMethod() {
        // CGLIB无法代理final方法
    }

    /**
     * 问题3:private方法不能被代理
     */
    private void privateMethod() {
        // 任何代理都无法代理private方法
    }
}

/**
 * 启用AopContext
 */
@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class AopConfig {
}

7.3 调试代理

java 复制代码
/**
 * 代理调试技巧
 */
public class ProxyDebug {

    public static void main(String[] args) {
        // 1. 查看代理类名
        Object proxy = createProxy();
        System.out.println("代理类: " + proxy.getClass().getName());

        // 2. 判断是否为代理对象
        System.out.println("是JDK代理: " + Proxy.isProxyClass(proxy.getClass()));
        System.out.println("是CGLIB代理: " + proxy.getClass().getName().contains("$$"));

        // 3. 获取原始对象
        if (AopUtils.isAopProxy(proxy)) {
            try {
                Object target = ((Advised) proxy).getTargetSource().getTarget();
                System.out.println("目标对象: " + target.getClass().getName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 4. 保存生成的代理类
        // JDK代理
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        // CGLIB代理
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/tmp/cglib");
    }

    private static Object createProxy() {
        // ...
        return null;
    }
}

八、总结

核心知识点回顾

swift 复制代码
Spring动态代理核心要点
│
├── 代理模式基础
│   ├── 静态代理
│   └── 动态代理
│
├── JDK动态代理
│   ├── 基于接口
│   ├── Proxy + InvocationHandler
│   └── 限制:必须有接口
│
├── CGLIB代理
│   ├── 基于继承
│   ├── Enhancer + MethodInterceptor
│   └── 限制:不能代理final类/方法
│
├── Spring代理选择
│   ├── 默认JDK代理(有接口)
│   ├── 自动CGLIB代理(无接口)
│   └── 强制CGLIB代理(配置)
│
├── 实战应用
│   ├── 日志代理
│   ├── 重试代理
│   ├── 缓存代理
│   └── 权限代理
│
└── 最佳实践
    ├── 面向接口编程
    ├── 避免同类调用
    ├── 注意final/private方法
    └── 合理选择代理方式

动态代理是Spring框架的核心技术,它使得AOP、事务管理、缓存等功能的实现成为可能。理解JDK动态代理和CGLIB代理的原理和区别,掌握正确的使用方法,能够帮助我们更好地使用Spring框架,并在需要时实现自定义的代理功能。


相关推荐
若水不如远方20 分钟前
深入理解Reactor:从单线程到主从模式演进之路
java·架构
爱分享的鱼鱼22 分钟前
Java高级查询、分页、排序
java
某空_32 分钟前
【Android】线程池解析
java
q***116541 分钟前
总结:Spring Boot 之spring.factories
java·spring boot·spring
追风少年浪子彦1 小时前
Spring Boot 使用自定义 JsonDeserializer 同时支持多种日期格式
java·spring boot·后端
牢七1 小时前
Javan
java
我叫黑大帅1 小时前
六边形架构?小白也能秒懂的「抗造代码秘诀」
java·后端·架构
不穿格子的程序员1 小时前
Java基础篇——JDK新特性总结
java·虚拟线程·jdk新特性
踏浪无痕1 小时前
6张表、14步业务逻辑,Mall订单事务凭什么比你的3步事务还稳?
spring boot·spring·面试