本文共 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) { MapflashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; }
@SuppressWarnings("unchecked") public static MapgetInputFlashMap(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(Mapmodel, 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) { ListallFlashMaps = 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 ListretrieveFlashMaps(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) { ListallFlashMaps = 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/