news 2026/4/22 23:28:26

每日一学:设计模式之代理模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
每日一学:设计模式之代理模式

代理模式是结构型设计模式的一种,核心思想:给一个对象找一个 “代理人”,让代理人代替原对象处理请求,原对象只做核心业务逻辑

代理模式在Java的使用

声明式事务 @Transactional、AOP 切面增强 @Aspect等等

  • 日志记录
  • 权限校验
  • 耗时统计
  • 参数校验
  • 异常统一处理

代理对象负责重复的非业务操作,如记录前后日志、权限校验。原对象只需要关注核心业务。

一、核心概念

  1. 抽象主题(Subject):定义原对象和代理对象的公共接口(同时做的事情)
  2. 真实主题(RealSubject):真正做事的原对象(例如项目中的核心业务逻辑)
  3. 代理(Proxy):代替原对象处理请求,可附加额外功能(给业务加的日志等功能)
  4. 客户端(Client):使用代理对象,不直接接触原对象(调用代理对象,先执行完代理对象的操作再执行核心业务逻辑)

核心特点

  • 不修改原对象代码:通过代理扩展功能(开闭原则)
  • 职责分离:原对象只做核心业务,代理做日志、权限、缓存等附加操作
  • 保护 / 控制访问:代理可以拦截、过滤对原对象的请求

二、两种代理实现方式

1. 静态代理(手动写代理类)

适合代理对象固定的场景,代码写死,编译时就确定代理关系。

代码示例

/** * @author T * @version 1.0 2026-04-19 */ public interface UserService { void deleteUser(Long userId); } public class UserServiceImpl implements UserService{ @Override public void deleteUser(Long userId) { // 这里我们来写核心的业务逻辑 System.out.println("删除用户id为"+userId+"的用户!"); } } public class ProxyServiceImpl implements UserService{ private UserService userService; public ProxyServiceImpl(UserService userService){ this.userService = userService; } @Override public void deleteUser(Long userId) { System.out.println("执行删除前的日志记录、权限校验"); userService.deleteUser(userId); System.out.println("执行删除成功后的操作"); } } public class Client { public static void main(String[] args) { UserService proxy = new ProxyServiceImpl(new UserServiceImpl()); proxy.deleteUser(1L); } }

输出

2. 动态代理(自动生成代理类)

不用手动写代理类,运行时自动生成代理对象,适合批量代理多个类。Java 中常用两种:

  • JDK 动态代理:JDK 动态代理通过生成“实现接口的代理类”,因此必须依赖接口,否则无法代理
  • CGLIB 动态代理:基于类(无需接口),通过继承 + 方法重写 + 方法拦截

JDK 动态代理示例

public interface UserService { void deleteUser(Long userId); } public class UserServiceImpl implements UserService { @Override public void deleteUser(Long userId) { // 核心业务逻辑 System.out.println("删除用户id为"+userId+"的用户!"); } } public class DynamicProxyFactory { // 传入目标对象,返回 动态代理对象 public static Object getProxy(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; } ); } }

输出

CGLIB动态代理示例

public class UserService { public void deleteUser(Long userId) { System.out.println("删除用户id为"+userId+"的用户!"); } } public class CglibProxyFactory { public static UserService getProxy(Class<?> clazz) { Enhancer enhancer = new Enhancer(); // 继承目标类,生成子类代理 enhancer.setSuperclass(clazz); // 设置回调拦截器 enhancer.setCallback(new UserMethodInterceptor()); return (UserService) enhancer.create(); } } public class UserMethodInterceptor implements MethodInterceptor { /** * 拦截所有目标方法执行 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 前置增强 System.out.println("CGLIB前置:日志记录、权限校验"); // 执行目标原有方法 Object result = proxy.invokeSuper(obj, args); // 后置增强 System.out.println("CGLIB后置:删除操作完毕"); return result; } } public class Client { public static void main(String[] args) { // 直接获取代理对象,无需接口 UserService proxy = CglibProxyFactory.getProxy(UserService.class); proxy.deleteUser(1L); } }

结果

三、代理模式的典型应用场景

  1. 远程代理:代理远程对象(比如 RPC 框架、微服务调用)
  2. 虚拟代理:延迟创建耗资源的对象(比如:网页先显示图片占位符,加载完再显示真实图片)
  3. 安全代理:控制访问权限(比如:接口权限校验)
  4. 日志 / 监控代理:统一记录日志、统计接口耗时
  5. 缓存代理:给方法加缓存,避免重复执行
  6. Spring AOP:底层就是动态代理,实现切面编程(日志、事务、权限)

四、静态代理 vs 动态代理

特点静态代理动态代理
实现方式手动编写代理类运行时自动生成代理对象
灵活性低(一个类一个代理)高(一个代理类代理多个类)
效率编译期确定,性能高运行时生成,略低
适用场景代理对象少且固定批量代理、框架开发

五、CGlib和JDK动态代理

  • JDK 动态代理

    • 基于接口实现
    • 底层反射
    • 目标类可以不用继承
    • JDK 动态代理通过生成“实现接口的代理类”,因此必须依赖接口,否则无法代理
  • CGLIB 动态代理

    • 基于继承子类实现
    • 无需接口
    • 不能代理final类、final方法
    • Spring 没有接口时自动用 CGLIB

六、总结

  1. 代理模式 = 代理人 + 真实对象,代理帮原对象处理杂事,原对象专注核心业务
  2. 核心价值:不修改原代码,扩展功能(日志、权限、缓存等)
  3. 两种实现:静态代理(简单)、动态代理(灵活,Spring 底层核心)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 23:28:22

Docker与Streamlit:解决本地部署问题

在使用Docker部署Python应用程序时,尤其是像Streamlit这样的Web应用,我们经常会遇到一些网络连接问题。本文将通过一个实例来解释如何解决这些问题,帮助你成功运行并访问你的Streamlit应用。 问题描述 假设我们正在构建一个基本的Q&A chatbot,使用的Python版本是3.10…

作者头像 李华
网站建设 2026/4/22 23:28:05

如何永久保存微信聊天记录?WeChatMsg三步搞定完整备份与年度报告

如何永久保存微信聊天记录&#xff1f;WeChatMsg三步搞定完整备份与年度报告 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trendin…

作者头像 李华
网站建设 2026/4/22 23:24:53

微服务配置管理进阶

微服务配置管理进阶&#xff1a;构建高效可靠的分布式系统 在微服务架构中&#xff0c;配置管理是确保系统稳定性和灵活性的关键环节。随着业务规模扩大&#xff0c;传统的配置管理方式往往难以应对动态变化的需求。如何实现配置的高效管理、动态更新和版本控制&#xff0c;成…

作者头像 李华
网站建设 2026/4/22 23:23:24

别再复制粘贴了!用这个option.js文件统一管理你的Avue表单配置

别再复制粘贴了&#xff01;用Option.js统一管理Avue表单配置 每次新建表单都要重复配置size、labelWidth这些基础属性&#xff1f;团队协作时表单样式五花八门&#xff1f;是时候告别这种低效的开发模式了。本文将带你用工程化思维重构Avue表单配置管理&#xff0c;通过一个中…

作者头像 李华
网站建设 2026/4/22 23:22:26

从VBA迁移到LibreOffice?这份Basic宏避坑指南和代码对照表请收好

从VBA到LibreOffice Basic&#xff1a;开发者迁移实战手册 当长期依赖Microsoft Office生态的开发者突然需要切换到LibreOffice时&#xff0c;那种感觉就像习惯右手写字的人被迫改用左手——工具看似相似&#xff0c;却处处藏着微妙的差异。本文将从实际应用场景出发&#xff0…

作者头像 李华