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框架,并在需要时实现自定义的代理功能。


相关推荐
@淡 定8 分钟前
Spring中@Autowired注解的实现原理
java·后端·spring
时空无限16 分钟前
Java Buildpack Reference
java·开发语言
爱笑的眼睛111 小时前
超越剪枝与量化:下一代AI模型压缩工具的技术演进与实践
java·人工智能·python·ai
阿里云云原生1 小时前
Android App 崩溃排查指南:阿里云 RUM 如何让你快速从告警到定位根因?
android·java
历程里程碑1 小时前
C++ 9 stack_queue:数据结构的核心奥秘
java·开发语言·数据结构·c++·windows·笔记·算法
醇氧2 小时前
【Windows】从守护到终结:解析一个 Java 服务的优雅停止脚本
java·开发语言·windows
努力发光的程序员2 小时前
互联网大厂Java求职面试实录
java·jvm·线程池·多线程·hashmap·juc·arraylist
小鹿学程序2 小时前
FileZilla连接到虚拟机
java·服务器·开发语言
Haooog2 小时前
Docker面试题(不定时更新)
java·docker·面试
feathered-feathered2 小时前
Redis基础知识+RDB+AOF(面试)
java·数据库·redis·分布式·后端·中间件·面试