当前位置: 首页 > news >正文

Java设计模式之结构型—代理模式

Java中最常用的设计模式-CSDN博客

 静态代理

“在编译期就写死代理类,实现/继承同一个接口/父类,把真实对象包一层,手动加逻辑。”

  1. 场景

  • 对真实对象做 日志、权限、缓存 等横切增强

  • 真实对象不许改动(第三方、旧代码)

  • 代理类数量固定 → 适合 接口少、实现类少 的场景

代码块

// 1) 公共接口
public interface UserService {void save(String name);
}// 2) 真实对象
public class UserServiceImpl implements UserService {public void save(String name) {System.out.println("保存用户:" + name);}
}// 3) 代理类(编译期手写)
public class UserServiceProxy implements UserService {private final UserService real;   // 组合真实对象public UserServiceProxy(UserService real) {this.real = real;}public void save(String name) {System.out.println("前置日志:准备保存");real.save(name);              // 调用真实对象System.out.println("后置日志:保存完成");}
}// 4) 使用
UserService service = new UserServiceProxy(new UserServiceImpl());
service.save("Alice");

优缺点

优点缺点
简单直观,无运行时开销每多一个接口/实现,就要手写一个代理类 → 类爆炸
编译期即可检查类型无法代理 未实现接口的方法

动态代理

Dynamic Proxy

方案技术代理目标关键类
JDK 动态代理java.lang.reflect.Proxy + InvocationHandler只能代理接口Proxy.newProxyInstance()
CGLIB/ByteBuddy字节码生成库接口 + 普通类Enhancer.create()

JDK 动态代理

// 订单服务接口
public interface OrderService {void create();          // 创建订单的业务方法
}// 订单服务实现类
public class OrderServiceImpl implements OrderService {public void create() {System.out.println("创建订单");  // 真正的业务逻辑}
}// 日志处理器:实现 InvocationHandler 接口,用来在真实方法前后附加日志
public class LogHandler implements InvocationHandler {private final Object target;   // 被代理的“真实对象”public LogHandler(Object target) {this.target = target;      // 构造时把真实对象传进来}// 每当代理对象上的任何方法被调用时,都会走到这里public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("前置日志");                // 1. 前置增强(如记录开始时间、打印参数等)Object result = method.invoke(target, args);  // 2. 通过反射调用真实对象的方法System.out.println("后置日志");                // 3. 后置增强(如记录结束时间、打印返回值等)return result;                               // 4. 把真实方法的返回值原样返回}
}// ---------------- 使用示例 ----------------
public class Main {public static void main(String[] args) {// 1. 用 JDK 的 Proxy 工具生成一个“代理对象”OrderService proxy = (OrderService) Proxy.newProxyInstance(OrderService.class.getClassLoader(),   // 类加载器:告诉 JVM 把代理类加载到哪个命名空间new Class<?>[]{OrderService.class},    // 需要实现的接口列表(可多个)new LogHandler(new OrderServiceImpl()) // 调用处理器:真正干活儿的 LogHandler);// 2. 通过代理对象调用方法proxy.create();  // 控制台会依次打印://   前置日志//   创建订单//   后置日志}
}

public Object invoke(Object proxy, Method method, Object[] args)参数含义

  1. Object proxy
    这是 代理对象本身(就是 Proxy.newProxyInstance() 返回的那个对象)。

    • 如果你在这里面再调用 proxy.toString()proxy.create() 之类的方法,会再次进入 invoke,极易造成无限递归,所以通常不会直接用它。

    • 主要用途:在需要判断 proxy == 某个代理实例 或打印调试信息时才会用到。

  2. Method method
    本次被调用的方法对象

    • 通过 method.getName() 可以知道调的是哪个方法(如 create)。

    • 通过 method.getParameterTypes()method.getReturnType() 等可以拿到签名信息,用来做通用逻辑(例如所有 get* 方法做缓存、所有 save* 方法做事务等)。

  3. Object[] args
    本次方法调用时传进来的实参数组,按声明顺序排列。

    • 如果方法无参,它就是 null 或空数组。

    • 可以通过 args[i] 读取或修改参数值,实现诸如统一参数校验、脱敏、记录日志等横切逻辑。

CGLIB 最简示例(可代理类)

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {System.out.println("前置");Object result = proxy.invokeSuper(obj, args);System.out.println("后置");return result;
});
UserService proxy = (UserService) enhancer.create();
proxy.save("Bob");

动态代理 vs 静态代理

维度动态代理静态代理
代码量1 个 InvocationHandler 即可每接口/实现都要手写
代理范围任意接口/类(CGLIB)只能固定接口
性能反射调用略慢(JDK)/ ASM 接近原生(CGLIB)直接调用,零反射
生成时机运行时编译期
http://www.xdnf.cn/news/1440703.html

相关文章:

  • leetcode算法刷题的第二十五天
  • Python:AI开发第一语言的全面剖析
  • Springboot3+SpringSecurity6Oauth2+vue3前后端分离认证授权-客户端
  • 【机器学习入门】5.4 线性回归模型的应用——从CO₂浓度预测学透实战全流程
  • 远程的 develop 比你本地的 develop 更新,Git 拒绝直接覆盖
  • 【55页PPT】旧QC七大手法培训精选讲义(附下载方式)
  • 深入解析Flowable工作流引擎:从原理到实践
  • 2 XSS
  • 深入掌握sed:Linux文本处理的流式编辑器利器
  • PHP如何解决使用国密SM4解密Base64数据错误问题?(基于lpilp/guomi)
  • 协议分析基础
  • 以技术共享点燃全球能源变革新引擎的智慧能源开源了
  • 低代码革命遇瓶颈?这个“套娃神技“才是破局关键!
  • 在Excel和WPS表格中隔多行插入一个空白行
  • 多场景对练数据的 Excel 横向导出方案(EasyExcel 动态表头实践)
  • 【XR硬件系列】Vivo Vision 与 Apple VisionPro 深度技术对比:MR 时代的轻量化革命与生态霸权
  • 单元测试数据库回滚问题
  • Android音频学习(十六)——CreateTrack
  • 资产管理还靠Excel?深度体验系统如何让企业高效数字化升级!
  • 自然语言处理深层语义分析中公理化体系的可行性、挑战与前沿进展
  • php:PHP 8 新特性深度解析与实战应用:提升开发效率的关键技巧
  • 为何 React JSX 循环需要使用 key
  • 一文弄懂C/C++不定参数底层原理
  • Zygote 进程启动流程
  • 视频判重需求:别为同一内容花两次钱!
  • 涨了一倍多的顺丰同城,还能继续做大即时零售基建的蛋糕吗?
  • HTML5 标题标签、段落、换行和水平线
  • 光谱相机的探测器类型
  • 相机在两个机械臂上安装方式比较
  • 字节跳动后端 一面凉经