在上一篇博客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重定向数据保存的更多相关文章

  1. springMVC源码分析--DispatcherServlet请求获取及处理

    在之前的博客springMVC源码分析--容器初始化(二)DispatcherServlet中我们介绍过DispatcherServlet,是在容器初始化过程中出现的,我们之前也说过Dispatche ...

  2. springMVC源码分析--页面跳转RedirectView(三)

    之前两篇博客springMVC源码分析--视图View(一)和springMVC源码分析--视图AbstractView和InternalResourceView(二)中我们已经简单的介绍了View相 ...

  3. SpringMVC源码分析--容器初始化(五)DispatcherServlet

    上一篇博客SpringMVC源码分析--容器初始化(四)FrameworkServlet我们已经了解到了SpringMVC容器的初始化,SpringMVC对容器初始化后会进行一系列的其他属性的初始化操 ...

  4. SpringMVC源码分析6:SpringMVC的视图解析原理

    title: SpringMVC源码分析6:SpringMVC的视图解析原理 date: 2018-06-07 11:03:19 tags: - SpringMVC categories: - 后端 ...

  5. [心得体会]SpringMVC源码分析

    1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...

  6. 8、SpringMVC源码分析(3):分析ModelAndView的形成过程

    首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...

  7. 7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解

    从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, res ...

  8. springMVC源码分析--ViewNameMethodReturnValueHandler返回值处理器(三)

    之前两篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)和springMVC源码分析--HandlerMethodReturnValu ...

  9. springMVC源码分析--HandlerMethodReturnValueHandlerComposite返回值解析器集合(二)

    在上一篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)我们介绍了返回值解析器HandlerMethodReturnValueHand ...

随机推荐

  1. js的模块化

    模块化的好处 以前我们平常写js,需要引用大量大js文件,还得注意先后顺序,因为是一层一层依赖的关系.很是麻烦,现在有了模块化,我们只需要知道,我们要实现这个功能需要哪一个js文件就ok,其他的不需要 ...

  2. Codeforces Round #470 (rated, Div. 2, based on VK Cup 2018 Round 1) C.Producing Snow

    题目链接  题意  每天有体积为Vi的一堆雪,所有存在的雪每天都会融化Ti体积,求出每天具体融化的雪的体积数. 分析 对于第i天的雪堆,不妨假设其从一开始就存在,那么它的初始体积就为V[i]+T[1. ...

  3. 如何用matplotlib绘制决策边界

    import matplotlib.pyplot as plt import numpy as np import sklearn import sklearn.datasets import skl ...

  4. Nginx 学习笔记(二)Web 服务启用 HTTP/2

    个人网站提升为HTTP2.0协议,加速访问 一.条件 1.openssl的版本必须在1.0.2e及以上 2.nginx的版本必须在1.9.5以上 二.编译安装 1.openssl 编译安装 (1)开始 ...

  5. idea常用快捷键及自定义快捷键汇总

    以下都是挨个进行验证过的 生成get和set方法为:alt+insert 自动补全返回值,自动补全变量名称和属性名称:ctrl+alt+v 输入System.out.println()的快捷方法是:输 ...

  6. 【转】Robot Framework作者建议如何选择自动化测试框架

    原文:http://www.infoq.com/cn/news/2012/06/robot-author-suggest-autotest 软件自动化测试,作为手工测试的替代,越来越受到关注.Pekk ...

  7. luogu P3565 [POI2014]HOT-Hotels

    传送门 无脑暴力+O2=AC 题目要统计距离两两相等的三个点的组数,这三个点之间显然有一个点,并且这三个点到这个点的距离都相同.所以枚举中间这个点作为根,然后bfs整棵树,对于每一层,把以根的某个儿子 ...

  8. 使用flask_socketio实现服务端向客户端定时推送

    websocket连接是客户端与服务器之间永久的双向通信通道,直到某方断开连接. 双向通道意味着在连接时,服务端随时可以发送消息给客户端,反之亦然,这在一些需要即时通讯的场景比如多人聊天室非常重要. ...

  9. 解决kali linux 开启ssh服务后连接不上的问题

    今天在手机端装了NetHunter 想连接PC的kali ,可是怎么都连不上 综合网友的经验: 1.修改sshd_config文件,命令为:vi /etc/ssh/sshd_config 将#Pass ...

  10. C++:MSVCRTD.lib(crtexe.obj) : error LNK2019: 无法解析的外部符号 _main,该符号在函数 ___tmainCRTStart

    在VS2013中Build一个C++程序报这个错,解决方案如下: 在解决方案管理器中选择该项目,项目/属性/连接器/系统/子系统 把控制台 (/SUBSYSTEM:CONSOLE)改为 窗口 (/SU ...