springMVC源码分析--FlashMap和FlashMapManager重定向数据保存
在上一篇博客springMVC源码分析--页面跳转RedirectView(三)中我们看到了在RedirectView跳转时会将跳转之前的请求中的参数保存到fFlashMap中,然后通过FlashManager保存起来。
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,
HttpServletResponse response) throws IOException {
//创建跳转链接
String targetUrl = createTargetUrl(model, request);
targetUrl = updateTargetUrl(targetUrl, model, request, response);
//获取原请求所携带的数据
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
if (!CollectionUtils.isEmpty(flashMap)) {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(targetUrl).build();
flashMap.setTargetRequestPath(uriComponents.getPath());
flashMap.addTargetRequestParams(uriComponents.getQueryParams());
FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
if (flashMapManager == null) {
throw new IllegalStateException("FlashMapManager not found despite output FlashMap having been set");
}
//将数据保存起来,作为跳转之后请求的数据使用
flashMapManager.saveOutputFlashMap(flashMap, request, response);
}
//重定向操作
sendRedirect(request, response, targetUrl, this.http10Compatible);
}
接下来我们分别认识一下FlashMap和FalshMapManager
FlashMapManager是一个接口,定义了保存FlashMap和获取FlashMap的方法。
public interface FlashMapManager {
//从session中获取flashMap
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
//将falshMap保存到session中
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}
两个实现方法都在AbstractFlashMapManager抽象方法中:
saveOutputFlashMap方法实现如下
@Override
public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
if (CollectionUtils.isEmpty(flashMap)) {
return;
}
String path = decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);
flashMap.setTargetRequestPath(path);
if (logger.isDebugEnabled()) {
logger.debug("Saving FlashMap=" + flashMap);
}
flashMap.startExpirationPeriod(getFlashMapTimeout());
Object mutex = getFlashMapsMutex(request);
if (mutex != null) {
synchronized (mutex) {
List<FlashMap> allFlashMaps = retrieveFlashMaps(request);
allFlashMaps = (allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList<FlashMap>());
allFlashMaps.add(flashMap);
//flashMap是在子类SessionFlashManager中
updateFlashMaps(allFlashMaps, request, response);
}
}
else {
List<FlashMap> allFlashMaps = retrieveFlashMaps(request);
allFlashMaps = (allFlashMaps != null ? allFlashMaps : new LinkedList<FlashMap>());
allFlashMaps.add(flashMap);
//flashMap是在子类SessionFlashManager中
updateFlashMaps(allFlashMaps, request, response);
}
}
updateFlashMaps方法的实现机制就是将数据保存的请求的Session中,这样跳转后的请求可以从Session中获取数据,并且所有的实现都保存在同一个Session中,为了防止不同重定向请求的数据相互影响,这边有锁进行处理控制。
@Override
protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {
WebUtils.setSessionAttribute(request, FLASH_MAPS_SESSION_ATTRIBUTE, (!flashMaps.isEmpty() ? flashMaps : null));
}
retrieveAndUpdate获取FlashMap的实现如下:
@Override
public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {
List<FlashMap> allFlashMaps = retrieveFlashMaps(request);
........
}
retrieveFlashMaps中的实现机制就是从Session中获取数据
@Override
@SuppressWarnings("unchecked")
protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request) {
HttpSession session = request.getSession(false);
return (session != null ? (List<FlashMap>) session.getAttribute(FLASH_MAPS_SESSION_ATTRIBUTE) : null);
}
当重定向的请求在浏览器中重定向之后会进入的DispatcherServlet的doService方法
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
........
//从Session中获取保存的FlashMap中的值
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
//将值保存到request中。这样就不需要通过浏览器跳转组装链接来传递参数了
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
.......
}
FlashMap简单来说就是一个HashMap,用于数据保存。
public final class FlashMap extends HashMap<String, Object> implements Comparable<FlashMap> {
private String targetRequestPath;
private final MultiValueMap<String, String> targetRequestParams = new LinkedMultiValueMap<String, String>(4);
private long expirationTime = -1;
public void setTargetRequestPath(String path) {
this.targetRequestPath = path;
}
public String getTargetRequestPath() {
return this.targetRequestPath;
}
public FlashMap addTargetRequestParams(MultiValueMap<String, String> params) {
if (params != null) {
for (String key : params.keySet()) {
for (String value : params.get(key)) {
addTargetRequestParam(key, value);
}
}
}
return this;
}
public FlashMap addTargetRequestParam(String name, String value) {
if (StringUtils.hasText(name) && StringUtils.hasText(value)) {
this.targetRequestParams.add(name, value);
}
return this;
}
public MultiValueMap<String, String> getTargetRequestParams() {
return this.targetRequestParams;
}
public void startExpirationPeriod(int timeToLive) {
this.expirationTime = System.currentTimeMillis() + timeToLive * 1000;
}
public void setExpirationTime(long expirationTime) {
this.expirationTime = expirationTime;
}
public long getExpirationTime() {
return this.expirationTime;
}
public boolean isExpired() {
return (this.expirationTime != -1 && System.currentTimeMillis() > this.expirationTime);
}
@Override
public int compareTo(FlashMap other) {
int thisUrlPath = (this.targetRequestPath != null ? 1 : 0);
int otherUrlPath = (other.targetRequestPath != null ? 1 : 0);
if (thisUrlPath != otherUrlPath) {
return otherUrlPath - thisUrlPath;
}
else {
return other.targetRequestParams.size() - this.targetRequestParams.size();
}
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof FlashMap)) {
return false;
}
FlashMap otherFlashMap = (FlashMap) other;
return (super.equals(otherFlashMap) &&
ObjectUtils.nullSafeEquals(this.targetRequestPath, otherFlashMap.targetRequestPath) &&
this.targetRequestParams.equals(otherFlashMap.targetRequestParams));
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + ObjectUtils.nullSafeHashCode(this.targetRequestPath);
result = 31 * result + this.targetRequestParams.hashCode();
return result;
}
@Override
public String toString() {
return "FlashMap [attributes=" + super.toString() + ", targetRequestPath=" +
this.targetRequestPath + ", targetRequestParams=" + this.targetRequestParams + "]";
}
}
springMVC源码分析--FlashMap和FlashMapManager重定向数据保存的更多相关文章
- springMVC源码分析--DispatcherServlet请求获取及处理
在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...
- springMVC源码分析--页面跳转RedirectView(三)
之前两篇博客springMVC源码分析--视图View(一)和springMVC源码分析--视图AbstractView和InternalResourceView(二)中我们已经简单的介绍了View相 ...
- SpringMVC源码分析--容器初始化(五)DispatcherServlet
上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...
- SpringMVC源码分析6:SpringMVC的视图解析原理
title: SpringMVC源码分析6:SpringMVC的视图解析原理 date: 2018-06-07 11:03:19 tags: - SpringMVC categories: - 后端 ...
- [心得体会]SpringMVC源码分析
1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...
- 8、SpringMVC源码分析(3):分析ModelAndView的形成过程
首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...
- 7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解
从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, res ...
- springMVC源码分析--ViewNameMethodReturnValueHandler返回值处理器(三)
之前两篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)和springMVC源码分析--HandlerMethodReturnValu ...
- springMVC源码分析--HandlerMethodReturnValueHandlerComposite返回值解析器集合(二)
在上一篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)我们介绍了返回值解析器HandlerMethodReturnValueHand ...
随机推荐
- Java_myBatis_一对多映射
例如我们有需求需要实现以下查询 "一个用户对多条订单编号": select user.*,o.number,o.createtime from user left JOIN or ...
- IdeaVim-常用操作
IdeaVim简介 IdeaVim是IntelliJ IDEA的一款插件,他提高了我们写代码的速度,对代码的跳转,查找也很友好. 安装位置 安装之后它在 Tools > Vim Emulator ...
- java了解哪些锁
问题: 1.Java里的锁了解哪些? 说了Lock和synchronized 它们的使用方式和实现原理有什么区别呢?使用的话,synchronize用于方法和代码块,可以锁对象和类以及方法,Lock ...
- dubbo面试问题
为什么用微服务: 为什么zookeeper能作为注册中心: 使用分布式碰到的bug, zookeeper有集群吗?怎么实现的: zookeeper宕机还能访问吗: 服务失效踢出zookeeper中临时 ...
- 五、u-boot 启动流程---u-boot.lds
5.1 u-boot.lds 链接脚本分析 uboot 编译出来的第一个链接脚本就是执行 u-boot.lds 链接脚本,去掉里面无用的和没有定义的,进行分析. /* 配置头文件,自动生成的,包含芯 ...
- IDAPython学习(一)
1.概述 IDAPython在IDA中集成了Python解释器,除了提供了Python功能外,使用这个插件还可以编写实现IDC脚本语言的所有Python脚本. IDAPython显著优势在于,它可以充 ...
- 并行动画组QParallelAnimationGroup
QParallelAnimationGroup会同时执行添加到该组的所有动画 import sys from PyQt5.QtGui import QPixmap from PyQt5.QtCore ...
- Java SE 之 DAO层接口设计思想
用图说话 好处 1.只需要定义好IBaseDao的接口方法,并只需要实现BaseDaoImpl的方法,而具体的业务类和业务类/接口的方法的基本方法(IBaseDao已定义的)并不需要再考虑实现. 2. ...
- DSO windowed optimization 代码 (4)
5 "step"计算 参考<DSO windowed optimization 公式>,计算各个优化变量的增加量. 公式再写一下: \[\begin{align} \b ...
- 解决ipad连接不上电脑的问题
检查一下信息: 1.iTunes是否安装 2.数据线是否完好 3.检查下图中的两个设备是否开启 4.最后一步是最恶心的:是否关闭了防火墙!!!! 操作步骤如下图示 我就是因为打开了防火墙,所以一直连接 ...