Skip to content

代理模式 (Proxy Pattern)

一、定义

一句话概括:为其他对象提供一种代理,以控制对这个对象的访问。

官方定义(GoF):Provide a surrogate or placeholder for another object to control access to it.

代理模式的核心思想是:引入一个代理对象来间接访问目标对象,在访问目标对象的前后可以添加额外的控制逻辑。代理对象和目标对象实现相同的接口,客户端无法区分代理和真实对象。


二、解决的问题

2.1 什么场景下需要代理模式?

  • 访问控制:控制对敏感对象的访问权限。
  • 延迟加载:大对象的创建开销很大,需要延迟到真正使用时才创建。
  • 远程代理:本地代理远程对象,屏蔽网络通信细节(如 RMI、gRPC)。
  • 日志/监控:在不修改原代码的情况下,记录方法调用日志。
  • 缓存:为耗时操作添加缓存层。
  • 事务管理:在方法调用前后添加事务控制。

2.2 不用代理模式会有什么问题?

java
// 假设我们要给 UserService 的所有方法添加日志和性能监控
class UserService {
    public User getUser(Long id) {
        // 业务逻辑
        return userRepository.findById(id);
    }

    public void saveUser(User user) {
        // 业务逻辑
        userRepository.save(user);
    }
}

// 不用代理:每个方法都要手动添加日志
class UserServiceWithLog {
    public User getUser(Long id) {
        long start = System.currentTimeMillis();
        System.out.println("开始调用 getUser, 参数: " + id);
        try {
            User user = userRepository.findById(id);
            System.out.println("getUser 完成, 耗时: " + (System.currentTimeMillis() - start) + "ms");
            return user;
        } catch (Exception e) {
            System.out.println("getUser 异常: " + e.getMessage());
            throw e;
        }
    }
    // saveUser 也要重复相同的日志代码...
}
问题1:代码重复 —— 每个方法都要写相同的日志代码
问题2:违反单一职责 —— 业务类混杂了日志、监控等横切关注点
问题3:无法统一管理 —— 修改日志格式需要改所有方法
问题4:紧耦合 —— 日志代码和业务代码耦合在一起

三、结构

3.1 文字描述

代理模式包含以下角色:

  • Subject(抽象主题):定义 RealSubject 和 Proxy 的公共接口。
  • RealSubject(真实主题):真正的业务对象,代理所代表的真实实体。
  • Proxy(代理):持有 RealSubject 的引用,控制对 RealSubject 的访问。

3.2 ASCII 类图

┌─────────────────────────┐
│  <<interface>>          │
│     Subject             │
├─────────────────────────┤
│ + request()             │
└────────────┬────────────┘

    ┌────────┴──────────────────────┐
    │                               │
┌───┴──────────────┐    ┌──────────┴──────────────┐
│   RealSubject    │    │         Proxy            │
├──────────────────┤    ├─────────────────────────┤
│ + request()      │    │ - realSubject: Subject   │
│   实际业务逻辑    │    │ + request()              │
└──────────────────┘    │   preProcess()           │
                        │   realSubject.request()  │
                        │   postProcess()          │
                        └─────────────────────────┘

3.3 三种代理模式对比

┌──────────────┬──────────────────────┬──────────────────────────────┐
│   代理类型    │       实现方式         │          特点                │
├──────────────┼──────────────────────┼──────────────────────────────┤
│   静态代理    │ 手动编写代理类         │ 编译时确定,一个代理对应一个接口│
│ JDK 动态代理  │ java.lang.reflect.Proxy│ 运行时生成,基于接口          │
│ CGLIB 代理   │ 字节码增强             │ 运行时生成,基于继承(无需接口)│
└──────────────┴──────────────────────┴──────────────────────────────┘

四、代码实现

4.1 基础实现

场景:图片加载器(延迟加载 + 访问控制)

java
// ============ Subject:抽象主题 ============
interface Image {
    void display();
    String getFileName();
}

// ============ RealSubject:真实主题 ============
class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk();  // 模拟加载开销
    }

    private void loadFromDisk() {
        System.out.println("从磁盘加载图片: " + fileName);
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
    }

    @Override
    public void display() {
        System.out.println("显示图片: " + fileName);
    }

    @Override
    public String getFileName() {
        return fileName;
    }
}

// ============ Proxy:代理主题(延迟加载)============
class ImageProxy implements Image {
    private String fileName;
    private RealImage realImage;  // 延迟初始化

    public ImageProxy(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void display() {
        // 延迟加载:只在真正需要时才创建 RealImage
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }

    @Override
    public String getFileName() {
        return fileName;
    }
}

// ============ 客户端测试 ============
public class ProxyDemo {
    public static void main(String[] args) {
        // 创建代理时,RealImage 不会加载
        Image image1 = new ImageProxy("photo1.jpg");
        Image image2 = new ImageProxy("photo2.jpg");

        System.out.println("代理创建完成,图片尚未加载");

        // 只有真正显示时才加载图片
        image1.display();  // 此时才加载 photo1.jpg
        image1.display();  // 第二次显示,直接使用已加载的图片
        image2.display();  // 加载 photo2.jpg
    }
}

4.2 进阶实现

4.2.1 JDK 动态代理

JDK 动态代理基于接口,运行时通过 java.lang.reflect.Proxy 动态生成代理类。

java
// ============ Subject 接口 ============
interface UserService {
    User getUser(Long id);
    void saveUser(User user);
    void deleteUser(Long id);
}

// ============ RealSubject ============
class UserServiceImpl implements UserService {
    @Override
    public User getUser(Long id) {
        System.out.println("查询用户: " + id);
        return new User(id, "张三");
    }

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

    @Override
    public void deleteUser(Long id) {
        System.out.println("删除用户: " + id);
    }
}

// ============ InvocationHandler:调用处理器 ============
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 {
        // 前置增强
        long start = System.currentTimeMillis();
        System.out.println("[LOG] 开始调用 " + method.getName() +
            ", 参数: " + Arrays.toString(args));

        try {
            // 调用真实对象的方法
            Object result = method.invoke(target, args);

            // 后置增强
            long elapsed = System.currentTimeMillis() - start;
            System.out.println("[LOG] " + method.getName() +
                " 完成, 耗时: " + elapsed + "ms, 结果: " + result);

            return result;
        } catch (Exception e) {
            // 异常增强
            System.out.println("[LOG] " + method.getName() +
                " 异常: " + e.getMessage());
            throw e;
        }
    }
}

// ============ 代理工厂 ============
class JdkProxyFactory {
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),      // 类加载器
            target.getClass().getInterfaces(),        // 接口数组
            new LogInvocationHandler(target)          // 调用处理器
        );
    }
}

// ============ 测试 ============
public class JdkDynamicProxyDemo {
    public static void main(String[] args) {
        // 原始对象
        UserService userService = new UserServiceImpl();

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

        // 通过代理调用
        proxy.getUser(1L);
        proxy.saveUser(new User(2L, "李四"));
        proxy.deleteUser(3L);

        // 查看生成的代理类
        System.out.println("代理类: " + proxy.getClass().getName());
        // 输出:com.sun.proxy.$Proxy0
    }
}

4.2.2 CGLIB 动态代理

CGLIB 基于继承,运行时通过字节码增强生成目标类的子类作为代理,不需要接口。

java
// ============ 目标类(没有接口)============
class ProductService {
    public Product getProduct(Long id) {
        System.out.println("查询商品: " + id);
        return new Product(id, "iPhone 15");
    }

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

// ============ MethodInterceptor:方法拦截器 ============
class PerformanceInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                            MethodProxy proxy) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("[性能监控] 开始: " + method.getName());

        try {
            // 调用父类(目标对象)的方法
            // 注意:使用 proxy.invokeSuper 而不是 method.invoke
            Object result = proxy.invokeSuper(obj, args);
            long elapsed = System.currentTimeMillis() - start;
            System.out.println("[性能监控] " + method.getName() +
                " 耗时: " + elapsed + "ms");

            // 慢查询告警
            if (elapsed > 100) {
                System.out.println("[性能告警] " + method.getName() +
                    " 执行超过 100ms!");
            }
            return result;
        } catch (Exception e) {
            System.out.println("[性能监控] " + method.getName() +
                " 异常: " + e.getMessage());
            throw e;
        }
    }
}

// ============ CGLIB 代理工厂 ============
class CglibProxyFactory {
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);               // 设置父类
        enhancer.setCallback(new PerformanceInterceptor());
        return (T) enhancer.create();
    }
}

// ============ 测试 ============
public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建 CGLIB 代理
        ProductService proxy = CglibProxyFactory.createProxy(ProductService.class);

        proxy.getProduct(1L);
        // finalMethod 不会被代理
        proxy.finalMethod();

        System.out.println("代理类: " + proxy.getClass().getName());
        // 输出:ProductService$$EnhancerByCGLIB$$xxxxx
    }
}

4.2.3 JDK 动态代理 vs CGLIB 代理对比

java
// ============ 对比总结 ============
/*
┌──────────────┬──────────────────────┬──────────────────────────────┐
│    对比维度    │    JDK 动态代理        │       CGLIB 代理              │
├──────────────┼──────────────────────┼──────────────────────────────┤
│   实现原理    │ 基于接口 + 反射         │ 基于继承 + 字节码增强          │
│   接口要求    │ 必须有接口              │ 不需要接口                     │
│   性能        │ 反射调用,早期略慢       │ 字节码直接调用,性能更好        │
│   final 方法  │ 不受影响(接口无 final) │ 不能代理(final 方法不可重写)  │
│   创建成本    │ 低(只生成一个类)       │ 高(需要生成字节码)            │
│   调用成本    │ 较高(反射)             │ 较低(FastClass 机制)         │
│ Spring 默认   │ 有接口时使用             │ 无接口时使用                   │
└──────────────┴──────────────────────┴──────────────────────────────┘
*/

4.3 生产级实现

4.3.1 Spring AOP 原理(重点)

Spring AOP 是代理模式最经典的工业级应用。其核心原理:在 Bean 初始化时,通过 BeanPostProcessor 创建代理对象替代原始 Bean

java
// ============ Spring AOP 底层原理模拟 ============

// 1. 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Loggable {
    String value() default "";
}

// 2. 切面逻辑(Advice)
class LoggingAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();
        Object[] args = invocation.getArguments();

        System.out.println("[AOP-BEFORE] " + methodName + " 参数: " + Arrays.toString(args));
        long start = System.currentTimeMillis();

        try {
            Object result = invocation.proceed();  // 执行目标方法
            System.out.println("[AOP-AFTER] " + methodName + " 耗时: " +
                (System.currentTimeMillis() - start) + "ms");
            return result;
        } catch (Exception e) {
            System.out.println("[AOP-EXCEPTION] " + methodName + " " + e.getMessage());
            throw e;
        }
    }
}

// 3. 模拟 Spring 的代理创建(BeanPostProcessor)
class AopProxyCreator {
    // 判断是否需要创建代理
    public static boolean shouldProxy(Class<?> clazz) {
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Loggable.class)) {
                return true;
            }
        }
        return false;
    }

    // 创建代理对象
    public static Object createProxy(Object target) {
        Class<?> clazz = target.getClass();
        if (clazz.getInterfaces().length > 0) {
            // 有接口:使用 JDK 动态代理
            return Proxy.newProxyInstance(
                clazz.getClassLoader(),
                clazz.getInterfaces(),
                (proxy, method, args) -> {
                    // 检查方法是否有 @Loggable 注解
                    Method targetMethod = clazz.getMethod(method.getName(),
                        method.getParameterTypes());
                    if (targetMethod.isAnnotationPresent(Loggable.class)) {
                        System.out.println("[AOP] 拦截: " + method.getName());
                        long start = System.currentTimeMillis();
                        Object result = method.invoke(target, args);
                        System.out.println("[AOP] " + method.getName() +
                            " 耗时: " + (System.currentTimeMillis() - start) + "ms");
                        return result;
                    }
                    return method.invoke(target, args);
                }
            );
        } else {
            // 无接口:使用 CGLIB 代理
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(new org.springframework.cglib.proxy.MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method,
                        Object[] args, MethodProxy proxy) throws Throwable {
                    Method targetMethod = clazz.getMethod(method.getName(),
                        method.getParameterTypes());
                    if (targetMethod.isAnnotationPresent(Loggable.class)) {
                        System.out.println("[AOP] 拦截: " + method.getName());
                        long start = System.currentTimeMillis();
                        Object result = proxy.invokeSuper(obj, args);
                        System.out.println("[AOP] " + method.getName() +
                            " 耗时: " + (System.currentTimeMillis() - start) + "ms");
                        return result;
                    }
                    return proxy.invokeSuper(obj, args);
                }
            });
            return enhancer.create();
        }
    }
}

4.3.2 Spring Boot 实际 AOP 应用

java
// ============ 1. 自定义注解 ============
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface RateLimit {
    int value() default 10;   // 每秒允许的请求数
    int timeout() default 1;  // 超时时间(秒)
}

// ============ 2. 切面 ============
@Aspect
@Component
class RateLimitAspect {
    private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();

    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
        String key = point.getSignature().toShortString();
        RateLimiter limiter = limiters.computeIfAbsent(key,
            k -> RateLimiter.create(rateLimit.value()));

        if (limiter.tryAcquire(rateLimit.timeout(), TimeUnit.SECONDS)) {
            return point.proceed();
        } else {
            throw new RuntimeException("请求过于频繁,请稍后重试");
        }
    }
}

// ============ 3. 使用 ============
@Service
class ApiService {
    @RateLimit(value = 5)  // 每秒最多 5 次调用
    public String queryData(String param) {
        return "data: " + param;
    }
}

// ============ 4. Spring AOP 核心流程图 ============
/*
 *  Spring 容器启动
 *       │
 *       ▼
 *  Bean 实例化 (new ApiService())
 *       │
 *       ▼
 *  BeanPostProcessor.postProcessAfterInitialization()
 *       │
 *       ├── 检查 Bean 是否需要代理
 *       │   (是否有 @Aspect 匹配的切点)
 *       │
 *       ├── 需要代理
 *       │   ├── 有接口 → JDK 动态代理
 *       │   └── 无接口 → CGLIB 代理
 *       │
 *       ▼
 *  返回代理对象(替换原始 Bean)
 *       │
 *       ▼
 *  客户端调用 Bean 方法
 *       │
 *       ▼
 *  代理对象拦截 → 执行 Advice 链 → 调用目标方法 → 返回结果
 */

4.3.3 远程代理(RPC 调用)

java
// ============ 远程代理:本地代理远程服务 ============

// 服务接口
interface OrderRpcService {
    OrderDTO getOrder(Long orderId);
    List<OrderDTO> listOrders(Long userId);
}

// 远程代理(RPC 代理)
class OrderRpcProxy implements OrderRpcService {
    private String remoteUrl;
    private RestTemplate restTemplate;

    public OrderRpcProxy(String remoteUrl) {
        this.remoteUrl = remoteUrl;
        this.restTemplate = new RestTemplate();
    }

    @Override
    public OrderDTO getOrder(Long orderId) {
        // 封装远程调用细节
        String url = remoteUrl + "/api/orders/" + orderId;
        System.out.println("[RPC] 远程调用: GET " + url);
        try {
            return restTemplate.getForObject(url, OrderDTO.class);
        } catch (Exception e) {
            throw new RuntimeException("RPC 调用失败: " + url, e);
        }
    }

    @Override
    public List<OrderDTO> listOrders(Long userId) {
        String url = remoteUrl + "/api/orders?userId=" + userId;
        System.out.println("[RPC] 远程调用: GET " + url);
        try {
            OrderDTO[] orders = restTemplate.getForObject(url, OrderDTO[].class);
            return orders != null ? Arrays.asList(orders) : Collections.emptyList();
        } catch (Exception e) {
            throw new RuntimeException("RPC 调用失败: " + url, e);
        }
    }
}

// 客户端使用
// OrderRpcService orderService = new OrderRpcProxy("http://order-service:8080");
// OrderDTO order = orderService.getOrder(123L);
// 客户端完全不知道这是远程调用!

4.3.4 缓存代理

java
// ============ 缓存代理:为耗时操作添加缓存 ============
class CachingProxy implements UserService {
    private UserService delegate;
    private Map<Long, User> cache = new ConcurrentHashMap<>();

    public CachingProxy(UserService delegate) {
        this.delegate = delegate;
    }

    @Override
    public User getUser(Long id) {
        // 先从缓存获取
        User cached = cache.get(id);
        if (cached != null) {
            System.out.println("[缓存命中] userId=" + id);
            return cached;
        }
        // 缓存未命中,调用真实服务
        User user = delegate.getUser(id);
        cache.put(id, user);
        System.out.println("[缓存更新] userId=" + id);
        return user;
    }

    @Override
    public void saveUser(User user) {
        delegate.saveUser(user);
        // 更新缓存
        cache.put(user.getId(), user);
    }

    @Override
    public void deleteUser(Long id) {
        delegate.deleteUser(id);
        // 清除缓存
        cache.remove(id);
    }
}

五、优缺点

优点

优点说明
职责清晰真实对象专注业务,代理对象处理横切关注点
高扩展性无需修改目标对象即可添加功能
智能化可以添加访问控制、延迟加载、缓存等智能逻辑
解耦客户端与真实对象解耦,代理可以透明替换真实对象

缺点

缺点说明
性能开销多一层代理调用,有轻微性能损失
复杂度增加引入代理类,系统复杂度增加
调试困难代理链较长时,调用栈深,排查问题困难
CGLIB 限制不能代理 final 类和 final 方法,代理类不能是 final

六、适用场景

  1. AOP(面向切面编程):日志、事务、权限、缓存等横切关注点。
  2. 延迟加载:大对象(如图片、报表)的按需加载。
  3. 访问控制:权限校验、防火墙代理。
  4. 远程代理:RPC、RMI、gRPC 等远程服务调用。
  5. 缓存代理:为耗时操作添加缓存层。
  6. 虚拟代理:为创建开销大的对象提供占位符。
  7. 保护代理:控制对敏感对象的访问。

七、JDK / Spring 框架中的实际应用

7.1 JDK 中的代理

java
// 1. java.lang.reflect.Proxy —— JDK 动态代理核心类
// 2. java.rmi.* —— RMI 远程代理
// 3. Collections.unmodifiableList() —— 保护代理(禁止修改)
List<String> list = Collections.unmodifiableList(new ArrayList<>());
// 4. Collections.synchronizedList() —— 同步代理
List<String> syncList = Collections.synchronizedList(new ArrayList<>());

7.2 Spring 中的代理(核心)

java
// 1. Spring AOP 全面基于代理模式
//    @Transactional  → 事务代理
//    @Cacheable      → 缓存代理
//    @Async          → 异步代理
//    @Retryable      → 重试代理
//    @PreAuthorize   → 安全代理

// 2. Spring 的代理创建策略
//    spring.aop.proxy-target-class=true  → 强制使用 CGLIB
//    spring.aop.proxy-target-class=false → 有接口用 JDK,无接口用 CGLIB

// 3. MyBatis Mapper 代理
//    MapperProxy 实现了 InvocationHandler
//    userMapper.getUser(1L) → MapperProxy.invoke() → SQL 执行

7.3 Spring 事务代理原理

java
// @Transactional 注解的工作原理(简化版)
@Service
class OrderService {
    @Transactional
    public void createOrder(Order order) {
        // Spring 实际上创建了代理对象:
        // OrderService$$EnhancerBySpringCGLIB extends OrderService {
        //     TransactionInterceptor 拦截 createOrder() {
        //         beginTransaction();
        //         try {
        //             super.createOrder(order);  // 调用真实方法
        //             commit();
        //         } catch (Exception e) {
        //             rollback();
        //             throw e;
        //         }
        //     }
        // }
    }
}

八、与其他模式的关系

模式关系
装饰器模式结构几乎相同,但意图不同:代理控制访问,装饰器增强功能。代理通常由框架自动生成,装饰器由客户端显式组装。
适配器模式代理和原对象实现相同接口,适配器和目标对象接口不同。
外观模式外观为复杂子系统提供统一入口,代理为单个对象提供访问控制。外观可以包含多个对象,代理只代理一个对象。
享元模式享元模式中的工厂可以返回代理对象,实现延迟加载。

九、面试常见问题

Q1:JDK 动态代理和 CGLIB 代理有什么区别?(高频)

  • JDK 动态代理基于反射+接口,通过 Proxy.newProxyInstance() 生成 $Proxy 类,目标类必须实现接口。优点是 JDK 原生支持,无需额外依赖;缺点是只能代理接口方法。
  • CGLIB 代理基于继承+字节码增强,通过 Enhancer 生成目标类的子类作为代理。优点是不需要接口,性能更好(FastClass 机制);缺点是不能代理 final 类和方法。
  • Spring 默认策略:有接口用 JDK,无接口用 CGLIB。Spring Boot 2.x 默认使用 CGLIB(proxy-target-class=true)。

Q2:Spring AOP 的原理是什么?(高频)

:Spring AOP 基于动态代理实现。在 Bean 初始化时,BeanPostProcessor 检查 Bean 是否需要被代理(是否有切面匹配)。如果需要,则创建代理对象替代原始 Bean:

  1. 如果目标类实现了接口,默认使用 JDK 动态代理。
  2. 如果目标类没有接口,使用 CGLIB 代理。
  3. 方法调用时,代理对象拦截调用,执行 Advice 链(Before → 目标方法 → AfterReturning/AfterThrowing → After),最后返回结果。

Q3:为什么 @Transactional 注解在同一个类中自调用会失效?(高频)

:因为 Spring AOP 基于代理,@Transactional 是通过代理对象拦截来实现的。当在同一个类中调用 this.methodB() 时,this 指向的是目标对象而不是代理对象,因此不会被代理拦截,事务注解失效。

解决方案

  1. 通过 AopContext.currentProxy() 获取代理对象后调用。
  2. 将方法拆分到不同的 Service 中。
  3. 在启动类上添加 @EnableAspectJAutoProxy(exposeProxy = true)

Q4:静态代理和动态代理有什么区别?

  • 静态代理:代理类在编译时就已确定,需要手动编写代理类。一个代理类只能代理一个接口,代码冗余。
  • 动态代理:代理类在运行时动态生成,无需手动编写代理类。一个 InvocationHandler 可以代理多个接口,复用性高。
  • 动态代理的优势:减少代码量,统一处理横切逻辑,运行时动态切换。

Q5:Spring 中哪些场景使用了代理模式?

  1. AOP@Transactional@Cacheable@Async@Retryable 等。
  2. MyBatis MapperMapperProxy 代理 Mapper 接口,自动生成 SQL 执行逻辑。
  3. Spring Security@PreAuthorize@Secured 通过代理实现方法级安全控制。
  4. Spring Data JPAJpaRepository 的代理实现,自动生成 CRUD 方法。
  5. Spring Cloud FeignFeignClient 接口的代理,封装 HTTP 远程调用。

Q6:CGLIB 代理为什么不能代理 final 方法?

:CGLIB 通过继承目标类来生成代理,Java 中 final 方法不能被重写,因此 CGLIB 无法覆盖 final 方法来添加代理逻辑。同理,final 类不能被继承,也不能被 CGLIB 代理。private 方法同样不能被代理(不可见)。

Q7:如何查看 Spring 生成的代理类?

  1. 打印 bean.getClass().getName(),CGLIB 代理类名包含 $$EnhancerBySpringCGLIB
  2. application.yml 中设置 logging.level.org.springframework.aop=DEBUG
  3. 可以使用 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglib") 将 CGLIB 生成的字节码输出到文件。