Spring MVC 九大组件源码深度剖析(九):FlashMapManager - 重定向数据的守护者
本文深入解析Spring MVC中的FlashMapManager组件,揭秘其如何优雅解决重定向请求间的数据传递难题。
·
文章目录
本文是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 {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String FLASH_MAP_KEY_PREFIX = "flashmap:";
private static final long DEFAULT_EXPIRATION = 180; // 3分钟
@Override
protected 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();
}
@Override
protected 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 {
@Autowired
private CryptoService cryptoService;
@Override
protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {
List<FlashMap> encryptedMaps = super.retrieveFlashMaps(request);
return encryptedMaps.stream()
.map(this::decryptFlashMap)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
@Override
protected 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; // 1MB
private static final int MAX_MAP_COUNT = 20;
@Override
protected 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-16
if (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");
}
@Override
public 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;
});
}
@Override
public 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);
});
}
}
九、设计思想总结
- 一次性数据传递
自动清理机制确保数据不会长期驻留内存 - 精确的目标匹配
支持基于路径和参数的精确数据投递 - 线程安全设计
基于Session互斥锁确保并发安全 - 可扩展架构
支持自定义存储策略(Session、Redis等) - 无缝集成
与Spring MVC现有组件完美协作
系列总结:Spring MVC九大组件架构哲学
通过这九篇深度剖析,我们见证了Spring MVC精妙的设计思想:
🏗️ 架构层次清晰

🎯 设计模式典范
- 策略模式:各种Resolver和Adapter
- 责任链模式:拦截器、异常解析器链
- 适配器模式:HandlerAdapter统一处理器接口
- 工厂模式:ViewResolver创建视图对象
- 观察者模式:事件发布与监听
🔧 扩展性设计
每个组件都通过接口抽象,支持自定义实现,体现了Spring框架"开放封闭"的设计原则。
Spring MVC九大组件源码剖析系列至此圆满结束! 希望这个系列能帮助大家深入理解Spring MVC的设计精髓,在实际开发中更好地运用和扩展这些强大的组件。
更多推荐

所有评论(0)