跳到正文
hello world

JDK 动态代理和 CGLIB 动态代理

在业务开发中,我们经常会遇到这样的问题:

一个业务方法已经写好了,但现在还需要在它执行前后增加一些通用功能,例如:

  • 记录接口日志
  • 校验用户权限
  • 统计方法耗时
  • 开启和提交事务
  • 查询缓存
  • 接口额度校验
  • 异常统一处理

最直接的写法,是把这些代码全部写进业务方法中:

public void createOrder() {

    System.out.println("记录请求日志");

    long start = System.currentTimeMillis();

    System.out.println("创建订单");

    long cost = System.currentTimeMillis() - start;

    System.out.println("方法耗时:" + cost + "ms");
}

这种代码虽然能运行,但存在明显问题:

业务代码和通用代码混在一起
多个方法需要重复编写相同逻辑
后续修改日志或权限逻辑时,需要修改大量业务代码

代理模式就是用来解决这类问题的。

代理对象挡在调用者和目标对象之间,在不修改原业务代码的前提下,对目标方法进行增强。

调用者
  ↓
代理对象
  ↓
目标对象

动态代理则是在程序运行过程中,自动创建代理对象。

Java 中常见的动态代理有两种:

JDK 动态代理
CGLIB 动态代理

Spring AOP 的底层也主要依赖这两种代理方式。


一、什么是代理模式

假设我们有一个订单服务:

public class OrderService {

    public void createOrder() {
        System.out.println("创建订单");
    }
}

调用者直接调用:

OrderService orderService = new OrderService();
orderService.createOrder();

调用关系是:

调用者 → OrderService

使用代理后,调用关系变成:

调用者 → OrderServiceProxy → OrderService

代理对象可以在调用目标方法之前和之后增加逻辑:

代理前置处理
目标方法执行
代理后置处理

例如:

记录开始时间
创建订单
计算执行耗时

代理模式的核心不是替代目标对象,而是:

在不修改目标类代码的情况下,对目标对象的方法进行增强。


二、静态代理

理解动态代理之前,可以先看静态代理。

定义一个接口:

public interface OrderService {

    void createOrder();
}

目标类:

public class OrderServiceImpl implements OrderService {

    @Override
    public void createOrder() {
        System.out.println("创建订单");
    }
}

代理类:

public class OrderServiceProxy implements OrderService {

    private final OrderService target;

    public OrderServiceProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void createOrder() {

        long start = System.currentTimeMillis();

        System.out.println("开始创建订单");

        target.createOrder();

        long cost = System.currentTimeMillis() - start;

        System.out.println("方法耗时:" + cost + "ms");
    }
}

使用:

public class Main {

    public static void main(String[] args) {

        OrderService target = new OrderServiceImpl();

        OrderService proxy = new OrderServiceProxy(target);

        proxy.createOrder();
    }
}

输出:

开始创建订单
创建订单
方法耗时:1ms

静态代理本身没有问题,但它有一个明显缺点:

每代理一个接口,通常都需要手动写一个代理类。

假如项目中有:

OrderService
UserService
ProductService
PaymentService

就需要分别创建:

OrderServiceProxy
UserServiceProxy
ProductServiceProxy
PaymentServiceProxy

这些代理类的逻辑可能都一样,只是目标对象不同。

动态代理解决的就是这个问题:

不再手动为每个目标对象编写代理类,而是在程序运行时自动生成代理对象。


三、JDK 动态代理

JDK 动态代理由 Java 标准库提供,核心类是:

java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler

它有一个重要限制:

被代理的目标类必须实现接口。


四、JDK 动态代理完整示例

先定义业务接口:

public interface OrderService {

    void createOrder(String productName);

    String queryOrder(Long orderId);
}

实现类:

public class OrderServiceImpl implements OrderService {

    @Override
    public void createOrder(String productName) {
        System.out.println("创建订单,商品:" + productName);
    }

    @Override
    public String queryOrder(Long orderId) {
        System.out.println("查询订单:" + orderId);
        return "ORDER-" + orderId;
    }
}

创建动态代理处理器:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LogInvocationHandler implements InvocationHandler {

    private final Object target;

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

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

        long start = System.currentTimeMillis();

        System.out.println(
                "开始执行方法:" + method.getName()
        );

        try {

            Object result = method.invoke(target, args);

            System.out.println(
                    "方法执行成功:" + method.getName()
            );

            return result;

        } catch (Exception e) {

            System.out.println(
                    "方法执行失败:" + method.getName()
            );

            throw e;

        } finally {

            long cost =
                    System.currentTimeMillis() - start;

            System.out.println(
                    "方法耗时:" + cost + "ms"
            );
        }
    }
}

创建代理对象:

import java.lang.reflect.Proxy;

public class Main {

    public static void main(String[] args) {

        OrderService target =
                new OrderServiceImpl();

        OrderService proxy =
                (OrderService) Proxy.newProxyInstance(
                        target.getClass().getClassLoader(),
                        target.getClass().getInterfaces(),
                        new LogInvocationHandler(target)
                );

        proxy.createOrder("Java 并发编程");

        String orderNo = proxy.queryOrder(1001L);

        System.out.println("查询结果:" + orderNo);
    }
}

输出:

开始执行方法:createOrder
创建订单,商品:Java 并发编程
方法执行成功:createOrder
方法耗时:1ms

开始执行方法:queryOrder
查询订单:1001
方法执行成功:queryOrder
方法耗时:0ms
查询结果:ORDER-1001

五、Proxy.newProxyInstance 的三个参数

创建代理对象的核心代码是:

Proxy.newProxyInstance(
        classLoader,
        interfaces,
        invocationHandler
);

完整写法:

OrderService proxy =
        (OrderService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new LogInvocationHandler(target)
        );

三个参数分别表示什么?

1. ClassLoader

target.getClass().getClassLoader()

代理类也是一个 Java 类。

既然是类,就需要类加载器把它加载进 JVM。

一般直接使用目标类的类加载器即可。


2. interfaces

target.getClass().getInterfaces()

表示代理对象需要实现哪些接口。

目标类:

public class OrderServiceImpl
        implements OrderService

所以生成的代理对象也会实现:

OrderService

调用者最终拿到的是一个实现了 OrderService 接口的代理对象。


3. InvocationHandler

new LogInvocationHandler(target)

它负责处理所有被代理方法的调用。

当执行:

proxy.createOrder("Java");

实际上会进入:

InvocationHandler.invoke(...)

因此可以把 InvocationHandler 理解为:

代理对象所有方法调用的统一入口。


六、InvocationHandler 的三个参数

public Object invoke(
        Object proxy,
        Method method,
        Object[] args
)

proxy

当前生成的代理对象。

一般不需要直接使用它。

特别注意,不要随便在 invoke() 中调用:

proxy.toString();

因为它可能再次进入 invoke(),造成递归调用。


method

当前被调用的方法对象。

例如调用:

proxy.createOrder("Java");

那么:

method.getName()

得到:

createOrder

还可以获取注解、返回值类型和参数信息:

method.getAnnotations();
method.getReturnType();
method.getParameterTypes();

args

当前方法的参数。

例如:

proxy.createOrder("Java");

那么:

args[0]

就是:

Java

七、method.invoke(target, args) 做了什么

这行代码是 JDK 动态代理中真正执行目标方法的地方:

Object result = method.invoke(target, args);

它表示:

使用反射,在目标对象 target 上调用当前 method,并把 args 作为参数传进去。

例如当前调用的是:

proxy.createOrder("Java");

最终相当于执行:

target.createOrder("Java");

只不过这里通过反射完成。

完整流程是:

调用 proxy.createOrder()
        ↓
进入 InvocationHandler.invoke()
        ↓
执行前置增强
        ↓
method.invoke(target, args)
        ↓
执行 OrderServiceImpl.createOrder()
        ↓
执行后置增强
        ↓
返回结果

八、封装通用的 JDK 代理工厂

为了避免每次都写 Proxy.newProxyInstance(),可以封装一个代理工厂。

import java.lang.reflect.Proxy;

public class JdkProxyFactory {

    private final Object target;

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

    public Object getProxy() {

        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new LogInvocationHandler(target)
        );
    }
}

使用:

public class Main {

    public static void main(String[] args) {

        OrderService target =
                new OrderServiceImpl();

        OrderService proxy =
                (OrderService) new JdkProxyFactory(target)
                        .getProxy();

        proxy.createOrder("MySQL 实战");
    }
}

使用泛型进一步优化:

import java.lang.reflect.Proxy;

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)
        );
    }
}

调用:

OrderService proxy =
        JdkProxyFactory.createProxy(
                new OrderServiceImpl()
        );

proxy.createOrder("Spring Boot");

九、CGLIB 动态代理

JDK 动态代理要求目标类必须实现接口。

但有些类没有接口:

public class ProductService {

    public void queryProduct() {
        System.out.println("查询商品");
    }
}

这时无法直接使用 JDK 动态代理。

CGLIB 的解决方案是:

动态创建目标类的子类,通过重写父类方法实现代理。

原来的类:

ProductService

CGLIB 运行时大致生成:

public class ProductServiceProxy
        extends ProductService {

    @Override
    public void queryProduct() {
        // 代理逻辑
    }
}

所以 CGLIB 不要求目标类实现接口。


十、CGLIB 依赖

现代 Spring 项目中一般已经包含 CGLIB 相关能力。

单独演示时,可以使用 Spring 提供的 CGLIB 包:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
</dependency>

使用的类通常位于:

org.springframework.cglib.proxy.Enhancer
org.springframework.cglib.proxy.MethodInterceptor
org.springframework.cglib.proxy.MethodProxy

十一、CGLIB 动态代理完整示例

目标类不实现任何接口:

public class ProductService {

    public void createProduct(String productName) {
        System.out.println("创建商品:" + productName);
    }

    public String queryProduct(Long productId) {
        System.out.println("查询商品:" + productId);
        return "PRODUCT-" + productId;
    }
}

创建 CGLIB 方法拦截器:

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LogMethodInterceptor
        implements MethodInterceptor {

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

        long start = System.currentTimeMillis();

        System.out.println(
                "开始执行方法:" + method.getName()
        );

        try {

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

            System.out.println(
                    "方法执行成功:" + method.getName()
            );

            return result;

        } catch (Exception e) {

            System.out.println(
                    "方法执行失败:" + method.getName()
            );

            throw e;

        } finally {

            long cost =
                    System.currentTimeMillis() - start;

            System.out.println(
                    "方法耗时:" + cost + "ms"
            );
        }
    }
}

创建代理对象:

import org.springframework.cglib.proxy.Enhancer;

public class CglibProxyFactory {

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(
            Class<T> targetClass
    ) {

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(targetClass);

        enhancer.setCallback(
                new LogMethodInterceptor()
        );

        return (T) enhancer.create();
    }
}

使用:

public class Main {

    public static void main(String[] args) {

        ProductService proxy =
                CglibProxyFactory.createProxy(
                        ProductService.class
                );

        proxy.createProduct("机械键盘");

        String productNo =
                proxy.queryProduct(1001L);

        System.out.println(productNo);
    }
}

输出:

开始执行方法:createProduct
创建商品:机械键盘
方法执行成功:createProduct
方法耗时:1ms

开始执行方法:queryProduct
查询商品:1001
方法执行成功:queryProduct
方法耗时:0ms
PRODUCT-1001

十二、CGLIB 的执行流程

当调用:

proxy.queryProduct(1001L);

执行流程大致是:

调用 CGLIB 生成的子类对象
        ↓
进入 MethodInterceptor.intercept()
        ↓
执行前置增强
        ↓
调用父类原方法
        ↓
执行后置增强
        ↓
返回结果

核心代码:

methodProxy.invokeSuper(proxy, args);

它表示:

调用代理对象父类中的原始方法。

因为代理对象本身就是目标类的子类,所以可以调用父类实现。


十三、为什么使用 invokeSuper,而不是 method.invoke

CGLIB 中推荐使用:

methodProxy.invokeSuper(proxy, args);

而不是:

method.invoke(proxy, args);

因为 proxy 本身是代理对象。

如果执行:

method.invoke(proxy, args);

可能再次调用代理对象的方法,再次进入 intercept(),最终形成递归调用。

正确方式是:

methodProxy.invokeSuper(proxy, args);

直接执行父类原始方法。


十四、带构造参数的 CGLIB 代理

如果目标类没有无参构造方法:

public class ProductService {

    private final String tenantId;

    public ProductService(String tenantId) {
        this.tenantId = tenantId;
    }

    public void queryProduct() {
        System.out.println(
                tenantId + " 查询商品"
        );
    }
}

直接调用:

enhancer.create();

会失败,因为 CGLIB 创建子类对象时需要调用父类构造方法。

需要传入构造参数:

ProductService proxy =
        (ProductService) enhancer.create(
                new Class[]{String.class},
                new Object[]{"tenant-1001"}
        );

完整代码:

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(ProductService.class);

enhancer.setCallback(
        new LogMethodInterceptor()
);

ProductService proxy =
        (ProductService) enhancer.create(
                new Class[]{String.class},
                new Object[]{"tenant-1001"}
        );

十五、JDK 动态代理和 CGLIB 的核心区别

对比项 JDK 动态代理 CGLIB 动态代理
实现方式 实现目标接口 继承目标类
是否要求接口 必须有接口 不要求接口
代理对象类型 接口实现类 目标类的子类
核心入口 InvocationHandler MethodInterceptor
目标方法调用 Method.invoke MethodProxy.invokeSuper
final 类 可以代理接口,但不能代理类本身 不能代理
final 方法 接口方式通常不受影响 不能重写,因此无法增强
private 方法 不能通过接口代理 不能被子类重写
常见应用 面向接口开发 无接口类代理

可以这样记:

JDK 动态代理是“实现接口”,CGLIB 动态代理是“继承目标类”。


十六、为什么 CGLIB 不能代理 final 类

CGLIB 的原理是继承目标类:

class ProxyClass extends TargetClass {
}

如果目标类是:

public final class ProductService {
}

Java 不允许其他类继承它。

因此 CGLIB 无法创建:

class ProductServiceProxy
        extends ProductService

同理,如果方法是:

public final void queryProduct() {
}

子类不能重写这个方法,所以 CGLIB 也无法对它进行增强。


十七、业务示例:统一记录接口耗时

假设系统中有多个业务服务:

public interface UserService {

    void createUser(String username);

    String getUser(Long userId);
}

实现类:

public class UserServiceImpl
        implements UserService {

    @Override
    public void createUser(String username) {
        System.out.println("创建用户:" + username);
    }

    @Override
    public String getUser(Long userId) {
        System.out.println("查询用户:" + userId);
        return "user-" + userId;
    }
}

定义通用耗时代理:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class PerformanceInvocationHandler
        implements InvocationHandler {

    private final Object target;

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

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

        long start = System.nanoTime();

        try {

            return method.invoke(target, args);

        } finally {

            long cost =
                    System.nanoTime() - start;

            System.out.printf(
                    "%s.%s 执行耗时:%.3f ms%n",
                    target.getClass().getSimpleName(),
                    method.getName(),
                    cost / 1_000_000.0
            );
        }
    }
}

代理工厂:

import java.lang.reflect.Proxy;

public class PerformanceProxyFactory {

    @SuppressWarnings("unchecked")
    public static <T> T create(T target) {

        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new PerformanceInvocationHandler(target)
        );
    }
}

使用:

UserService userService =
        PerformanceProxyFactory.create(
                new UserServiceImpl()
        );

userService.createUser("zhangsan");

userService.getUser(1001L);

以后其他实现了接口的服务,也可以直接使用同一个代理工厂:

OrderService orderService =
        PerformanceProxyFactory.create(
                new OrderServiceImpl()
        );

这就是动态代理相对静态代理的价值:

一套代理逻辑可以复用到多个不同的目标对象上。


十八、业务示例:接口额度校验

假设查询接口执行前,需要检查用户剩余额度。

业务接口:

public interface ReportService {

    String generateReport(Long userId);
}

实现类:

public class ReportServiceImpl
        implements ReportService {

    @Override
    public String generateReport(Long userId) {

        System.out.println("生成数据报告");

        return "report-success";
    }
}

额度服务:

public class QuotaService {

    public boolean hasQuota(Long userId) {
        return userId != null && userId > 0;
    }

    public void consume(Long userId) {
        System.out.println(
                "用户 " + userId + " 扣减一次额度"
        );
    }
}

代理处理器:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class QuotaInvocationHandler
        implements InvocationHandler {

    private final Object target;

    private final QuotaService quotaService;

    public QuotaInvocationHandler(
            Object target,
            QuotaService quotaService
    ) {
        this.target = target;
        this.quotaService = quotaService;
    }

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

        Long userId = (Long) args[0];

        if (!quotaService.hasQuota(userId)) {
            throw new RuntimeException(
                    "用户额度不足"
            );
        }

        Object result =
                method.invoke(target, args);

        quotaService.consume(userId);

        return result;
    }
}

创建代理:

ReportService target =
        new ReportServiceImpl();

QuotaService quotaService =
        new QuotaService();

ReportService proxy =
        (ReportService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new QuotaInvocationHandler(
                        target,
                        quotaService
                )
        );

调用:

String result =
        proxy.generateReport(1001L);

System.out.println(result);

执行流程:

检查额度
    ↓
执行报告查询
    ↓
查询成功
    ↓
扣减额度

这和我们在 Spring AOP 中编写的 @Around 切面非常相似。


十九、动态代理与 Spring AOP 的关系

Spring AOP 本质上就是基于代理实现的。

假设我们写了一个切面:

@Around("@annotation(apiLog)")
public Object around(
        ProceedingJoinPoint joinPoint,
        ApiLog apiLog
) throws Throwable {

    System.out.println("执行前");

    Object result = joinPoint.proceed();

    System.out.println("执行后");

    return result;
}

表面上看,是 Spring 自动拦截了业务方法。

底层实际上可以理解为:

Spring 创建业务 Bean
        ↓
Spring 为业务 Bean 创建代理对象
        ↓
容器中保存的是代理对象
        ↓
调用代理对象的方法
        ↓
进入切面
        ↓
调用目标方法

例如:

@Service
public class UserServiceImpl
        implements UserService {
}

注入:

@Resource
private UserService userService;

这里注入的 userService 很可能不是原始的 UserServiceImpl,而是 Spring 创建的代理对象。


二十、Spring 如何选择 JDK 和 CGLIB

通常可以这样理解:

目标类实现了接口

Spring 可以使用 JDK 动态代理:

代理对象实现目标接口

目标类没有实现接口

Spring 使用 CGLIB:

代理对象继承目标类

在 Spring Boot 中,也可以通过配置影响代理方式:

spring:
  aop:
    proxy-target-class: true

true 表示优先使用基于类的代理,也就是 CGLIB 代理。

不过具体行为还会受到 Spring 版本、代理配置和目标类特征等因素影响。


二十一、为什么同类内部调用会导致 AOP 失效

这是 Spring AOP 中非常常见的问题。

例如:

@Service
public class OrderService {

    public void createOrder() {
        this.saveOrder();
    }

    @Transactional
    public void saveOrder() {
        System.out.println("保存订单");
    }
}

外部调用:

orderService.createOrder();

执行 createOrder() 时,会进入代理对象。

但是在 createOrder() 内部:

this.saveOrder();

这里的 this 指向的是目标对象本身,不是代理对象。

调用关系变成:

代理对象.createOrder()
        ↓
目标对象.createOrder()
        ↓
目标对象.this.saveOrder()

saveOrder() 没有再次经过代理对象,所以事务、日志、缓存等切面可能不会生效。

正确的代理调用流程应该是:

调用者
  ↓
代理对象
  ↓
切面逻辑
  ↓
目标方法

this.xxx() 绕过了代理对象。

常见解决方法有:

把被调用方法拆到另一个 Bean
通过代理对象调用
重新设计业务边界

最推荐的是拆分到另一个 Bean:

@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderPersistenceService
            orderPersistenceService;

    public void createOrder() {
        orderPersistenceService.saveOrder();
    }
}
@Service
public class OrderPersistenceService {

    @Transactional
    public void saveOrder() {
        System.out.println("保存订单");
    }
}

二十二、异常处理中的一个细节

JDK 动态代理中常见写法:

method.invoke(target, args);

如果目标方法抛出异常,反射会把原异常包装成:

InvocationTargetException

可以把真实异常取出来:

try {

    return method.invoke(target, args);

} catch (InvocationTargetException e) {

    throw e.getTargetException();
}

完整写法:

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

    try {

        return method.invoke(target, args);

    } catch (InvocationTargetException e) {

        throw e.getTargetException();
    }
}

这样调用者拿到的是业务方法本身抛出的异常,而不是反射包装异常。


二十三、动态代理适合解决什么问题

动态代理适合处理横跨多个业务模块的通用逻辑。

例如:

日志记录
权限校验
事务管理
接口耗时统计
缓存处理
接口额度校验
统一异常处理
审计记录
重试机制
数据权限

这些逻辑的共同特点是:

它们不是某一个具体业务的核心,但很多业务方法都需要使用。

例如订单创建方法的核心应该是:

创建订单

而不是:

检查权限
记录日志
开启事务
统计耗时
提交事务
发送监控指标

动态代理可以把这些通用逻辑从业务代码中抽离出来。


二十四、动态代理不适合解决什么问题

代理不是所有问题都应该使用。

如果逻辑只属于某个具体方法,例如:

订单价格计算
商品库存判断
用户等级计算
报告数据组装

这些属于业务本身,不应该为了“解耦”而全部放进代理。

代理更适合:

通用、重复、横跨多个模块的逻辑

不适合:

和具体业务强相关的核心逻辑

二十五、JDK 动态代理和 CGLIB 怎么选择

如果自己编写动态代理,可以按照下面的方式选择。

有接口,且面向接口编程

优先考虑 JDK 动态代理:

public class UserServiceImpl
        implements UserService

优点是结构清晰,调用者依赖接口。


没有接口

使用 CGLIB:

public class ProductService {
}

CGLIB 可以直接创建目标类的子类。


使用 Spring AOP

大部分情况下不需要手动选择。

交给 Spring 创建代理即可。

开发者更需要关注的是:

方法是否被 Spring Bean 管理
调用是否经过代理对象
方法是否可以被代理
是否存在同类内部调用
final 方法是否阻止 CGLIB 增强

二十六、总结

代理模式的核心是:

在不修改目标类代码的前提下,通过代理对象对目标方法进行增强。

静态代理需要手动编写代理类:

一个目标类型对应一个代理类

动态代理则是在运行时自动创建代理对象:

一套代理逻辑可以复用到多个目标对象

JDK 动态代理的核心:

目标类实现接口
Proxy.newProxyInstance()
InvocationHandler.invoke()
Method.invoke()

CGLIB 动态代理的核心:

创建目标类的子类
Enhancer
MethodInterceptor.intercept()
MethodProxy.invokeSuper()

两者最核心的区别可以记成一句话:

JDK 动态代理通过实现接口创建代理,CGLIB 通过继承目标类创建代理。

而 Spring AOP 可以理解为:

Spring 自动帮我们创建代理对象
        ↓
把日志、事务、权限、缓存等通用逻辑
织入目标方法的执行过程

当我们真正理解 JDK 动态代理和 CGLIB 后,再看 Spring AOP、@Transactional@Async、缓存注解和自定义切面,就不会只停留在“加一个注解就能生效”的层面,而是能够理解它们背后的代理调用过程。

评论

填写昵称与邮箱即可评论,无需登录。

推荐阅读