博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
假装看源码之springmvc (二) 重定向带参数RedirectAttributes的原理
阅读量:4074 次
发布时间:2019-05-25

本文共 6922 字,大约阅读时间需要 23 分钟。

   重定向带参数RedirectAttributes的原理

一、   概述:有些功能需要在重定向的时候,带上上一次请求的参数,第一个方法,可以采用重定向拼接参数方法,第二个方法,可以采用RedirectAttributes的方式,用它来添加需要重定向带上的参数,而RedirectAttributes也有2个方式。然后重定向请求返回到页面的时候,才可以得到这个请求的参数,或者用@ModelAttribute获取。

  RedirectAttributes重定向带参数的2个方式:

 方式1: 通过attr.addAttribute(key, value),其就是拼接请求参数的方式加到需要重定向的请求上。

方式2:通过attr.addFlashAttribute(key, value) , 其是通过session临时存储参数数据,下一次重定向时取出临时存储的参数数据。

/**	 * 1、原始请求: /demo2/redirect	 */	@RequestMapping("/redirect")	public String RedirecAttr(RedirectAttributes attr){		attr.addAttribute("id", 1);  // 方式1: 即拼接id=1		attr.addFlashAttribute("result", "成功"); // 方式2		return "redirect:/demo2/list";	}		/**	 * 2、重定向的请求: 在浏览器显示的链接是 /demo2/list?id=1	 */	@RequestMapping("list")	public String list(int id,String result, @ModelAttribute("result") String result2){		System.out.println(id); // 可以获取参数		System.out.println(result); // 无法获取参数		System.out.println(result2); // 可以获取参数		return "/index"; // 页面${result}可以获取参数的值	}

 

二、源码分析

   1、分析问题? 

       请求参数什么时候存入session?  请求参数什么时候取出session? 存入session的是什么? 

    2、请求参数什么时候存入session?

    我们在处理方法中添加attr.addFlashAttribute(key, value),在处理方法结束后,在重定向前,会将RedirectAttributes所有的参数添加到FlashMap,让后放到OUTPUT_FLASH_MAP为key的请求域中。我们可以追踪请求流程,在RequestMappingHandlerAdapter中找到getModelAndView方法。

private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {	    ......省略		if (model instanceof RedirectAttributes) {			Map
flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; }
@SuppressWarnings("unchecked")	public static Map
getInputFlashMap(HttpServletRequest request) { return (Map
) request.getAttribute(DispatcherServlet.INPUT_FLASH_MAP_ATTRIBUTE); } public static FlashMap getOutputFlashMap(HttpServletRequest request) { return (FlashMap) request.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE); }

RequestContextUtils中,我们可以看到请求域存有2个falshMap,先不探究flashMap结构,inputFlashMap就是从上一个请求重定向带的参数,outFlashMap就是包装了本次成需要重定向的参数,那么我们这里只是放到了请求域,还需要往下看找到何时通过什么放入session。我们需要跟踪重定向视图RedirectView的处理。

RedirectView中,我们在renderMergedOutputModel()方法中可以找到将outFlashMap通过一个FlashMapManager放入session中。暂时也不看FlashMapManager的内部细节。'

protected void renderMergedOutputModel(Map
model, HttpServletRequest request, HttpServletResponse response) throws IOException { // 从请求域取出FlashMap FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); if (!CollectionUtils.isEmpty(flashMap)) { FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request); // 放入session中 flashMapManager.saveOutputFlashMap(flashMap, request, response); } // 重定向 sendRedirect(request, response, targetUrl, this.http10Compatible); }

  3、请求参数什么时候取出session?

  请求参数取出session就是在处理方法处理前也是通过FlashMapManager从session中取出的我们可以在DispatcherServlet的doDispatch方法中找到。

FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);		if (inputFlashMap != null) {			request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));		}		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());

 retrieveAndUpdate即通过FlashMapManager从session中取出inputFlashMap,并且放到请求域中,后续在参数解析的时候会放入到ModelAttribute中,并且这里初始化了outFlashMap。

  4、存入session的具体方式是什么? 

  现在取出和放入的时机和方式都了解了,他们都是通过FlashMapManager取出和放置FlashMap对象,那么我们就看看FlashMap类和FlashMapManager类。

FlashMap是继承了HashMap,扩展的主要功能,就是增加一个过期时间,创建的时候,设置过期时间,取的时候判断是否过期。

public void startExpirationPeriod(int timeToLive) {		this.expirationStartTime = System.currentTimeMillis();		this.timeToLive = timeToLive;	}	public boolean isExpired() {		return (this.expirationStartTime != 0 &&				(System.currentTimeMillis() - this.expirationStartTime > this.timeToLive * 1000));	}

FlashMapManager的继承结构

 

取参数:AbstractFlashMapManager的retrieveAndUpdate方法主要是通过模板方法retrieveFlashMaps调用子类SessionFlashMapManager的实现,获取FlashMap的列表,然后取出掉过期的,匹配出路径跟请求路径匹配的。我们住户要看retrieveAndUpdate方法如何获取sessipn中存储的上一个请求参数。

public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response) {		List
allFlashMaps = retrieveFlashMaps(request); // 模板方法,子类sessin中取 if (CollectionUtils.isEmpty(allFlashMaps)) { return null; } List
mapsToRemove = getExpiredFlashMaps(allFlashMaps); // 过期的 FlashMap match = getMatchingFlashMap(allFlashMaps, request); // 匹配当前请求的 if (match != null) { mapsToRemove.add(match); } if (!mapsToRemove.isEmpty()) { if (logger.isDebugEnabled()) { logger.debug("Removing FlashMap(s): " + mapsToRemove); } Object mutex = getFlashMapsMutex(request); if (mutex != null) { // 如果加锁就已sesison为锁进行更新FlashMaps的操作 synchronized (mutex) { allFlashMaps = retrieveFlashMaps(request); if (allFlashMaps != null) { allFlashMaps.removeAll(mapsToRemove); // 移除失效 updateFlashMaps(allFlashMaps, request, response); // 更新剩余的 } } } else { allFlashMaps.removeAll(mapsToRemove); updateFlashMaps(allFlashMaps, request, response); } } return match; }

SessionFlashMapManager的retrieveFlashMaps方法,就是session中取FlashMap

protected List
retrieveFlashMaps(HttpServletRequest request) { HttpSession session = request.getSession(false); return (session != null ? (List
) session.getAttribute(FLASH_MAPS_SESSION_ATTRIBUTE) : null); }

存参数:存参数是SessionFlashMapManager的saveOutputFlashMap方法,就是设置flashMap的路径和过期时间,存入session中。

public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {            String path = decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);        flashMap.setTargetRequestPath(path); // 设置目标路径        decodeParameters(flashMap.getTargetRequestParams(), request);         flashMap.startExpirationPeriod(getFlashMapTimeout()); // 设置过期时间180秒        Object mutex = getFlashMapsMutex(request);        if (mutex != null) { // 如果有锁就以session为锁操作session            synchronized (mutex) {                  List
allFlashMaps = retrieveFlashMaps(request);                allFlashMaps = (allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList
());                allFlashMaps.add(flashMap); // 添加                updateFlashMaps(allFlashMaps, request, response); // 更新            }        }        else {            List
allFlashMaps = retrieveFlashMaps(request);            allFlashMaps = (allFlashMaps != null ? allFlashMaps : new LinkedList
());            allFlashMaps.add(flashMap);            updateFlashMaps(allFlashMaps, request, response);        }    }

三、结尾

   spring-mvc的redirectAttributes就是利用session临时存储重定向参数,在重定向的时候,将redirectAttributes的参数复制到一个FlashMap中,然后通过FlashMapManager存储到session中,在下一次请求的时候,再次通过FlashMapManager获取跟此次请求匹配存储于session中的FlashMap,在参数解析的时候,加入到@ModelAttribute。

 

转载地址:http://geuni.baihongyu.com/

你可能感兴趣的文章
搞定Java面试中的数据结构问题
查看>>
React Native(一):搭建开发环境、出Hello World
查看>>
React Native(二):属性、状态
查看>>
JSX使用总结
查看>>
React Native(四):布局(使用Flexbox)
查看>>
React Native(七):Android双击Back键退出应用
查看>>
Android自定义apk名称、版本号自增
查看>>
【剑指offer】q50:树中结点的最近祖先
查看>>
二叉树的非递归遍历
查看>>
【leetcode】Reorder List (python)
查看>>
【leetcode】Linked List Cycle (python)
查看>>
【leetcode】Candy(python)
查看>>
【leetcode】Sum Root to leaf Numbers
查看>>
【leetcode】Pascal's Triangle II (python)
查看>>
如何成为编程高手
查看>>
本科生的编程水平到底有多高
查看>>
Solr及Spring-Data-Solr入门学习
查看>>
python_time模块
查看>>
python_configparser(解析ini)
查看>>
selenium学习资料
查看>>