代理模式核心概念

06-01 382阅读

代理模式核心概念

代理模式是一种结构型设计模式,通过创建一个代理对象来控制对原始对象的访问。主要分为两类:


一、静态代理 (Static Proxy)

定义:在编译期确定代理关系的模式,代理类和目标类都需要实现相同的接口。

核心特点:

  1. 手动编码:需要为每个目标类编写对应的代理类
  2. 编译时绑定:代理关系在编译期确定
  3. 强类型:代理类直接实现目标接口
  4. 无反射:直接调用目标方法,性能较高

实现步骤:

// 1. 定义接口
interface Database {
    void query(String sql);
}
// 2. 真实目标类
class MySQL implements Database {
    public void query(String sql) {
        System.out.println("执行MySQL查询: " + sql);
    }
}
// 3. 静态代理类
class DatabaseProxy implements Database {
    private Database target;
    
    public DatabaseProxy(Database target) {
        this.target = target;
    }
    
    public void query(String sql) {
        // 前置增强
        System.out.println("[日志] 开始执行查询: " + sql);
        
        // 调用真实对象
        target.query(sql);
        
        // 后置增强
        System.out.println("[日志] 查询完成");
    }
}
// 4. 使用代理
public class Main {
    public static void main(String[] args) {
        Database realDB = new MySQL();
        Database proxy = new DatabaseProxy(realDB);
        proxy.query("SELECT * FROM users");
    }
}

输出:

[日志] 开始执行查询: SELECT * FROM users
执行MySQL查询: SELECT * FROM users
[日志] 查询完成

优点:

  • 代码直观,易于理解
  • 编译期检查,类型安全
  • 执行效率高(无反射开销)

    缺点:

    • 每个目标类都需要创建代理类
    • 接口变更时代码需要同步修改
    • 无法动态扩展功能

      适用场景:

      • 代理少量固定类
      • 需要严格类型检查的场景
      • 性能敏感的场景

        二、动态代理 (Dynamic Proxy)

        定义:在运行时动态创建代理对象的模式,无需提前编写代理类。

        核心特点:

        1. 运行时生成:代理类在程序运行时动态创建
        2. 基于接口:JDK动态代理要求目标类必须实现接口
        3. 反射机制:通过反射调用目标方法
        4. 灵活扩展:一个代理类可代理多个目标类
        1. JDK 动态代理(基于接口)
        // 1. 定义接口(同上)
        interface Database { ... }
        // 2. 真实目标类(同上)
        class MySQL implements Database { ... }
        // 3. 实现InvocationHandler
        class LoggingHandler implements InvocationHandler {
            private Object target;
            
            public LoggingHandler(Object target) {
                this.target = target;
            }
            
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("[日志] 开始执行: " + method.getName());
                Object result = method.invoke(target, args);
                System.out.println("[日志] 执行完成");
                return result;
            }
        }
        // 4. 使用代理
        public class Main {
            public static void main(String[] args) {
                Database realDB = new MySQL();
                
                Database proxy = (Database) Proxy.newProxyInstance(
                    Database.class.getClassLoader(),
                    new Class[]{Database.class},
                    new LoggingHandler(realDB)
                );
                
                proxy.query("SELECT * FROM orders");
            }
        }
        
        2. CGLIB 动态代理(基于继承)
        // 1. 目标类(无需接口)
        class PaymentService {
            public void pay(double amount) {
                System.out.println("支付金额: " + amount);
            }
        }
        // 2. 方法拦截器
        class PaymentInterceptor implements MethodInterceptor {
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("[安全校验] 开始支付");
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("[通知] 支付成功");
                return result;
            }
        }
        // 3. 使用代理
        public class Main {
            public static void main(String[] args) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(PaymentService.class);
                enhancer.setCallback(new PaymentInterceptor());
                
                PaymentService proxy = (PaymentService) enhancer.create();
                proxy.pay(199.99);
            }
        }
        

        输出:

        [安全校验] 开始支付
        支付金额: 199.99
        [通知] 支付成功
        

        优点:

        • 无需编写代理类
        • 支持代理多个目标类
        • 功能扩展灵活
        • 适应接口变化

          缺点:

          • JDK代理要求目标类必须实现接口
          • CGLIB不能代理final类/方法
          • 反射调用有性能开销
          • 调试相对复杂

            适用场景:

            • AOP实现(如Spring)
            • 远程方法调用(RPC)
            • 事务管理
            • 权限控制
            • 日志记录

              三、关键对比

              特性静态代理动态代理
              创建时机编译期运行时
              实现方式手动编码代理类自动生成字节码
              接口要求需要实现相同接口JDK代理需要接口/CGLIB不需要
              性能高(直接调用)中(反射调用)
              扩展性差(每类需单独代理)强(通用代理处理器)
              代码复杂度高(重复代码多)低(集中处理)
              维护成本高(接口变更需修改)低(自动适应)
              代理类数量与目标类数量相同运行时动态生成

              四、Spring框架中的应用

              1. AOP实现:

                代理模式核心概念
                (图片来源网络,侵删)
                • JDK动态代理:代理接口实现类
                • CGLIB:代理无接口的类
                  // Spring配置强制使用CGLIB
                  @EnableAspectJAutoProxy(proxyTargetClass = true)
                  
                • 事务管理:

                  @Transactional // 基于动态代理实现
                  public void transfer(Account from, Account to, double amount) {
                      // ...
                  }
                  
                • 解决循环依赖:

                  代理模式核心概念
                  (图片来源网络,侵删)
                  • 通过三级缓存存储代理对象
                  • 提前暴露代理对象解决依赖
                  • 声明式服务:

                    • @Cacheable 缓存代理
                    • @Async 异步方法代理
                    • @Retryable 重试代理

              最佳实践:在Spring Boot 3.x中,默认优先使用CGLIB代理(通过设置spring.aop.proxy-target-class=true),因为它能代理任何类而不仅限于接口实现类。

              代理模式核心概念
              (图片来源网络,侵删)

              AOP 中的代理机制详解

              在 AOP(面向切面编程)中,代理是实现横切关注点(如日志、事务、安全等)的核心技术。Spring AOP 主要使用动态代理实现切面功能,下面详细解析其在 AOP 中的应用:


              一、代理在 AOP 中的作用

              1. 解耦核心业务与横切逻辑

                • 代理对象包裹原始对象(Target)
                • 在方法执行前后插入增强逻辑(Advice)
                  // 代理执行流程
                  proxy.method() {
                     beforeAdvice();     // 前置增强
                     target.method();    // 原始方法
                     afterAdvice();      // 后置增强
                  }
                  
                • 实现方式对比

                  代理类型实现机制在 AOP 中的应用场景
                  静态代理手动编写代理类简单场景,不常用
                  动态代理运行时生成字节码Spring AOP 默认实现

              二、Spring AOP 的代理实现

              1. JDK 动态代理(基于接口)
              • 触发条件:目标类实现了至少一个接口
              • 实现原理:
                public class JdkDynamicProxy {
                    public static Object createProxy(Object target) {
                        return Proxy.newProxyInstance(
                            target.getClass().getClassLoader(),
                            target.getClass().getInterfaces(), // 关键:获取所有接口
                            (proxy, method, args) -> {
                                System.out.println("[前置增强]");
                                Object result = method.invoke(target, args);
                                System.out.println("[后置增强]");
                                return result;
                            }
                        );
                    }
                }
                
                2. CGLIB 动态代理(基于继承)
                • 触发条件:目标类未实现接口
                • 实现原理:
                  public class CglibProxy {
                      public static Object createProxy(Class targetClass) {
                          Enhancer enhancer = new Enhancer();
                          enhancer.setSuperclass(targetClass); // 关键:设置父类
                          enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
                              System.out.println("[事务开始]");
                              Object result = proxy.invokeSuper(obj, args);
                              System.out.println("[事务提交]");
                              return result;
                          });
                          return enhancer.create();
                      }
                  }
                  

                  三、Spring AOP 代理工作流程

                  典型场景:日志记录切面
                  @Aspect
                  @Component
                  public class LoggingAspect {
                      
                      // 切点定义
                      @Pointcut("execution(* com.example.service.*.*(..))")
                      public void serviceMethods() {}
                      
                      // 环绕通知(最强大的通知类型)
                      @Around("serviceMethods()")
                      public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
                          String methodName = joinPoint.getSignature().getName();
                          
                          // 前置增强
                          System.out.println("[LOG] 进入方法: " + methodName);
                          
                          try {
                              // 执行原始方法
                              Object result = joinPoint.proceed();
                              
                              // 后置增强
                              System.out.println("[LOG] 方法成功: " + methodName);
                              return result;
                              
                          } catch (Exception e) {
                              // 异常增强
                              System.out.println("[LOG] 方法异常: " + methodName);
                              throw e;
                          }
                      }
                  }
                  
                  代理执行时序:
                  1. 容器创建目标 Bean(如 UserService)
                  2. 检测到需要 AOP 增强
                  3. 根据目标类选择代理方式:
                    • 有接口 → JDK 代理
                    • 无接口 → CGLIB 代理
                    • 生成代理对象并注入到依赖方
                    • 方法调用时执行增强链

                  四、关键特性解析

                  1. 代理选择策略
                  • Spring Boot 2.x+:默认优先使用 CGLIB
                    # 显式配置使用 CGLIB
                    spring.aop.proxy-target-class=true
                    
                  • 传统 Spring:按目标类是否实现接口自动选择
                    2. 代理限制与解决方案
                    问题解决方案
                    自调用失效(this调用)使用 AopContext.currentProxy()
                    final 方法无法增强避免对 final 方法使用 AOP
                    构造方法不拦截改用初始化回调(@PostConstruct)
                    私有方法不拦截Spring AOP 只拦截 public 方法
                    3. 性能优化建议
                    • 减少切点匹配复杂度:精确限定切点范围
                      // 优化前(低效)
                      @Pointcut("execution(* com.example..*.*(..))")
                      // 优化后(高效)
                      @Pointcut("execution(public * com.example.service.*Service.*(..))")
                      
                    • 避免在切面中做重型操作:如数据库访问
                    • 合理使用缓存:对重复计算的结果进行缓存

                      五、静态代理在 AOP 中的应用

                      虽然 Spring AOP 主要使用动态代理,但理解静态代理有助于掌握 AOP 本质:

                      手动实现 AOP 效果(伪代码)
                      // 原始类
                      class UserService {
                          public void saveUser(User user) {
                              // 业务逻辑
                          }
                      }
                      // 静态代理增强
                      class UserServiceProxy extends UserService {
                          private UserService target;
                          
                          public UserServiceProxy(UserService target) {
                              this.target = target;
                          }
                          
                          @Override
                          public void saveUser(User user) {
                              // 前置增强
                              log.info("开始保存用户");
                              
                              // 调用原始方法
                              target.saveUser(user);
                              
                              // 后置增强
                              log.info("用户保存成功");
                          }
                      }
                      

                      注意:实际 Spring AOP 不采用此方式,因为需要为每个类创建代理,无法应对复杂系统。


                      六、最佳实践

                      1. 优先使用接口:方便 JDK 代理,避免 CGLIB 限制

                        // 推荐实现接口
                        public class OrderService implements IOrderService {...}
                        
                      2. 最小化切面范围:精确控制增强目标

                        // 精确到具体方法
                        @Pointcut("execution(* com.example.service.OrderService.createOrder(..))")
                        
                      3. 谨慎使用 @Around:确保调用 proceed()

                        @Around("pointcut()")
                        public Object aroundAdvice(ProceedingJoinPoint pjp) {
                           // 必须调用 pjp.proceed()
                           return pjp.proceed(); 
                        }
                        
                      4. 代理类型检测:

                        if (AopUtils.isJdkDynamicProxy(bean)) {
                            // JDK 代理处理
                        } else if (AopUtils.isCglibProxy(bean)) {
                            // CGLIB 代理处理
                        }
                        

                      通过合理利用代理机制,Spring AOP 实现了业务逻辑与横切关注的完美解耦,是构建可维护、可扩展系统的关键技术。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码