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

Spring MVC 九大组件源码深度剖析(九):FlashMapManager - 重定向数据的守护者

文章目录

      • 一、重定向数据传递的经典难题
      • 二、核心接口设计
      • 三、FlashMap数据结构
        • 1. FlashMap核心定义
        • 2. 核心特性
      • 四、默认实现:SessionFlashMapManager
        • 1. 核心源码解析
        • 2. Session存储结构
      • 五、在DispatcherServlet中的工作流程
        • 1. 完整工作流程
        • 2. 关键集成点源码
      • 六、实际应用场景
        • 1. 表单提交后重定向显示结果
        • 2. 复杂对象传递
      • 七、高级特性与扩展
        • 1. 自定义FlashMapManager实现
        • 2. 安全增强FlashMapManager
      • 八、性能优化与最佳实践
        • 1. 内存管理优化
        • 2. 监控与诊断
      • 九、设计思想总结
      • 系列总结:Spring MVC九大组件架构哲学
        • 🏗️ 架构层次清晰
        • 🎯 设计模式典范
        • 🔧 扩展性设计

本文是Spring MVC九大组件解析系列的收官之作,我们将深入探索FlashMapManager如何解决Web开发中的经典难题——在重定向请求间安全传递数据,揭秘POST-REDIRECT-GET模式的实现原理。
Spring MVC整体设计核心解密参阅:Spring MVC设计精粹:源码级架构解析与实践指南

一、重定向数据传递的经典难题

在Web开发中,重定向后的数据传递一直是个棘手问题:

在这里插入图片描述

传统解决方案的缺陷:

  • URL参数:长度限制,暴露敏感数据
  • Session存储:需要手动清理,容易造成内存泄漏
  • 数据库存储:过度设计,性能开销大

Spring MVC通过FlashMapManager完美解决了这个问题,实现了一次性、安全、自动清理的重定向数据传递。

二、核心接口设计

源码位置org.springframework.web.servlet.FlashMapManager
核心接口
在这里插入图片描述

设计哲学:通过统一的接口抽象不同的存储策略,支持Session、Cookie等多种实现方式。

三、FlashMap数据结构

1. FlashMap核心定义

源码位置org.springframework.web.servlet.FlashMap
核心源码
在这里插入图片描述

2. 核心特性
特性说明优势
自动过期设置存活时间,避免数据长期驻留防止内存泄漏
精确匹配基于路径和参数的目标请求匹配避免数据误用
线程安全继承ConcurrentMap特性支持并发访问
轻量级基于标准Map实现性能高效

四、默认实现:SessionFlashMapManager

1. 核心源码解析

源码位置org.springframework.web.servlet.support.SessionFlashMapManager
核心源码
在这里插入图片描述

2. Session存储结构
// Session中的数据结构示例
session: {"org.springframework.web.servlet.support.SessionFlashMapManager.FLASH_MAPS": [{"targetRequestPath": "/success","expirationTime": 1640995200000,"targetRequestParams": {},"message": "操作成功!","alertType": "success"},{"targetRequestPath": "/user/list","expirationTime": 1640995260000, "userStatus": "created","userId": 12345}]
}

五、在DispatcherServlet中的工作流程

1. 完整工作流程

在这里插入图片描述

2. 关键集成点源码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

六、实际应用场景

1. 表单提交后重定向显示结果
@Controller
public class UserController {@PostMapping("/users")public String createUser(User user, RedirectAttributes redirectAttributes) {try {userService.create(user);// 添加成功消息到Flash属性redirectAttributes.addFlashAttribute("message", "用户创建成功!");redirectAttributes.addFlashAttribute("alertType", "success");} catch (Exception e) {// 添加错误消息到Flash属性redirectAttributes.addFlashAttribute("message", "用户创建失败:" + e.getMessage());redirectAttributes.addFlashAttribute("alertType", "error");}// 重定向到用户列表页return "redirect:/users";}@GetMapping("/users")public String listUsers(Model model) {// Flash属性会自动添加到Model中model.addAttribute("users", userService.findAll());return "user/list";}
}
2. 复杂对象传递
@Controller
public class OrderController {@PostMapping("/orders/{id}/process")public String processOrder(@PathVariable Long id, RedirectAttributes redirectAttributes) {Order order = orderService.process(id);// 传递复杂对象redirectAttributes.addFlashAttribute("processedOrder", order);redirectAttributes.addFlashAttribute("processingTime", System.currentTimeMillis());// 设置精确的目标匹配FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);flashMap.setTargetRequestPath("/orders/result");flashMap.addTargetRequestParam("orderId", id.toString());return "redirect:/orders/result";}@GetMapping("/orders/result")public String showOrderResult(@RequestParam Long orderId, Model model) {// processedOrder会自动从FlashMap中获取return "order/result";}
}

七、高级特性与扩展

1. 自定义FlashMapManager实现
/*** 基于Redis的分布式FlashMapManager* 适用于集群环境*/
@Component
public class RedisFlashMapManager extends AbstractFlashMapManager {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;private static final String FLASH_MAP_KEY_PREFIX = "flashmap:";private static final long DEFAULT_EXPIRATION = 180; // 3分钟@Overrideprotected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {String sessionId = request.getSession().getId();String key = FLASH_MAP_KEY_PREFIX + sessionId;@SuppressWarnings("unchecked")List<FlashMap> flashMaps = (List<FlashMap>) redisTemplate.opsForValue().get(key);if (flashMaps != null) {// 过滤过期数据List<FlashMap> validMaps = flashMaps.stream().filter(flashMap -> !flashMap.isExpired()).collect(Collectors.toList());// 更新存储(移除过期数据)if (validMaps.size() != flashMaps.size()) {updateFlashMaps(validMaps, request, null);}return validMaps;}return Collections.emptyList();}@Overrideprotected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {String sessionId = request.getSession().getId();String key = FLASH_MAP_KEY_PREFIX + sessionId;if (!flashMaps.isEmpty()) {redisTemplate.opsForValue().set(key, flashMaps, DEFAULT_EXPIRATION, TimeUnit.SECONDS);} else {redisTemplate.delete(key);}}
}
2. 安全增强FlashMapManager
/*** 支持数据加密的FlashMapManager* 防止Session中的数据被篡改*/
public class SecureFlashMapManager extends SessionFlashMapManager {@Autowiredprivate CryptoService cryptoService;@Overrideprotected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {List<FlashMap> encryptedMaps = super.retrieveFlashMaps(request);return encryptedMaps.stream().map(this::decryptFlashMap).filter(Objects::nonNull).collect(Collectors.toList());}@Overrideprotected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {List<FlashMap> encryptedMaps = flashMaps.stream().map(this::encryptFlashMap).collect(Collectors.toList());super.updateFlashMaps(encryptedMaps, request, response);}private FlashMap encryptFlashMap(FlashMap original) {try {FlashMap encrypted = new FlashMap();for (Map.Entry<String, Object> entry : original.entrySet()) {String encryptedKey = cryptoService.encrypt(entry.getKey());String encryptedValue = cryptoService.encrypt(entry.getValue().toString());encrypted.put(encryptedKey, encryptedValue);}encrypted.setTargetRequestPath(original.getTargetRequestPath());encrypted.setExpirationTime(original.getExpirationTime());return encrypted;} catch (Exception e) {logger.error("Failed to encrypt FlashMap", e);return original; // 降级处理}}private FlashMap decryptFlashMap(FlashMap encrypted) {try {FlashMap decrypted = new FlashMap();for (Map.Entry<String, Object> entry : encrypted.entrySet()) {String decryptedKey = cryptoService.decrypt(entry.getKey());String decryptedValue = cryptoService.decrypt(entry.getValue().toString());decrypted.put(decryptedKey, decryptedValue);}decrypted.setTargetRequestPath(encrypted.getTargetRequestPath());decrypted.setExpirationTime(encrypted.getExpirationTime());return decrypted;} catch (Exception e) {logger.error("Failed to decrypt FlashMap", e);return null; // 解密失败,丢弃该FlashMap}}
}

八、性能优化与最佳实践

1. 内存管理优化
/*** 带内存限制的FlashMapManager* 防止FlashMap数据过大导致内存溢出*/
public class MemoryAwareFlashMapManager extends SessionFlashMapManager {private static final long MAX_TOTAL_SIZE = 1024 * 1024; // 1MBprivate static final int MAX_MAP_COUNT = 20;@Overrideprotected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {// 1. 限制FlashMap数量if (flashMaps.size() > MAX_MAP_COUNT) {flashMaps = flashMaps.subList(0, MAX_MAP_COUNT);}// 2. 限制总数据大小long totalSize = calculateTotalSize(flashMaps);if (totalSize > MAX_TOTAL_SIZE) {flashMaps = trimToSize(flashMaps, MAX_TOTAL_SIZE);}super.updateFlashMaps(flashMaps, request, response);}private long calculateTotalSize(List<FlashMap> flashMaps) {return flashMaps.stream().mapToLong(this::calculateSize).sum();}private long calculateSize(FlashMap flashMap) {long size = 0;for (Map.Entry<String, Object> entry : flashMap.entrySet()) {size += entry.getKey().length() * 2; // UTF-16if (entry.getValue() instanceof String) {size += ((String) entry.getValue()).length() * 2;} else {// 估算对象大小size += 100; // 保守估计}}return size;}private List<FlashMap> trimToSize(List<FlashMap> flashMaps, long maxSize) {List<FlashMap> trimmed = new ArrayList<>();long currentSize = 0;for (FlashMap map : flashMaps) {long mapSize = calculateSize(map);if (currentSize + mapSize <= maxSize) {trimmed.add(map);currentSize += mapSize;} else {break;}}return trimmed;}
}
2. 监控与诊断
/*** 带监控的FlashMapManager* 记录使用情况用于性能分析*/
public class MonitoredFlashMapManager extends SessionFlashMapManager {private final MeterRegistry meterRegistry;private final Counter saveCounter;private final Counter retrieveCounter;private final Timer operationTimer;public MonitoredFlashMapManager(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;this.saveCounter = meterRegistry.counter("flashmap.operation", "type", "save");this.retrieveCounter = meterRegistry.counter("flashmap.operation", "type", "retrieve");this.operationTimer = meterRegistry.timer("flashmap.operation.duration");}@Overridepublic FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {return operationTimer.record(() -> {retrieveCounter.increment();FlashMap result = super.retrieveAndUpdate(request, response);// 记录命中情况if (result != null) {meterRegistry.counter("flashmap.hit").increment();} else {meterRegistry.counter("flashmap.miss").increment();}return result;});}@Overridepublic void save(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {operationTimer.record(() -> {saveCounter.increment();// 记录数据大小int size = flashMap.size();meterRegistry.summary("flashmap.size").record(size);super.save(flashMap, request, response);});}
}

九、设计思想总结

  1. 一次性数据传递
    自动清理机制确保数据不会长期驻留内存
  2. 精确的目标匹配
    支持基于路径和参数的精确数据投递
  3. 线程安全设计
    基于Session互斥锁确保并发安全
  4. 可扩展架构
    支持自定义存储策略(Session、Redis等)
  5. 无缝集成
    与Spring MVC现有组件完美协作

系列总结:Spring MVC九大组件架构哲学

通过这九篇深度剖析,我们见证了Spring MVC精妙的设计思想:

🏗️ 架构层次清晰

在这里插入图片描述

🎯 设计模式典范
  • 策略模式:各种Resolver和Adapter
  • 责任链模式:拦截器、异常解析器链
  • 适配器模式:HandlerAdapter统一处理器接口
  • 工厂模式:ViewResolver创建视图对象
  • 观察者模式:事件发布与监听
🔧 扩展性设计

每个组件都通过接口抽象,支持自定义实现,体现了Spring框架"开放封闭"的设计原则。

Spring MVC九大组件源码剖析系列至此圆满结束! 希望这个系列能帮助大家深入理解Spring MVC的设计精髓,在实际开发中更好地运用和扩展这些强大的组件。

http://www.dtcms.com/a/449659.html

相关文章:

  • 网站设计上市公司继续浏览此网站(不推荐)
  • The “Launch” - 价值交付与灰度发布
  • 做网站公司(信科网络)网站开发外包报价
  • libopenssl1_0_0-1.0.2p-3.49.1.x86_64安装教程(RPM包手动安装步骤+依赖解决附安装包下载)
  • 有些人做网站不用钱的 对吗网站建设经典范例
  • C52-二级指针
  • 【微科普】PID 多久计算一次?(第四弹):嵌入式系统中 PID 控制周期的科学选择与实践
  • 目前流行的网站开发设计廊坊商昊网站建设
  • 《WSGI 到 ASGI:Python Web 架构的演进与桥梁之道》
  • 数据库完整指南:从基础到 Django 集成
  • 福建设计招聘网站seo sem什么意思
  • 用scala做的网站视频网址链接哪里找
  • 基于pyqt5实现的视频抽帧工具源码+项目说明用于目标检测图片采集
  • 浙江省建设局房管科网站建筑模板915 1830价格
  • 怎么做公司官方网站苏州教育网站建设
  • AI Agent:重塑未来智能的核心驱动力
  • node-red 采集CNC?
  • Linux驱动开发与BuildRoot是什么关系与其的应用场景
  • 如何自己做企业网站网站建设与开发的论文
  • Windows批处理进阶使用教程
  • 中秋佳节与 Java 的奇妙联想
  • 评委打分算法解析:从基础实现到性能优化(洛谷)
  • k8s中Pod和Node的故事(2):优先级、抢占和驱逐
  • 网站架构包含哪几部分苏州网站建设网站制作的公司
  • UML笔记 之 事物和关系
  • 中国黄金集团建设有限公司官方网站照片在线编辑
  • 从零开始学习Python Django:从环境搭建到第一个 Web 应用
  • Lenovo XiaoXin Pro13 i5-10210U_i7-10710U 黑苹果 EFI
  • 网站建设服务商24小时接单移动应用开发专业学什么
  • 从 0 到 PB 级存储:MinIO 分布式文件系统实战指南与架构解密