代理模式(Proxy Pattern):静态代理与动态代理详解

为其他对象提供一种代理以控制对这个对象的访问

🎯 面试重点

📖 一、代理模式核心思想

1.1 什么是代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,为其他对象提供一种代理以控制对这个对象的访问

核心角色:

┌─────────────┐       ┌─────────────┐       ┌─────────────┐
│   Client    │──────▶│   Subject   │◀──────│ RealSubject │
└─────────────┘       └─────────────┘       └─────────────┘
                             │
                             │ implements
                             ▼
                      ┌─────────────┐
                      │    Proxy    │
                      └─────────────┘
                      │ - realSubject│
                      │ + request()  │
                      └─────────────┘

1.2 为什么需要代理模式

/**
 * 代理模式的核心价值
 */
public class WhyProxy {
    
    // ❌ 不使用代理:直接调用,无法添加额外逻辑
    public void withoutProxy() {
        UserService userService = new UserService();
        userService.deleteUser(1);  // 无法记录日志、权限校验等
    }
    
    // ✅ 使用代理:在调用前后添加控制逻辑
    public void withProxy() {
        UserService proxy = new UserServiceProxy(new UserService());
        proxy.deleteUser(1);  // 自动添加:日志、权限、事务等
    }
}

代理模式的核心价值:

场景 说明
访问控制 控制对对象的访问权限
延迟加载 延迟创建开销大的对象(虚拟代理)
远程访问 为远程对象提供本地代理(远程代理)
日志监控 记录方法调用信息
性能优化 缓存结果、统计耗时
事务管理 Spring 声明式事务

1.3 代理模式的三种类型

/**
 * 代理模式的三种类型
 */
public class ProxyTypes {
    
    // 1. 远程代理(Remote Proxy)
    // - 为远程对象提供本地代理
    // - 示例:RMI、Dubbo、Feign
    // RPC调用看起来像本地调用
    
    // 2. 虚拟代理(Virtual Proxy)
    // - 延迟创建开销大的对象
    // - 示例:图片懒加载、数据库连接池
    // 只有真正需要时才创建对象
    
    // 3. 保护代理(Protection Proxy)
    // - 控制对对象的访问权限
    // - 示例:权限校验、防火墙
    // 在访问前检查权限
}

📖 二、静态代理

2.1 静态代理实现

静态代理:代理类在编译时就已确定,由程序员手动编写或工具生成。

/**
 * 静态代理完整示例
 */
public class StaticProxyDemo {
    
    // 1. 定义接口(Subject)
    public interface UserService {
        void addUser(String name);
        void deleteUser(int id);
        String getUser(int id);
    }
    
    // 2. 真实对象(RealSubject)
    public static class UserServiceImpl implements UserService {
        @Override
        public void addUser(String name) {
            System.out.println("添加用户: " + name);
        }
        
        @Override
        public void deleteUser(int id) {
            System.out.println("删除用户: " + id);
        }
        
        @Override
        public String getUser(int id) {
            System.out.println("查询用户: " + id);
            return "User-" + id;
        }
    }
    
    // 3. 代理对象(Proxy)
    public static class UserServiceProxy implements UserService {
        
        private UserService target;  // 持有真实对象引用
        
        public UserServiceProxy(UserService target) {
            this.target = target;
        }
        
        @Override
        public void addUser(String name) {
            before("addUser");
            target.addUser(name);  // 调用真实对象
            after("addUser");
        }
        
        @Override
        public void deleteUser(int id) {
            before("deleteUser");
            target.deleteUser(id);
            after("deleteUser");
        }
        
        @Override
        public String getUser(int id) {
            before("getUser");
            String result = target.getUser(id);
            after("getUser");
            return result;
        }
        
        private void before(String method) {
            System.out.println("[Proxy] before " + method);
        }
        
        private void after(String method) {
            System.out.println("[Proxy] after " + method);
        }
    }
    
    // 4. 使用示例
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = new UserServiceProxy(target);
        
        proxy.addUser("张三");
        System.out.println("---");
        proxy.getUser(1);
    }
}

输出结果:

[Proxy] before addUser
添加用户: 张三
[Proxy] after addUser
---
[Proxy] before getUser
查询用户: 1
[Proxy] after getUser

2.2 静态代理的优缺点

/**
 * 静态代理的优缺点分析
 */
public class StaticProxyAnalysis {
    
    // ✅ 优点:
    // 1. 代码直观易懂,便于理解代理模式
    // 2. 在不修改目标对象的前提下扩展功能
    // 3. 代理对象可以控制对目标对象的访问
    
    // ❌ 缺点:
    // 1. 代理类和目标类实现相同接口,接口增加方法时代理类也要修改
    // 2. 每个目标类都需要单独编写代理类,代码冗余
    // 3. 一个代理类只能代理一个目标类,扩展性差
    
    public void example() {
        // 如果有 10 个 Service,就需要写 10 个 Proxy 类
        UserService userProxy = new UserServiceProxy(new UserServiceImpl());
        OrderService orderProxy = new OrderServiceProxy(new OrderServiceImpl());
        ProductService productProxy = new ProductServiceProxy(new ProductServiceImpl());
        // ... 代码爆炸
    }
}

静态代理的问题本质:

接口变化 → 所有实现类都要修改(包括代理类)
新增目标类 → 需要新增对应的代理类
代理逻辑相同 → 代理类代码重复

📖 三、动态代理

动态代理:代理类在运行时动态生成,无需手动编写代理类。

3.1 JDK 动态代理

JDK 动态代理:使用 java.lang.reflect.Proxy 类和 InvocationHandler 接口实现。

/**
 * JDK 动态代理实现
 * 核心类:Proxy + InvocationHandler
 */
public class JdkProxyDemo {
    
    // 1. 定义接口
    public interface UserService {
        void addUser(String name);
        void deleteUser(int id);
    }
    
    // 2. 目标类实现接口(JDK 动态代理要求目标类必须实现接口)
    public static class UserServiceImpl implements UserService {
        @Override
        public void addUser(String name) {
            System.out.println("添加用户: " + name);
        }
        
        @Override
        public void deleteUser(int id) {
            System.out.println("删除用户: " + id);
        }
    }
    
    // 3. 实现 InvocationHandler 接口
    public static 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 {
            // 前置通知
            System.out.println("[JDK Proxy] 方法执行前: " + method.getName());
            long start = System.currentTimeMillis();
            
            // 执行目标方法
            Object result = method.invoke(target, args);
            
            // 后置通知
            long end = System.currentTimeMillis();
            System.out.println("[JDK Proxy] 方法执行后: " + method.getName() + 
                             ", 耗时: " + (end - start) + "ms");
            
            return result;
        }
    }
    
    // 4. 创建代理对象
    public static void main(String[] args) {
        // 目标对象
        UserService target = new UserServiceImpl();
        
        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // 类加载器
            target.getClass().getInterfaces(),   // 目标类实现的接口
            new LogInvocationHandler(target)     // InvocationHandler
        );
        
        // 调用代理方法
        proxy.addUser("李四");
        proxy.deleteUser(1);
        
        // 查看代理类信息
        System.out.println("\n代理类名称: " + proxy.getClass().getName());
        System.out.println("代理类父类: " + proxy.getClass().getSuperclass().getName());
    }
}

输出结果:

[JDK Proxy] 方法执行前: addUser
添加用户: 李四
[JDK Proxy] 方法执行后: addUser, 耗时: 1ms
[JDK Proxy] 方法执行前: deleteUser
删除用户: 1
[JDK Proxy] 方法执行后: deleteUser, 耗时: 0ms

代理类名称: com.sun.proxy.$Proxy0
代理类父类: java.lang.reflect.Proxy

3.2 JDK 动态代理原理

/**
 * JDK 动态代理原理分析
 */
public class JdkProxyPrinciple {
    
    public void analysis() {
        // Proxy.newProxyInstance 内部做了什么?
        
        // 1. 生成代理类的字节码
        //    - 代理类继承 java.lang.reflect.Proxy
        //    - 实现目标类所实现的所有接口
        //    - 每个方法内部调用 InvocationHandler.invoke()
        
        // 2. 加载代理类
        //    - 使用传入的 ClassLoader 加载生成的代理类
        
        // 3. 创建代理类实例
        //    - 通过反射调用代理类的构造方法
        //    - 将 InvocationHandler 传入代理类
        
        // 生成的代理类大致结构:
        /*
        public final class $Proxy0 extends Proxy implements UserService {
            
            private static Method m1;  // addUser 方法
            private static Method m2;  // deleteUser 方法
            
            public $Proxy0(InvocationHandler h) {
                super(h);  // 调用 Proxy 的构造方法
            }
            
            public void addUser(String name) {
                try {
                    // 调用 InvocationHandler.invoke()
                    super.h.invoke(this, m1, new Object[]{name});
                } catch (Throwable t) {
                    // 异常处理
                }
            }
            
            public void deleteUser(int id) {
                try {
                    super.h.invoke(this, m2, new Object[]{id});
                } catch (Throwable t) {
                    // 异常处理
                }
            }
        }
        */
    }
    
    // JDK 动态代理的限制
    public void limitations() {
        // ❌ 目标类必须实现至少一个接口
        // ❌ 只能代理接口方法,不能代理类方法
        
        // 原因:代理类已经继承了 Proxy,Java 不支持多继承
        // 只能通过实现接口的方式来代理
    }
}

3.3 CGLIB 动态代理

CGLIB(Code Generation Library):通过继承目标类实现动态代理,无需接口。

/**
 * CGLIB 动态代理实现
 * 需要依赖:cglib 包
 * 核心类:Enhancer + MethodInterceptor
 */
public class CglibProxyDemo {
    
    // 1. 目标类(无需实现接口)
    public static class UserService {
        
        public void addUser(String name) {
            System.out.println("添加用户: " + name);
        }
        
        public void deleteUser(int id) {
            System.out.println("删除用户: " + id);
        }
        
        // final 方法不能被代理(无法被子类重写)
        public final void finalMethod() {
            System.out.println("final 方法不能被代理");
        }
        
        // static 方法不能被代理(不参与继承)
        public static void staticMethod() {
            System.out.println("static 方法不能被代理");
        }
    }
    
    // 2. 实现 MethodInterceptor 接口
    public static class LogMethodInterceptor implements MethodInterceptor {
        
        @Override
        public Object intercept(Object obj, Method method, Object[] args, 
                                MethodProxy proxy) throws Throwable {
            // 前置通知
            System.out.println("[CGLIB Proxy] 方法执行前: " + method.getName());
            long start = System.currentTimeMillis();
            
            // 执行目标方法(使用 MethodProxy 避免递归调用)
            Object result = proxy.invokeSuper(obj, args);
            
            // 后置通知
            long end = System.currentTimeMillis();
            System.out.println("[CGLIB Proxy] 方法执行后: " + method.getName() + 
                             ", 耗时: " + (end - start) + "ms");
            
            return result;
        }
    }
    
    // 3. 创建代理对象
    public static void main(String[] args) {
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();
        
        // 设置父类(目标类)
        enhancer.setSuperclass(UserService.class);
        
        // 设置回调(MethodInterceptor)
        enhancer.setCallback(new LogMethodInterceptor());
        
        // 创建代理对象
        UserService proxy = (UserService) enhancer.create();
        
        // 调用代理方法
        proxy.addUser("王五");
        proxy.deleteUser(1);
        
        // 查看代理类信息
        System.out.println("\n代理类名称: " + proxy.getClass().getName());
        System.out.println("代理类父类: " + proxy.getClass().getSuperclass().getName());
    }
}

输出结果:

[CGLIB Proxy] 方法执行前: addUser
添加用户: 王五
[CGLIB Proxy] 方法执行后: addUser, 耗时: 0ms
[CGLIB Proxy] 方法执行前: deleteUser
删除用户: 1
[CGLIB Proxy] 方法执行后: deleteUser, 耗时: 0ms

代理类名称: com.example.UserService$$EnhancerByCGLIB$$12345678
代理类父类: com.example.UserService

3.4 CGLIB 动态代理原理

/**
 * CGLIB 动态代理原理分析
 */
public class CglibProxyPrinciple {
    
    public void analysis() {
        // CGLIB 内部做了什么?
        
        // 1. 生成代理类的字节码
        //    - 代理类继承目标类
        //    - 重写所有非 final 方法
        //    - 每个方法内部调用 MethodInterceptor.intercept()
        
        // 2. FastClass 机制
        //    - 为代理类和目标类各生成一个 FastClass
        //    - FastClass 通过索引定位方法,比反射更快
        
        // 生成的代理类大致结构:
        /*
        public class UserService$$EnhancerByCGLIB$$12345678 extends UserService {
            
            private MethodInterceptor CGLIB$CALLBACK_0;
            
            // 重写父类方法
            @Override
            public void addUser(String name) {
                MethodInterceptor interceptor = CGLIB$CALLBACK_0;
                if (interceptor != null) {
                    // 调用 MethodInterceptor.intercept()
                    interceptor.intercept(this, method, args, proxy);
                } else {
                    super.addUser(name);
                }
            }
            
            @Override
            public void deleteUser(int id) {
                // 类似 addUser
            }
        }
        */
    }
    
    // CGLIB 的限制
    public void limitations() {
        // ❌ 不能代理 final 类(无法继承)
        // ❌ 不能代理 final 方法(无法重写)
        // ❌ 不能代理 static 方法(不参与继承)
        // ❌ 不能代理 private 方法(子类不可见)
    }
}

3.5 JDK vs CGLIB 对比

/**
 * JDK 动态代理 vs CGLIB 动态代理
 */
public class ProxyCompare {
    
    /*
    ┌──────────────────┬─────────────────────────┬─────────────────────────┐
    │      特性         │     JDK 动态代理         │     CGLIB 动态代理       │
    ├──────────────────┼─────────────────────────┼─────────────────────────┤
    │ 实现原理          │ 接口实现                 │ 类继承                  │
    │ 是否需要接口       │ ✅ 必须实现接口          │ ❌ 无需接口              │
    │ 代理方式          │ 代理接口方法             │ 代理类方法(重写)        │
    │ 生成代理类         │ 实现接口                 │ 继承目标类               │
    │ final 限制        │ 无                       │ ❌ 不能代理 final        │
    │ 性能(JDK8 之前)  │ 较慢                     │ 较快                    │
    │ 性能(JDK8 之后)  │ 优化后接近 CGLIB         │ 较快                    │
    │ 生成代理类速度     │ 快                       │ 慢(生成 FastClass)      │
    │ 依赖              │ JDK 内置                 │ 需要引入 cglib 依赖      │
    │ Spring 默认       │ ✅ 有接口时使用          │ 无接口时使用             │
    └──────────────────┴─────────────────────────┴─────────────────────────┘
    */
}

📖 四、Spring AOP 与代理模式

4.1 Spring AOP 核心概念

/**
 * Spring AOP 核心概念
 */
public class SpringAopConcepts {
    
    /*
    ┌─────────────────────────────────────────────────────────────┐
    │                      Spring AOP 术语                         │
    ├────────────────┬────────────────────────────────────────────┤
    │    JoinPoint   │ 连接点,程序执行的某个位置(方法调用、异常抛出)  │
    │                │ Spring AOP 中指方法的执行点                 │
    ├────────────────┼────────────────────────────────────────────┤
    │    Pointcut    │ 切入点,用于匹配连接点的谓词表达式            │
    │                │ 例如:execution(* com.example.service.*.*(..)) │
    ├────────────────┼────────────────────────────────────────────┤
    │     Advice     │ 通知/增强,拦截到连接点后执行的代码           │
    │                │ Before、After、AfterReturning、AfterThrowing │
    │                │ Around                                      │
    ├────────────────┼────────────────────────────────────────────┤
    │     Target     │ 目标对象,被代理的真实对象                    │
    ├────────────────┼────────────────────────────────────────────┤
    │    Weaving     │ 织入,将通知/增强应用到目标对象的过程          │
    │                │ 编译时织入、类加载时织入、运行时织入           │
    │                │ Spring AOP 使用运行时织入                    │
    ├────────────────┼────────────────────────────────────────────┤
    │     Aspect     │ 切面,Pointcut 和 Advice 的结合             │
    │                │ 告诉 AOP 在哪里做、做什么                    │
    └────────────────┴────────────────────────────────────────────┘
    
    通知类型(Advice Type):
    ┌────────────────────┬─────────────────────────────────────────┐
    │       类型          │              说明                       │
    ├────────────────────┼─────────────────────────────────────────┤
    │ Before             │ 方法执行前执行                            │
    │ After              │ 方法执行后执行(无论是否异常)             │
    │ AfterReturning     │ 方法正常返回后执行                        │
    │ AfterThrowing      │ 方法抛出异常后执行                        │
    │ Around             │ 包围方法执行,可决定是否调用目标方法      │
    └────────────────────┴─────────────────────────────────────────┘
    */
}

4.2 Spring AOP 代理选择策略

/**
 * Spring AOP 如何选择代理方式
 */
public class SpringProxyStrategy {
    
    public void analysis() {
        // Spring AOP 的代理选择规则:
        
        // 1. 如果目标类实现了接口
        //    → 默认使用 JDK 动态代理
        //    → 生成实现相同接口的代理类
        
        // 2. 如果目标类没有实现接口
        //    → 使用 CGLIB 动态代理
        //    → 生成继承目标类的代理类
        
        // 3. 配置强制使用 CGLIB
        //    → <aop:aspectj-autoproxy proxy-target-class="true" />
        //    → @EnableAspectJAutoProxy(proxyTargetClass = true)
        
        /*
        原理源码(Spring 5.x):
        
        // DefaultAopProxyFactory.createAopProxy()
        public AopProxy createAopProxy(AdvisedSupport config) {
            // 配置了强制使用 CGLIB 或者没有实现接口
            if (config.isProxyTargetClass() || 
                hasNoUserSuppliedProxyInterfaces(config)) {
                return new CglibAopProxy(config);
            }
            // 否则使用 JDK 动态代理
            return new JdkDynamicAopProxy(config);
        }
        */
    }
    
    // Spring Boot 2.x 开始,默认 proxyTargetClass = true
    // 即默认使用 CGLIB 代理
    public void springBootDefault() {
        // Spring Boot 2.x+
        // 默认使用 CGLIB 代理(proxyTargetClass = true)
        
        // Spring Boot 1.x
        // 默认使用 JDK 动态代理(有接口时)
    }
}

4.3 Spring 事务的代理实现

/**
 * Spring 声明式事务的代理实现原理
 */
public class TransactionProxyDemo {
    
    // 1. 开启事务注解
    // @Transactional
    public void transactionExample() {
        // Spring 会为带有 @Transactional 的方法创建代理
        // 在方法调用前后自动开启/提交/回滚事务
    }
    
    // 2. TransactionInterceptor 内部实现
    public void internalImplementation() {
        /*
        // TransactionInterceptor.invoke() 伪代码
        public Object invoke(MethodInvocation invocation) {
            // 1. 获取事务属性
            TransactionAttributeSource tas = getTransactionAttributeSource();
            TransactionAttribute ta = tas.getTransactionAttribute(
                invocation.getMethod(), invocation.getTargetClass()
            );
            
            // 2. 获取或创建事务
            TransactionInfo info = createTransactionIfNecessary(ta);
            
            try {
                // 3. 执行目标方法
                Object retVal = invocation.proceed();
                
                // 4. 提交事务
                commitTransactionAfterReturning(info);
                return retVal;
                
            } catch (Throwable ex) {
                // 5. 回滚事务
                rollbackTransactionOnThrowable(info, ex);
                throw ex;
            } finally {
                // 6. 清理事务信息
                cleanupTransactionInfo(info);
            }
        }
        */
    }
    
    // 3. DataSourceTransactionManager 核心方法
    public void transactionManager() {
        /*
        // 数据源事务管理器的核心操作
        
        // 获取连接
        Connection con = dataSource.getConnection();
        
        // 设置手动提交
        con.setAutoCommit(false);
        
        try {
            // 执行 SQL
            statement.execute(sql);
            
            // 提交
            con.commit();
            
        } catch (Exception e) {
            // 回滚
            con.rollback();
            
        } finally {
            con.close();
        }
        */
    }
    
    // 4. 事务传播行为
    public void propagation() {
        /*
        // @Transactional 的 propagation 属性
        
        REQUIRED      - 如果当前有事务,加入该事务(默认)
        REQUIRES_NEW  - 开启新事务,挂起当前事务
        SUPPORTS      - 如果当前有事务,加入该事务;否则无事务
        MANDATORY     - 必须在事务中执行,否则抛异常
        NOT_SUPPORTED - 无事务执行,挂起当前事务
        NEVER         - 必须无事务,否则抛异常
        NESTED        - 嵌套事务( Savepoint )
        */
    }
    
    // 5. 事务失效场景
    public void transactionFailure() {
        /*
        ⚠️ 事务可能失效的场景:
        
        1. 方法内部调用(自调用)
        ┌──────────────────────────────────────────────────┐
        │ @Service                                          │
        │ public class UserService {                        │
        │     public void methodA() {                       │
        │         // ❌ this.methodB() 不会走代理           │
        │         this.methodB();  // 事务不生效            │
        │     }                                             │
        │                                                   │
        │     @Transactional                                │
        │     public void methodB() { ... }                 │
        │ }                                                 │
        └──────────────────────────────────────────────────┘
        解决:从代理对象调用,或使用 AspectJ 编译时织入
        
        2. 非 public 方法
        ┌──────────────────────────────────────────────────┐
        │ @Transactional                                    │
        │ private void method() { ... }  // ❌ 事务不生效   │
        └──────────────────────────────────────────────────┘
        解决:改为 public 方法
        
        3. 异常被 catch 吞掉
        ┌──────────────────────────────────────────────────┐
        │ @Transactional                                    │
        │ public void method() {                           │
        │     try { ... } catch { /* 不抛异常 */ }         
         }                                                
        └──────────────────────────────────────────────────┘
        解决不捕获异常或手动回滚TransactionInterceptor.currentTransactionStatus().setRollbackOnly()
        
        4. 异常类型不匹配
        ┌──────────────────────────────────────────────────┐
         @Transactional(rollbackFor = Exception.class)    
         public void method() throws IOException {        
             throw new IOException(); // ❌ 默认不回滚     │
         }                                                
        └──────────────────────────────────────────────────┘
        解决指定 rollbackFor = IOException.class
        
        5. 异常被内层事务吞掉
        ┌──────────────────────────────────────────────────┐
         @Service A {                                     
             @Transactional                               
             public void a() {                            
                 b();  // b() 抛异常被 a() 捕获           │
             }                                             
                                                           
             @Transactional                               
             public void b() { throw new RuntimeException(); }
         }                                                
        └──────────────────────────────────────────────────┘
        */
    }
}

📖 五、代理模式 vs 装饰器模式

5.1 核心区别

/**
 * 代理模式 vs 装饰器模式
 */
public class ProxyVsDecorator {
    
    /*
    ┌──────────────────┬─────────────────────────┬─────────────────────────┐
    │      维度         │      代理模式             │     装饰器模式           │
    ├──────────────────┼─────────────────────────┼─────────────────────────┤
    │ 设计目的          │ 控制对对象的访问          │ 给对象动态添加职责        │
    │                   │ (不修改)               │ (增强)                 │
    ├──────────────────┼─────────────────────────┼─────────────────────────┤
    │ 代码形式          │ 代理类持有真实对象引用    │ 装饰器类持有对象引用      │
    │                   │ 调用时控制访问           │ 调用时添加额外行为        │
    ├──────────────────┼─────────────────────────┼─────────────────────────┤
    │ 客户端感知        │ 客户端可能不知道代理存在  │ 客户端知道被装饰了        │
    ├──────────────────┼─────────────────────────┼─────────────────────────┤
    │ 典型场景          │ 远程代理、虚拟代理        │ I/O 流、缓冲输出流        │
    │                   │ 安全代理、延迟加载        │ BufferedInputStream      │
    ├──────────────────┼─────────────────────────┼─────────────────────────┤
    │ 实现方式          │ 组合(持有引用)          │ 组合(持有引用)          │
    └──────────────────┴─────────────────────────┴─────────────────────────┘
    
    本质上两者都是"组合"模式,只是目的不同:
    - 代理模式:控制访问,客户端可能不知道代理存在
    - 装饰器模式:增强功能,客户端知道被装饰了
    
    在 Java 中,两者的实现几乎一样!
    关键区别在于语义和使用场景。
    */
    
    // 装饰器模式示例(Java I/O)
    public void ioDecorator() {
        /*
        // BufferedInputStream 就是装饰器模式
        FileInputStream fis = new FileInputStream("test.txt");
        BufferedInputStream bis = new BufferedInputStream(fis);
        DataInputStream dis = new DataInputStream(bis);
        
        // 一层层包装,层层增强功能:
        // FileInputStream    → 文件读取
        // BufferedInputStream → 缓冲读取
        // DataInputStream    → 数据类型转换
        
        // 客户端知道所有装饰器的存在
        // 目的是增强,不是控制访问
        */
    }
    
    // 代理模式示例
    public void proxyExample() {
        /*
        // 虚拟代理:延迟加载大图片
        Image image = new LargeImageProxy("big.jpg");
        // 此时还没有加载图片,只是创建了代理
        
        image.display();  // 第一次调用时才真正加载
        
        // 客户端可能不知道背后是代理在加载
        // 目的是控制访问(延迟加载)
        */
    }
}

📖 六、实际应用场景

6.1 性能监控

/**
 * 性能监控代理
 */
public class PerformanceMonitorProxy {
    
    // 动态代理实现
    public static Object createPerformanceProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                long start = System.nanoTime();
                Object result = method.invoke(target, args);
                long end = System.nanoTime();
                
                System.out.printf("[Performance] %s.%s() 耗时: %.2fms%n",
                    target.getClass().getSimpleName(),
                    method.getName(),
                    (end - start) / 1_000_000.0
                );
                
                return result;
            }
        );
    }
    
    // Spring AOP 实现
    /*
    @Aspect
    @Component
    public class PerformanceAspect {
        
        @Around("execution(* com.example.service.*.*(..))")
        public Object monitor(ProceedingJoinPoint point) throws Throwable {
            long start = System.currentTimeMillis();
            Object result = point.proceed();
            long end = System.currentTimeMillis();
            
            System.out.printf("方法: %s.%s, 耗时: %dms%n",
                point.getTarget().getClass().getSimpleName(),
                point.getSignature().getName(),
                end - start
            );
            
            return result;
        }
    }
    */
}

6.2 缓存代理

/**
 * 缓存代理
 */
public class CacheProxy {
    
    private static final Map<String, Object> CACHE = new ConcurrentHashMap<>();
    
    public static Object createCacheProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                // 只对查询方法添加缓存
                if (!method.getName().startsWith("get")) {
                    return method.invoke(target, args);
                }
                
                // 生成缓存 key
                String cacheKey = method.getName() + ":" + Arrays.toString(args);
                
                // 命中缓存
                if (CACHE.containsKey(cacheKey)) {
                    System.out.println("[Cache] 命中缓存: " + cacheKey);
                    return CACHE.get(cacheKey);
                }
                
                // 未命中,执行方法并缓存
                Object result = method.invoke(target, args);
                CACHE.put(cacheKey, result);
                System.out.println("[Cache] 写入缓存: " + cacheKey);
                
                return result;
            }
        );
    }
    
    // 缓存清除注解
    /*
    @CacheEvict(value = "users", allEntries = true)
    public void deleteUser(Long id) {
        // 删除用户后自动清除缓存
    }
    */
}

6.3 远程代理(RPC)

/**
 * 远程代理示例(简化版 RPC)
 */
public class RemoteProxyDemo {
    
    /*
    // Feign 远程调用
    @FeignClient(name = "user-service", url = "http://localhost:8080")
    public interface UserClient {
        @GetMapping("/user/{id}")
        User getUser(@PathVariable Long id);
    }
    
    // 使用时像本地接口一样调用
    @Autowired
    private UserClient userClient;
    
    public void getUser() {
        // 实际是 RPC 调用,但语法像本地调用
        User user = userClient.getUser(1L);
    }
    
    // 原理:Feign 会生成代理类
    // 代理类内部通过 HTTP 客户端调用远程服务
    */
}

6.4 安全控制

/**
 * 安全控制代理
 */
public class SecurityProxy {
    
    public static Object createSecurityProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                // 获取方法上的权限注解
                RequiresPermissions annotation = 
                    method.getAnnotation(RequiresPermissions.class);
                
                if (annotation != null) {
                    String[] permissions = annotation.value();
                    
                    // 检查用户权限
                    if (!hasPermissions(permissions)) {
                        throw new SecurityException("没有权限访问: " + method.getName());
                    }
                }
                
                return method.invoke(target, args);
            }
        );
    }
    
    private static boolean hasPermissions(String[] permissions) {
        // 实际应从 SecurityManager 获取当前用户权限
        // 这里简化处理
        return true;
    }
    
    // 使用注解
    /*
    public interface UserService {
        @RequiresPermissions("user:delete")
        void deleteUser(Long id);
        
        @RequiresPermissions("user:create")
        void createUser(User user);
    }
    */
}

📖 七、高频面试题

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

答:

区别 JDK 动态代理 CGLIB 动态代理
实现原理 实现接口 继承类
是否需要接口 必须实现接口 不需要接口
生成方式 Proxy.newProxyInstance Enhancer.create
代理类 继承 Proxy 继承目标类
性能 JDK8+ 优化后接近 CGLIB 较快(有 FastClass)
final 限制 不能代理 final 方法/类
Spring 默认 有接口时使用 无接口时使用

追问:Spring 为什么默认用 JDK 代理?

答:早期 JDK 动态代理性能差,Spring 默认用 CGLIB。Spring Boot 2.x 后默认 proxyTargetClass=true,强制使用 CGLIB,这样可以代理任何类而不限于接口。


Q2: Spring AOP 的织入方式有哪几种?

答:

织入方式 说明 特点
编译时织入 在编译时将切面织入字节码 需要特殊编译器(如 AspectJ)
类加载时织入 在类加载时织入 需要 Java Agent
运行时织入 在运行时创建代理 Spring AOP 使用这种方式

Spring AOP 使用运行时织入,通过代理模式:

追问:运行时织入和编译时织入哪个性能更好?

答:编译时织入性能更好,因为不需要在运行时生成字节码。但 Spring 选择运行时织入是为了保持简单性和灵活性,无需特殊编译器。


Q3: 代理模式和装饰器模式有什么区别?

答:

核心区别:

维度 代理模式 装饰器模式
目的 控制对对象的访问 给对象动态增加职责
客户端 可能不知道代理存在 明确知道被装饰
实现 几乎一样(组合+委托) 几乎一样(组合+委托)
典型场景 远程代理、虚拟代理、安全代理 I/O 流包装

本质: 两者都是”结构型组合模式”,代码实现几乎相同,区别在于语义和使用目的。

举例:


Q4: @Transactional 事务注解在哪些情况下会失效?

答:

失效场景 原因 解决方案
方法内部调用 自调用不走代理 从代理对象调用或用 AspectJ
非 public 方法 Spring AOP 只支持 public 方法 改为 public 方法
异常被 catch 没有异常传播到代理层 重新抛出或手动回滚
异常类型不匹配 默认只回滚 RuntimeException 设置 rollbackFor = Exception.class
同类相互调用 同一个类中方法调用不走代理 使用 self 注入自己

代码示例:

@Service
public class UserService {
    
    // ❌ 失效:内部调用不走代理
    public void methodA() {
        this.methodB();  // 不走代理,事务无效
    }
    
    @Transactional
    public void methodB() {
        // 事务无效
    }
    
    // ✅ 正确:注入自己,从代理对象调用
    @Autowired
    private UserService self;
    
    public void methodA() {
        self.methodB();  // 走代理,事务有效
    }
}

Q5: Spring Boot 如何配置强制使用 CGLIB 代理?

答:

方式一:注解配置(推荐)

@EnableAspectJAutoProxy(proxyTargetClass = true)
// proxyTargetClass = true  → 强制使用 CGLIB 代理
// proxyTargetClass = false → 优先使用 JDK 动态代理

方式二:配置文件

spring:
  aop:
    proxy-target-class: true

方式三:Spring Boot 默认行为

Spring Boot 2.x 开始默认 proxyTargetClass = true,自动使用 CGLIB 代理。

源码分析:

// DefaultAopProxyFactory.java
public AopProxy createAopProxy(AdvisedSupport config) {
    // 配置了 proxy-target-class="true" 或 没有实现接口
    if (config.isProxyTargetClass() || 
        hasNoUserSuppliedProxyInterfaces(config)) {
        
        if (targetClass.isInterface() || 
            Proxy.isProxyClass(targetClass)) {
            // 即使配置了 CGLIB,如果目标类是接口也用 JDK
            return new JdkDynamicAopProxy(config);
        }
        // 使用 CGLIB
        return new CglibAopProxy(config);
    }
    // 否则使用 JDK 动态代理
    return new JdkDynamicAopProxy(config);
}

⭐ 面试总结

核心要点速记

1. 代理模式 = 控制访问 + 组合委托
2. 静态代理:编译时确定,代码冗余
3. JDK 动态代理:必须实现接口,Proxy + InvocationHandler
4. CGLIB 动态代理:继承方式,Enhancer + MethodInterceptor
5. Spring AOP:有接口用 JDK,无接口用 CGLIB(Spring Boot 默认 CGLIB)
6. 织入方式:Spring AOP 用运行时织入
7. 事务失效:自调用、非 public、异常被吞
8. 代理 vs 装饰器:目的不同(控制 vs 增强)

面试加分项


⭐ 重点:代理模式是 Spring AOP 的核心基础,必须掌握!