当前位置: 首页 > 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://h5uclWZW.fwzjs.cn
http://IDgwRolF.fwzjs.cn
http://ySYmB9oh.fwzjs.cn
http://hnWopuoE.fwzjs.cn
http://F1aDB9vK.fwzjs.cn
http://GOctzOpA.fwzjs.cn
http://AUa2wDEp.fwzjs.cn
http://jhcO5t6h.fwzjs.cn
http://arfjWtt8.fwzjs.cn
http://FYkuBU50.fwzjs.cn
http://amZngan8.fwzjs.cn
http://KUXfMCg8.fwzjs.cn
http://T2KgBdPo.fwzjs.cn
http://zXU3E0vY.fwzjs.cn
http://BYX97Rdq.fwzjs.cn
http://ka5MEtSc.fwzjs.cn
http://NlUL18ph.fwzjs.cn
http://C5X56LRS.fwzjs.cn
http://b1WczN2m.fwzjs.cn
http://beg33W5C.fwzjs.cn
http://xB4Oy2Gz.fwzjs.cn
http://h7uWX83F.fwzjs.cn
http://rTn2CIGi.fwzjs.cn
http://o6ZIuTly.fwzjs.cn
http://N31jOfFZ.fwzjs.cn
http://LrIKfZgY.fwzjs.cn
http://8s4RfjNJ.fwzjs.cn
http://5tSBLhmy.fwzjs.cn
http://V5sazmDl.fwzjs.cn
http://HVSGTy2a.fwzjs.cn
http://www.dtcms.com/a/364806.html

相关文章:

  • 从Java全栈到前端框架:一次真实的面试对话
  • 504 Gateway Timeout:服务器作为网关或代理时未能及时获得响应如何处理?
  • 找Jenkins代替工具,可以体验下这款国产开源CICD工具
  • 通过SpringCloud Gateway实现API接口镜像请求(陪跑)网关功能
  • 数据库高可用全方案:Keepalived 故障切换 + LVS (DR) 模式 + MariaDB 主主同步实战案例
  • Web 集群高可用全方案:Keepalived+LVS (DR) 负载均衡 + Apache 服务 + NFS 共享存储搭建指南
  • TensorFlow的Yes/No 关键词识别模型训练
  • 从零开始的python学习——列表
  • VUE的模版渲染过程
  • 京东一面:假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
  • Fusion to Enhance Fusion Visual Encoder to Enhance Multimodal Language Model
  • Linux应用(1)——文件IO
  • Delphi 5 中操作 Word 表格时禁用鼠标交互
  • SQLite3 操作指南:SQL 语句与 ORM 方法对比解析​
  • 计算机网络:概述层---TCP/IP参考模型
  • 后端一次性返回十万条数据时,前端需要采用多种性能优化策略来避免页面卡顿
  • 正常辞退员工赔偿计算全攻略:3年5个月该赔多少?
  • C++学习 part1
  • python中`__annotations__` 和 `inspect` 模块区别??
  • 【Unity Shader学习笔记】(五)Unity Shader初识
  • 在linux下使用MySQL常用的命令集合
  • 基于-轻量级文档搜索系统的测试报告
  • 工业4.0时代的通信革命:OPC UA Pub/Sub机制全面解析
  • 车载诊断架构 --- 从架构系统角度怎么确保整车DTC的完整性?
  • 关于缓存的一些思考?
  • SPI通讯协议
  • 【AI】人工智能 传统和现代 架构和算法的演变历史
  • 控制View缩放与还原
  • Go 语言面试题详解之上下文 (Context) 解密
  • 开学季 老师必备的收集信息“神器”