动态代理

问:什么是动态代理?JDK 和 CGLIB 有什么区别?

动态代理就是一种“运行时帮你生成代理对象”的机制,它的最大特点是:你不需要提前写好代理类,而是在程序运行的时候,框架帮你“临时织入”增强逻辑。

在不动原来业务代码的前提下,它可以帮你在方法执行的前后插入各种通用逻辑,比如日志打印、权限校验、事务控制等,这在 AOP、RPC、监控等场景中非常常见。

Java 有两种动态代理方式:

  • JDK 动态代理:专门用来代理“接口”,它通过 ProxyInvocationHandler 实现,是基于反射的;
  • CGLIB 动态代理:用来代理“类”,不需要接口,是通过继承 + 动态生成字节码的方式实现的。

简单理解就是: 👉 有接口就用 JDK,没接口就用 CGLIB。

在 Spring 中:

  • 传统 Spring 默认用的是 JDK 动态代理
  • 从 Spring Boot 2.x 开始,默认改用 CGLIB,因为它兼容性更强,避免因为类没有接口而出错。

第一部分:问题的提出

一、现实中的需求:横切关注点

想象一个场景:

// 传统方式:业务代码和增强逻辑混在一起
public class UserServiceImpl implements UserService {
    @Override
    public void save(User user) {
        // 日志打印
        System.out.println("保存用户前:" + user);

        // 权限检查
        if (!hasPermission()) {
            throw new SecurityException("无权限");
        }

        // 事务开始
        beginTransaction();

        try {
            // 真正的业务逻辑
            userRepository.save(user);

            // 事务提交
            commitTransaction();
        } catch (Exception e) {
            // 事务回滚
            rollbackTransaction();
            throw e;
        }

        // 日志打印
        System.out.println("保存用户后");
    }

    @Override
    public void delete(int id) {
        // 又要重复写一遍日志、权限、事务的代码...
        System.out.println("删除用户前:" + id);
        if (!hasPermission()) {
            throw new SecurityException("无权限");
        }
        beginTransaction();
        try {
            userRepository.delete(id);
            commitTransaction();
        } catch (Exception e) {
            rollbackTransaction();
            throw e;
        }
        System.out.println("删除用户后");
    }
}

这种写法有什么问题?

  1. 代码重复 - 日志、权限、事务代码在每个方法中重复
  2. 职责混乱 - 业务逻辑和增强逻辑混在一起
  3. 难以维护 - 修改增强逻辑需要改动所有方法
  4. 难以测试 - 无法单独测试业务逻辑

二、理想的解决方案

我们希望能这样写:

// 理想方式:业务代码只关注业务逻辑
public class UserServiceImpl implements UserService {
    @Override
    public void save(User user) {
        // 只写业务逻辑,增强逻辑由框架自动处理
        userRepository.save(user);
    }

    @Override
    public void delete(int id) {
        // 只写业务逻辑
        userRepository.delete(id);
    }
}

// 框架自动为我们处理:日志、权限、事务等
UserService userService = createProxy(new UserServiceImpl());
userService.save(user);  // 自动执行:日志 → 权限检查 → 事务 → 业务逻辑 → 事务提交 → 日志

这就是动态代理要解决的问题:在不改变原始代码的前提下,为方法自动织入增强逻辑。


第二部分:解决方案的演进

三、静态代理:手工方案

在动态代理出现之前,我们可以手工写代理类:

// 1. 定义接口
public interface UserService {
    void save(User user);
    void delete(int id);
}

// 2. 实现接口
public class UserServiceImpl implements UserService {
    @Override
    public void save(User user) {
        userRepository.save(user);
    }

    @Override
    public void delete(int id) {
        userRepository.delete(id);
    }
}

// 3. 手工写代理类
public class UserServiceProxy implements UserService {
    private UserService target;

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

    @Override
    public void save(User user) {
        // 前置增强
        System.out.println("保存用户前");
        checkPermission();
        beginTransaction();

        try {
            // 调用原方法
            target.save(user);

            // 后置增强
            commitTransaction();
        } catch (Exception e) {
            rollbackTransaction();
            throw e;
        }

        System.out.println("保存用户后");
    }

    @Override
    public void delete(int id) {
        // 又要写一遍代理逻辑...
        System.out.println("删除用户前");
        checkPermission();
        beginTransaction();

        try {
            target.delete(id);
            commitTransaction();
        } catch (Exception e) {
            rollbackTransaction();
            throw e;
        }

        System.out.println("删除用户后");
    }
}

// 4. 使用代理
UserService userService = new UserServiceProxy(new UserServiceImpl());
userService.save(user);

静态代理的问题:

  1. 需要手工为每个接口写代理类
  2. 如果有100个接口,就要写100个代理类
  3. 代理逻辑重复,难以维护

四、动态代理:自动方案

动态代理就是让框架自动生成代理类,而不需要我们手工写。

// 动态代理:框架自动生成代理类
UserService target = new UserServiceImpl();

UserService proxy = createDynamicProxy(target, (method, args) -> {
    // 前置增强
    System.out.println("方法调用前:" + method.getName());
    checkPermission();
    beginTransaction();

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

        // 后置增强
        commitTransaction();
        return result;
    } catch (Exception e) {
        rollbackTransaction();
        throw e;
    }
});

// 所有方法都自动应用了增强逻辑
proxy.save(user);
proxy.delete(1);

动态代理的优势:

  1. 无需手工写代理类
  2. 一套增强逻辑适用于所有方法
  3. 易于维护和扩展

第三部分:动态代理的两种实现

五、JDK动态代理:基于接口

5.1 设计思路

┌─────────────────────────────────────────┐
│         JDK动态代理的设计思路             │
├─────────────────────────────────────────┤
│                                         │
│  1. 前提条件                             │
│     └─ 目标类必须实现接口                 │
│                                         │
│  2. 核心机制                             │
│     ├─ Proxy:代理工厂                   │
│     ├─ InvocationHandler:方法拦截器     │
│     └─ 反射:调用原方法                   │
│                                         │
│  3. 工作流程                             │
│     ├─ 调用代理对象的方法                 │
│     ├─ 转发到InvocationHandler.invoke()  │
│     ├─ 通过反射调用原方法                 │
│     └─ 返回结果                          │
│                                         │
└─────────────────────────────────────────┘

5.2 核心API

// Proxy.newProxyInstance() 的三个参数
Proxy.newProxyInstance(
    ClassLoader loader,              // 1. 类加载器
    Class<?>[] interfaces,           // 2. 要实现的接口
    InvocationHandler handler        // 3. 方法拦截器
);

5.3 完整实现

// 1. 定义接口
public interface UserService {
    void save(User user);
    String getName(int id);
}

// 2. 实现接口
public class UserServiceImpl implements UserService {
    @Override
    public void save(User user) {
        System.out.println("真正的保存逻辑:" + user);
    }

    @Override
    public String getName(int id) {
        System.out.println("真正的查询逻辑:" + id);
        return "Tom";
    }
}

// 3. 实现InvocationHandler(方法拦截器)
public class MyInvocationHandler implements InvocationHandler {
    private Object target;  // 被代理的目标对象

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // proxy:代理对象本身(通常不用)
        // method:被调用的方法
        // args:方法参数

        // 前置增强
        System.out.println("========== 方法调用前 ==========");
        System.out.println("方法名:" + method.getName());
        System.out.println("参数:" + Arrays.toString(args));

        // 调用原方法(通过反射)
        Object result = null;
        try {
            result = method.invoke(target, args);
            System.out.println("返回值:" + result);
        } catch (Exception e) {
            System.out.println("异常:" + e.getMessage());
            throw e;
        }

        // 后置增强
        System.out.println("========== 方法调用后 ==========");

        return result;
    }
}

// 4. 创建代理对象
public class JDKProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        UserService target = new UserServiceImpl();

        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),      // 类加载器
            target.getClass().getInterfaces(),       // 要实现的接口
            new MyInvocationHandler(target)          // 方法拦截器
        );

        // 调用代理对象的方法
        proxy.save(new User("Tom"));
        String name = proxy.getName(1);
    }
}

执行流程:

proxy.save(user)
  ↓
InvocationHandler.invoke(proxy, method, args)
  ↓
前置增强(日志、权限检查等)
  ↓
method.invoke(target, args)  // 反射调用原方法
  ↓
后置增强(事务提交等)
  ↓
返回结果

5.4 JDK动态代理的原理

// JDK动态代理的本质:运行时生成一个代理类
// 生成的代理类大概是这样的:

public class $Proxy0 implements UserService {
    private InvocationHandler h;

    public $Proxy0(InvocationHandler h) {
        this.h = h;
    }

    @Override
    public void save(User user) {
        try {
            // 获取save方法的Method对象
            Method method = UserService.class.getMethod("save", User.class);

            // 调用InvocationHandler的invoke方法
            h.invoke(this, method, new Object[]{user});
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public String getName(int id) {
        try {
            Method method = UserService.class.getMethod("getName", int.class);
            return (String) h.invoke(this, method, new Object[]{id});
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}

关键点:

  1. JDK动态代理在运行时生成一个实现了接口的代理类
  2. 代理类的每个方法都会调用InvocationHandler的invoke()
  3. 所有的增强逻辑都在invoke()中实现

六、CGLIB动态代理:基于继承

6.1 设计思路

┌─────────────────────────────────────────┐
│         CGLIB动态代理的设计思路           │
├─────────────────────────────────────────┤
│                                         │
│  1. 前提条件                             │
│     └─ 目标类可以没有接口                 │
│                                         │
│  2. 核心机制                             │
│     ├─ Enhancer:代理工厂                │
│     ├─ MethodInterceptor:方法拦截器    │
│     └─ 字节码增强:生成子类               │
│                                         │
│  3. 工作流程                             │
│     ├─ 创建目标类的子类                   │
│     ├─ 重写目标方法                       │
│     ├─ 在子类方法中插入拦截逻辑          │
│     └─ 调用super.method()执行原方法      │
│                                         │
└─────────────────────────────────────────┘

6.2 核心API

// Enhancer的基本用法
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);        // 设置父类
enhancer.setCallback(methodInterceptor);    // 设置拦截器
Object proxy = enhancer.create();           // 创建代理对象

6.3 完整实现

// 1. 目标类(注意:没有接口)
public class UserService {
    public void save(User user) {
        System.out.println("真正的保存逻辑:" + user);
    }

    public String getName(int id) {
        System.out.println("真正的查询逻辑:" + id);
        return "Tom";
    }
}

// 2. 实现MethodInterceptor(方法拦截器)
public class MyMethodInterceptor implements MethodInterceptor {
    private Object target;  // 被代理的目标对象

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

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
            throws Throwable {
        // obj:代理对象
        // method:被调用的方法
        // args:方法参数
        // proxy:方法代理,用于调用原方法

        // 前置增强
        System.out.println("========== 方法调用前 ==========");
        System.out.println("方法名:" + method.getName());
        System.out.println("参数:" + Arrays.toString(args));

        // 调用原方法(两种方式)
        Object result = null;
        try {
            // 方式1:使用MethodProxy(推荐,性能更好)
            result = proxy.invokeSuper(obj, args);

            // 方式2:使用反射(不推荐)
            // result = method.invoke(target, args);

            System.out.println("返回值:" + result);
        } catch (Exception e) {
            System.out.println("异常:" + e.getMessage());
            throw e;
        }

        // 后置增强
        System.out.println("========== 方法调用后 ==========");

        return result;
    }
}

// 3. 创建代理对象
public class CGLIBProxyDemo {
    public static void main(String[] args) {
        // 创建目标对象
        UserService target = new UserService();

        // 创建Enhancer
        Enhancer enhancer = new Enhancer();

        // 设置父类(要代理的类)
        enhancer.setSuperclass(UserService.class);

        // 设置拦截器
        enhancer.setCallback(new MyMethodInterceptor(target));

        // 创建代理对象
        UserService proxy = (UserService) enhancer.create();

        // 调用代理对象的方法
        proxy.save(new User("Tom"));
        String name = proxy.getName(1);
    }
}

执行流程:

proxy.save(user)
  ↓
MethodInterceptor.intercept(obj, method, args, proxy)
  ↓
前置增强(日志、权限检查等)
  ↓
proxy.invokeSuper(obj, args)  // 调用父类方法
  ↓
后置增强(事务提交等)
  ↓
返回结果

6.4 CGLIB动态代理的原理

// CGLIB动态代理的本质:运行时生成一个继承目标类的子类
// 生成的代理类大概是这样的:

public class UserService$$EnhancerByCGLIB$$12345 extends UserService {
    private MethodInterceptor methodInterceptor;

    @Override
    public void save(User user) {
        // 获取方法代理
        MethodProxy proxy = MethodProxy.find(
            UserService.class,
            new Class[]{User.class}
        );

        // 调用拦截器
        methodInterceptor.intercept(
            this,
            UserService.class.getMethod("save", User.class),
            new Object[]{user},
            proxy
        );
    }

    @Override
    public String getName(int id) {
        MethodProxy proxy = MethodProxy.find(
            UserService.class,
            new Class[]{int.class}
        );

        return (String) methodInterceptor.intercept(
            this,
            UserService.class.getMethod("getName", int.class),
            new Object[]{id},
            proxy
        );
    }
}

关键点:

  1. CGLIB动态代理在运行时生成一个继承目标类的子类
  2. 子类重写了目标类的所有方法
  3. 每个方法都会调用MethodInterceptor的intercept()
  4. 通过proxy.invokeSuper()调用父类(原始)方法

第四部分:JDK vs CGLIB对比

七、两者的核心区别

7.1 对比表格

维度 JDK动态代理 CGLIB动态代理
依赖接口 ✅ 必须实现接口 ❌ 不需要接口
实现方式 反射 + 接口实现 字节码增强 + 继承
代理对象 实现接口的代理类 继承目标类的子类
性能(调用) JDK8后接近 稍快(避免反射)
性能(创建) 慢(字节码生成)
final限制 无影响 ❌ 无法代理final方法
原生支持 ✅ JDK内置 ❌ 需要引入CGLIB
Spring默认 Spring Framework SpringBoot 2.x+

7.2 选择标准

┌─────────────────────────────────────────┐
│      如何选择JDK还是CGLIB?              │
├─────────────────────────────────────────┤
│                                         │
│  有接口?                                │
│  ├─ 是 → 优先选择JDK                    │
│  │       ├─ 原生支持                    │
│  │       ├─ 无需额外依赖                │
│  │       └─ 性能相当                    │
│  │                                     │
│  └─ 否 → 必须选择CGLIB                  │
│          ├─ 支持具体类                  │
│          ├─ 通用性强                    │
│          └─ 避免报错                    │
│                                         │
│  Spring框架选择:                        │
│  ├─ Spring Framework:JDK(默认)       │
│  ├─ SpringBoot 2.x+:CGLIB(默认)      │
│  └─ 可通过配置修改                      │
│                                         │
└─────────────────────────────────────────┘

7.3 为什么SpringBoot改用CGLIB?

// 问题场景:使用JDK代理的坑

// 情况1:有接口,JDK代理正常
public interface UserService {
    void save(User user);
}

public class UserServiceImpl implements UserService {
    @Override
    public void save(User user) { }
}

// ✅ 可以使用JDK代理
UserService proxy = (UserService) Proxy.newProxyInstance(...);

// 情况2:没有接口,JDK代理报错
public class UserService {  // 没有实现接口
    public void save(User user) { }
}

// ❌ JDK代理报错:java.lang.ClassCastException
// UserService proxy = (UserService) Proxy.newProxyInstance(...);
// 因为代理对象没有实现UserService接口

// ✅ CGLIB代理正常工作
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
UserService proxy = (UserService) enhancer.create();

SpringBoot改用CGLIB的原因:

  1. 通用性更强 - 支持有接口和无接口的类
  2. 避免ClassCastException - 不会因为缺接口而报错
  3. 易于维护 - 用户无需关心是否有接口
  4. 性能相当 - JDK8后性能差异已缩小

第五部分:实际应用

八、Spring AOP中的动态代理

// 1. 定义切面
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("方法调用前:" + joinPoint.getSignature());
    }

    @After("execution(* com.example.service.*.*(..))")
    public void afterMethod(JoinPoint joinPoint) {
        System.out.println("方法调用后:" + joinPoint.getSignature());
    }
}

// 2. 定义业务类(可以没有接口)
@Service
public class UserService {
    public void save(User user) {
        System.out.println("保存用户:" + user);
    }
}

// 3. Spring自动创建代理
@Autowired
private UserService userService;  // 实际上是代理对象

// 4. 调用代理对象
userService.save(user);  // 自动执行:前置通知 → 业务逻辑 → 后置通知

Spring AOP的工作流程:

1. 扫描@Aspect注解的切面类
2. 解析@Before、@After等通知
3. 为匹配的Bean创建代理对象
4. 调用代理对象时,自动织入增强逻辑

九、RPC框架中的动态代理

// RPC框架使用动态代理模拟本地调用

// 1. 定义远程服务接口
public interface UserServiceRPC {
    User getUserById(int id);
}

// 2. 创建RPC代理
public class RPCProxyFactory {
    public static <T> T createProxy(Class<T> serviceInterface, String serverAddress) {
        return (T) Proxy.newProxyInstance(
            serviceInterface.getClassLoader(),
            new Class[]{serviceInterface},
            (proxy, method, args) -> {
                // 1. 序列化参数
                byte[] requestData = serialize(method.getName(), args);

                // 2. 发送网络请求到远程服务器
                byte[] responseData = sendNetworkRequest(serverAddress, requestData);

                // 3. 反序列化返回值
                return deserialize(responseData);
            }
        );
    }
}

// 3. 使用RPC代理
UserServiceRPC userService = RPCProxyFactory.createProxy(
    UserServiceRPC.class,
    "192.168.1.100:8080"
);

// 看起来像本地调用,实际上是远程调用
User user = userService.getUserById(1);

第六部分:性能与最佳实践

十、性能对比

public class ProxyPerformanceDemo {
    public static void main(String[] args) {
        int iterations = 1_000_000;

        // ========== 直接调用 ==========
        UserService target = new UserServiceImpl();
        long directStart = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            target.save(new User());
        }
        long directTime = System.nanoTime() - directStart;

        // ========== JDK代理 ==========
        UserService jdkProxy = (UserService) Proxy.newProxyInstance(
            UserService.class.getClassLoader(),
            new Class[]{UserService.class},
            new MyInvocationHandler(target)
        );
        long jdkStart = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            jdkProxy.save(new User());
        }
        long jdkTime = System.nanoTime() - jdkStart;

        // ========== CGLIB代理 ==========
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(new MyMethodInterceptor(target));
        UserService cglibProxy = (UserService) enhancer.create();

        long cglibStart = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            cglibProxy.save(new User());
        }
        long cglibTime = System.nanoTime() - cglibStart;

        // 输出结果
        System.out.println("直接调用:" + directTime + "ns");
        System.out.println("JDK代理:" + jdkTime + "ns");
        System.out.println("CGLIB代理:" + cglibTime + "ns");
    }
}

典型结果(JDK8+):

直接调用:10ms
JDK代理:15ms(1.5倍)
CGLIB代理:12ms(1.2倍)

性能特点:

  • JDK代理:调用快,创建快
  • CGLIB代理:调用稍快,创建慢(字节码生成)
  • 总体差异不大,不是选择的主要因素

十一、最佳实践

// ✅ 推荐做法

// 1. 优先使用接口
public interface UserService {
    void save(User user);
}

public class UserServiceImpl implements UserService {
    @Override
    public void save(User user) {
        // 业务逻辑
    }
}

// 2. 使用Spring AOP而不是手工代理
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("方法调用前");
    }
}

// 3. 缓存代理对象
public class ProxyCache {
    private static final Map<Class<?>, Object> cache = new ConcurrentHashMap<>();

    public static <T> T getProxy(Class<T> serviceClass) {
        return (T) cache.computeIfAbsent(serviceClass, key -> {
            // 创建代理对象
            return createProxy(serviceClass);
        });
    }
}

// ❌ 避免做法

// 1. 不要频繁创建代理对象
for (int i = 0; i < 1000; i++) {
    UserService proxy = Proxy.newProxyInstance(...);  // 每次都创建
}

// 2. 不要在代理中做重操作
public Object invoke(Object proxy, Method method, Object[] args) {
    // ❌ 不要在这里做数据库查询等重操作
    userRepository.queryAll();

    return method.invoke(target, args);
}

// 3. 不要混用JDK和CGLIB
// 同一个应用中,尽量统一使用一种代理方式

第七部分:总结

十二、动态代理的本质

动态代理是在运行时自动生成代理类的机制,它让我们能够在不修改原始代码的前提下,为方法自动织入增强逻辑。

┌──────────────────────────────────────────┐
│ 动态代理的核心价值                         │
├──────────────────────────────────────────┤
│                                          │
│ 1. 解耦                                  │
│ ├─ 业务逻辑和增强逻辑分离                  │
│ ├─ 易于维护和扩展                         │
│ └─ 符合单一职责原则                       │
│                                         │
│ 2. 通用性                                │
│ ├─ 一套增强逻辑适用所有方法                │
│ ├─ 无需为每个类写代理                     │
│ └─ 支持动态添加功能                       │
│                                         │
│ 3. 灵活性                                │
│ ├─ 运行时动态决定是否代理                  │
│ ├─ 支持多个增强逻辑组合                    │
│ └─ 易于扩展新功能                         │
│                                         │
│ 4. 框架能力                               │
│ ├─ 实现AOP(面向切面编程)                  │
│ ├─ 实现事务管理                            │
│ ├─ 实现权限控制                            │
│ └─ 实现RPC框架                            │
│                                          │
└──────────────────────────────────────────┘

12.1 动态代理的工作原理总结

┌─────────────────────────────────────────────────────┐
│ 动态代理的完整工作流程                                 │
├─────────────────────────────────────────────────────┤
│                                                     │
│ 第1步:定义目标对象                                   │
│ ├─ 目标类(可以有接口或没有)                          │
│ └─ 包含真正的业务逻辑                                 │
│                                                     │
│ 第2步:定义增强逻辑                                   │
│ ├─ JDK:实现InvocationHandler                       │
│ └─ CGLIB:实现MethodInterceptor                     │
│                                                    │
│ 第3步:创建代理对象                                   │
│ ├─ JDK:Proxy.newProxyInstance()                    │
│ └─ CGLIB:Enhancer.create()                         │
│                                                    │
│ 第4步:调用代理对象                                  │
│ ├─ 代理对象拦截方法调用                              │
│ ├─ 执行前置增强                                     │
│ ├─ 调用原方法                                       │
│ ├─ 执行后置增强                                      │
│ └─ 返回结果                                          │
│                                                     │
└─────────────────────────────────────────────────────┘

12.2 JDK vs CGLIB的本质区别

┌──────────────────────────────────────────┐
│ JDK和CGLIB的本质区别                       │
├──────────────────────────────────────────┤
│                                          │
│ JDK动态代理                               │
│ ├─ 原理:实现接口                          │
│ ├─ 结构:代理类 implements 接口            │
│ ├─ 调用:通过接口多态                      │
│ └─ 反射:每次调用都需要反射                 │
│                                          │
│ CGLIB动态代理                             │
│ ├─ 原理:继承父类                          │
│ ├─ 结构:代理类 extends 目标类             │
│ ├─ 调用:通过继承多态                      │
│ └─ 反射:使用MethodProxy避免反射           │
│                                          │
│ 关键差异                                  │
│ ├─ 依赖:JDK需要接口,CGLIB不需要           │
│ ├─ 性能:CGLIB调用快(避免反射)            │
│ ├─ 创建:CGLIB创建慢(字节码生成)          │
│ └─ 限制:CGLIB无法代理final方法            │
│                                          │
└──────────────────────────────────────────┘

十三、反射与动态代理的关系

13.1 反射是基础

// 动态代理依赖反射机制

// JDK动态代理:显式使用反射
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // method是反射获取的Method对象
    return method.invoke(target, args);  // 通过反射调用原方法
}

// CGLIB动态代理:隐式使用反射
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
    // proxy.invokeSuper()内部也使用反射机制
    return proxy.invokeSuper(obj, args);
}

// 动态代理 = 反射 + 代理模式

13.2 动态代理是反射的应用

┌──────────────────────────────────────────┐
│    反射与动态代理的层次关系                │
├──────────────────────────────────────────┤
│                                          │
│  第1层:反射机制(基础)                  │
│  ├─ Class对象                            │
│  ├─ Method、Field、Constructor           │
│  └─ 运行时获取和操作类信息                │
│                                          │
│  第2层:动态代理(应用)                  │
│  ├─ 利用反射实现方法拦截                │
│  ├─ 在运行时生成代理类                  │
│  └─ 实现横切关注点                      │
│                                          │
│  第3层:AOP框架(高层)                   │
│  ├─ Spring AOP                          │
│  ├─ 自动创建代理                        │
│  └─ 用户无感知反射和代理                │
│                                          │
└──────────────────────────────────────────┘

十四、动态代理的实际应用场景

14.1 Spring AOP(面向切面编程)

// 场景:为所有Service方法添加日志和事务

@Aspect
@Component
public class TransactionAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beginTransaction() {
        System.out.println("开始事务");
    }

    @After("execution(* com.example.service.*.*(..))")
    public void commitTransaction() {
        System.out.println("提交事务");
    }

    @AfterThrowing("execution(* com.example.service.*.*(..))")
    public void rollbackTransaction() {
        System.out.println("回滚事务");
    }
}

// Spring自动为所有Service创建代理
// 用户无需关心代理的存在

14.2 权限控制

// 场景:为某些方法添加权限检查

public class PermissionInterceptor implements InvocationHandler {
    private Object target;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 检查权限
        if (!hasPermission(method)) {
            throw new SecurityException("无权限访问:" + method.getName());
        }

        // 调用原方法
        return method.invoke(target, args);
    }
}

// 使用
UserService userService = (UserService) Proxy.newProxyInstance(
    UserService.class.getClassLoader(),
    new Class[]{UserService.class},
    new PermissionInterceptor(new UserServiceImpl())
);

userService.delete(1);  // 自动检查权限

14.3 缓存

// 场景:为查询方法添加缓存

public class CacheInterceptor implements MethodInterceptor {
    private Map<String, Object> cache = new ConcurrentHashMap<>();

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
            throws Throwable {
        // 生成缓存key
        String cacheKey = method.getName() + Arrays.toString(args);

        // 检查缓存
        if (cache.containsKey(cacheKey)) {
            System.out.println("从缓存返回:" + cacheKey);
            return cache.get(cacheKey);
        }

        // 调用原方法
        Object result = proxy.invokeSuper(obj, args);

        // 存入缓存
        cache.put(cacheKey, result);

        return result;
    }
}

// 使用
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new CacheInterceptor());
UserService userService = (UserService) enhancer.create();

userService.getUserById(1);  // 第一次调用,执行查询
userService.getUserById(1);  // 第二次调用,从缓存返回

14.4 性能监控

// 场景:监控方法执行时间

public class PerformanceInterceptor implements InvocationHandler {
    private Object target;

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

        try {
            return method.invoke(target, args);
        } finally {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;

            System.out.println("方法:" + method.getName() +
                             ",耗时:" + duration + "ms");

            // 如果超过阈值,发送告警
            if (duration > 1000) {
                sendAlert("方法执行过慢:" + method.getName());
            }
        }
    }
}

14.5 RPC框架

// 场景:实现远程方法调用

public class RpcInvocationHandler implements InvocationHandler {
    private String serverAddress;

    public RpcInvocationHandler(String serverAddress) {
        this.serverAddress = serverAddress;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 序列化请求
        RpcRequest request = new RpcRequest(
            method.getDeclaringClass().getName(),
            method.getName(),
            method.getParameterTypes(),
            args
        );

        // 2. 发送网络请求
        RpcResponse response = sendRequest(serverAddress, request);

        // 3. 反序列化返回值
        return response.getResult();
    }
}

// 使用
UserServiceRPC userService = (UserServiceRPC) Proxy.newProxyInstance(
    UserServiceRPC.class.getClassLoader(),
    new Class[]{UserServiceRPC.class},
    new RpcInvocationHandler("192.168.1.100:8080")
);

// 看起来像本地调用,实际上是远程调用
User user = userService.getUserById(1);

十五、动态代理的常见问题

Q1: 为什么JDK代理需要接口,CGLIB不需要?

答:

  • JDK代理通过实现接口来创建代理类
  • 代理类需要实现目标接口的所有方法
  • 如果没有接口,就无法创建代理类
  • CGLIB通过继承来创建代理类
  • 代理类继承目标类,可以重写所有方法
  • 不需要接口,支持具体类

Q2: 为什么CGLIB无法代理final方法?

答:

  • CGLIB通过继承实现代理
  • final方法无法被重写
  • 所以CGLIB无法拦截final方法的调用
public class UserService {
    public final void save(User user) {  // final方法
        // CGLIB无法代理这个方法
    }
}

Q3: 动态代理会影响性能吗?

答:

  • 代理对象的创建有开销(特别是CGLIB)
  • 方法调用有额外开销(反射或字节码调用)
  • 但在实际应用中,这个开销通常可以接受

优化建议:

  1. 缓存代理对象,避免重复创建
  2. 避免在代理中做重操作
  3. 对性能敏感的代码,考虑使用MethodHandle

Q4: 如何选择JDK还是CGLIB?

答:

有接口?
├─ 是 → JDK(原生支持,无需依赖)
└─ 否 → CGLIB(支持具体类)

Spring框架?
├─ Spring Framework → JDK(默认)
└─ SpringBoot 2.x+ → CGLIB(默认)

可以通过配置修改默认选择

Q5: 代理对象和原对象是同一个吗?

答:

UserService target = new UserServiceImpl();
UserService proxy = createProxy(target);

System.out.println(target == proxy);  // false,不是同一个对象
System.out.println(target instanceof UserService);  // true
System.out.println(proxy instanceof UserService);   // true
  • 代理对象和原对象不是同一个
  • 但代理对象实现了相同的接口或继承了相同的类
  • 从外部看,它们是可以互换的

十六、动态代理的最佳实践

16.1 推荐做法

// 1. 优先使用框架提供的代理机制
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("方法调用前");
    }
}

// 2. 缓存代理对象
public class ProxyFactory {
    private static final Map<Class<?>, Object> proxyCache = new ConcurrentHashMap<>();

    public static <T> T getProxy(Class<T> targetClass, T target) {
        return (T) proxyCache.computeIfAbsent(targetClass, key -> {
            return Proxy.newProxyInstance(
                targetClass.getClassLoader(),
                new Class[]{targetClass},
                new MyInvocationHandler(target)
            );
        });
    }
}

// 3. 避免在代理中做重操作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // ✅ 轻操作:日志、权限检查
    System.out.println("方法调用");

    // ❌ 重操作:数据库查询、网络请求
    // userRepository.queryAll();

    return method.invoke(target, args);
}

// 4. 使用接口编程
public interface UserService {
    void save(User user);
}

public class UserServiceImpl implements UserService {
    @Override
    public void save(User user) {
        // 业务逻辑
    }
}

// 5. 异常处理
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        return method.invoke(target, args);
    } catch (InvocationTargetException e) {
        // 处理目标方法抛出的异常
        throw e.getTargetException();
    } catch (IllegalAccessException e) {
        // 处理访问异常
        throw new RuntimeException(e);
    }
}

16.2 避免做法

// ❌ 不要频繁创建代理对象
for (int i = 0; i < 1000; i++) {
    UserService proxy = Proxy.newProxyInstance(...);  // 每次都创建
}

// ❌ 不要在代理中做复杂业务逻辑
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // ❌ 不要在这里写业务逻辑
    if (method.getName().equals("save")) {
        // 复杂的业务逻辑
    }
    return method.invoke(target, args);
}

// ❌ 不要忽视异常处理
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // ❌ 没有异常处理
    return method.invoke(target, args);
}

// ❌ 不要混用多种代理方式
// 同一个应用中,尽量统一使用一种代理方式

十七、动态代理的演进

17.1 现代Java的替代方案

// 1. MethodHandle(Java 7+)
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(
    UserService.class,
    "save",
    MethodType.methodType(void.class, User.class)
);
mh.invoke(userService, user);

// 优势:性能接近直接调用,JVM可以优化

// 2. 字节码增强(ASM、Javassist、CGLIB)
// Spring使用CGLIB进行字节码增强
// 编译期生成代码,避免运行时反射成本

// 3. 编译期注解处理(APT)
// Lombok、Dagger等在编译期生成代码
// 避免运行时反射开销

17.2 Java 19+ 虚拟线程与代理

// Java 19引入虚拟线程,改变了异步编程方式
// 动态代理在虚拟线程中的应用

@Aspect
@Component
public class VirtualThreadAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        // 在虚拟线程中执行
        return ForkJoinTask.adapt(() -> {
            try {
                return joinPoint.proceed();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }).invoke();
    }
}

十八、关键要点回顾

18.1 动态代理的核心概念

概念 说明 用途
代理模式 为对象提供代理以控制访问 增强功能
JDK代理 基于接口的代理 有接口的类
CGLIB代理 基于继承的代理 无接口的类
InvocationHandler JDK代理的拦截器 拦截方法调用
MethodInterceptor CGLIB代理的拦截器 拦截方法调用
AOP 面向切面编程 横切关注点

18.2 动态代理的三个层次

1层:基础机制
  ├─ JDK动态代理(反射 + 接口)
  └─ CGLIB动态代理(字节码 + 继承)

第2层:应用框架
  ├─ Spring AOP
  ├─ 权限控制
  ├─ 缓存
  └─ 性能监控

第3层:高级特性
  ├─ 事务管理
  ├─ RPC框架
  ├─ 日志系统
  └─ 安全框架

18.3 动态代理的权衡

┌────────────────────────────────────┐
│    动态代理的权衡分析                │
├────────────────────────────────────┤
│                                    │
│  优点                               │
│  ✓ 解耦业务和增强逻辑              │
│  ✓ 通用性强                        │
│  ✓ 易于维护和扩展                  │
│  ✓ 支持动态添加功能                │
│                                    │
│  缺点                               │
│  ✗ 增加代码复杂度                  │
│  ✗ 性能开销(通常可接受)          │
│  ✗ 调试困难(代理隐藏细节)        │
│  ✗ 需要理解代理机制                │
│                                    │
│  结论                               │
│  → 框架层使用,业务层通过框架使用  │
│  → 理解原理,谨慎使用              │
│  → 优先使用Spring AOP等框架        │
│                                    │
└────────────────────────────────────┘

十九、记忆口诀

动态代理运行时,自动生成代理类
不改原始业务代码,横切关注点织入
JDK基于接口实现,CGLIB继承目标类

有接口用JDK,无接口用CGLIB
Spring默认JDK,Boot改用CGLIB
InvocationHandlerMethodInterceptor

反射是基础,代理是应用
AOP是框架,用户无感知
缓存代理对象,避免重复创建

权限日志事务,缓存性能监控
RPC框架安全,动态代理无处不在
理解原理很重要,框架使用最关键

二十、相关链接


⬅️ 异常体系 🏠 00-Java ➡️ 06-枚举