本文主要从falshMap初始化,存,取,消毁来进行源码分析,springmvc版本4.3.18。关于使用及验证请参考另一篇jsp取addFlashAttribute值深入理解即springMVC发redirect传隐藏参数

说明:在action中通过redirectAttributes.addFlashAttribute(userName,"mike"),retrun "redirect:indexTest.jsp",页面indexTest.jsp中打印该session的值发现有这组属性

org.springframework.web.servlet.support.SessionFlashMapManager.FLASH_MAPS==[FlashMap [attributes={userInfo=com.ccb.xx.vdo.UserInfo@2c367ad4, userID=ID001,userName=mike}, targetRequestPath=/project/page/public/indexTest.jsp, targetRequestParams={flag=[opening]}]],能过el表达式${sessionScope['org.springframework.web.servlet.support.SessionFlashMapManager.FLASH_MAPS'][0]['userId']}可取能值。

1.初始化和调用,首先是入springMVC 入口webmvc包中org.springframework.web.servlet.DispatcherServlet中的doService方法进行调用

public class DispatcherServlet extends FrameworkServlet
{
private FlashMapManager flashMapManager;
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
protected void initStrategies(ApplicationContext context)
{
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
  //初始化
initFlashMapManager(context);
}
private void initFlashMapManager(ApplicationContext context)
{
try
{
    //同级目录下DispatcherServlet.properties中配置
    //org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
    //此处代码相当于this.flashMapManager=(FlashMapManager)new SessionFlashMapManager(),
    //因SessionFlashMapManager extends AbstractFlashMapManager,此处会执行AbstractFlashMapManager.AbstractFlashMapManager()构造方法
    //,并设置过期时间this.flashMapTimeout = ;
this.flashMapManager = ((FlashMapManager)context.getBean("flashMapManager", FlashMapManager.class));
if (this.logger.isDebugEnabled())
this.logger.debug("Using FlashMapManager [" + this.flashMapManager + "]"); }
catch (NoSuchBeanDefinitionException ex)
{
this.flashMapManager = ((FlashMapManager)getDefaultStrategy(context, FlashMapManager.class));
if (this.logger.isDebugEnabled())
this.logger.debug("Unable to locate FlashMapManager with name 'flashMapManager': using default [" + this.flashMapManager + "]");
} } protected void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception
{
   //从session中获取及session中删除
   //此处执行的是模板类org.springframework.web.servlet.support.AbstractFlashMapManager中的retrieveAndUpdate方法
   FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null)//转存request
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));   //下面解释在调用controller的方法时,将request中falshmap==>ModelMap中,ModelMap继承自LinkedHashMap
  //在doDispatch中通过反射调用@RequestMapping修饰的方法,
   //HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  // mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  //HandlerAdapter是接口,模板类AbstractHandlerMethodAdapter具体handle(调用handleInternal)实现,
  //实际调用子类RequestMappingHandlerAdapter.handleInternal(调用invokeHandlerMethod(request, response, handlerMethod))进行处理,
  //在invokeHandlerMethod方法中,由下面代码将上面"转存request"放put到ModelMap中,所以jsp取addFlashAttribute值深入理解即springMVC发redirect传隐藏参数
  //中3.2的model输出中有flashAttribute参数
    // ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    // mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    // modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    // mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
   //invokeHandlerMethod详解SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解
   doDispatch(request, response);//最终由RequestMappingHandlerAdapter.invokeHandlerMethod(request, response, handlerMethod)进行处理
}
}

AbstractFlashMapManager源码如下,

retrieveAndUpdate调用实现子类的retrieveFlashMaps取session中的flashmap,调用子类updateFlashMaps删除session的方法

public abstract class AbstractFlashMapManager
  implements FlashMapManager
{
public final FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response)
{//从session中取SessionFlashMapManager.FLASH_MAPS
List allFlashMaps = retrieveFlashMaps(request);
if (CollectionUtils.isEmpty(allFlashMaps)) {
return null;
} if (this.logger.isDebugEnabled())
this.logger.debug("Retrieved FlashMap(s): " + allFlashMaps); List mapsToRemove = getExpiredFlashMaps(allFlashMaps);
  //此处match返回SessionFlashMapManager.FLASH_MAPS值,值的结构如([FlashMap [attributes={userInfo=com.ccb.xx.vdo.UserInfo@2c367ad4, userId=ID001,
  //userName=mike}, targetRequestPath=/project/page/public/indexTest.jsp, targetRequestParams={flag=[opening]}]])
  //虽然此时request attribute为空,
  //但实际执行此函数的spring-web包中org.springframework.web.util.UrlPathHelper中方法
  //getOriginatingRequestUri(request)代码“if (uri == null) uri = request.getRequestURI()”
  //代码可取到值,与SessionFlashMapManager.FLASH_MAPS中的targetRequestPath值相等
FlashMap match = getMatchingFlashMap(allFlashMaps, request);
if (match != null) {//将FLASH_MAPS加入待删除列表,后续请空allFlashMaps,再清空session该项的值
mapsToRemove.add(match);
} if (!(mapsToRemove.isEmpty())) {
if (this.logger.isDebugEnabled())
this.logger.debug("Removing FlashMap(s): " + mapsToRemove); Object mutex = getFlashMapsMutex(request);
if (mutex != null) {
synchronized (mutex) {
allFlashMaps = retrieveFlashMaps(request);
if (allFlashMaps != null) {
        //将SessionFlashMapManager.FLASH_MAPS从allFlashMaps中删除
allFlashMaps.removeAll(mapsToRemove);
        //此时allFlashMaps中SessionFlashMapManager.FLASH_MAPS为空,下面方法将其从session中删除
updateFlashMaps(allFlashMaps, request, response);
}
}
}
else {
allFlashMaps.removeAll(mapsToRemove);
    
updateFlashMaps(allFlashMaps, request, response);
}
} return match;
}
//具体实现在SessionFlashMapManager中

  protected abstract List<FlashMap> retrieveFlashMaps(HttpServletRequest paramHttpServletRequest);

  protected abstract void updateFlashMaps(List<FlashMap> paramList, HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse); 
}

实现类SessionFlashMapManager中重写retrieveFlashMaps和updateFlashMaps

public class SessionFlashMapManager extends AbstractFlashMapManager
{
private static final String FLASH_MAPS_SESSION_ATTRIBUTE = SessionFlashMapManager.class.getName() + ".FLASH_MAPS"; protected List<FlashMap> retrieveFlashMaps(HttpServletRequest request)
{
HttpSession session = request.getSession(false);
return ((session != null) ? (List)session.getAttribute(FLASH_MAPS_SESSION_ATTRIBUTE) : null);
} protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response)
{//flashMaps中SessionFlashMapManager.FLASH_MAPS为空,(!(flashMaps.isEmpty())) ? flashMaps : null)整个表达式返回null
  //即实现falshMaps中值为空,清空sesssion中该项的目的
WebUtils.setSessionAttribute(request, FLASH_MAPS_SESSION_ATTRIBUTE, (!(flashMaps.isEmpty())) ? flashMaps : null);
} protected Object getFlashMapsMutex(HttpServletRequest request)
{
return WebUtils.getSessionMutex(request.getSession());
}
}

spring-web包org.springframework.web.util.WebUtils中WebUtils.setSessionAttribute

public static void setSessionAttribute(HttpServletRequest request, String name, Object value)
{
Assert.notNull(request, "Request must not be null");
if (value != null) {
request.getSession().setAttribute(name, value);
}
else {//进入此步
HttpSession session = request.getSession(false);
if (session != null)
session.removeAttribute(name);//删除falshmap
}
}

接口类org.springframework.web.servlet.FlashMapManager

public abstract interface FlashMapManager
{
public abstract FlashMap retrieveAndUpdate(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse); public abstract void saveOutputFlashMap(FlashMap paramFlashMap, HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse);
}

springMVC源码学习之addFlashAttribute源码分析的更多相关文章

  1. 【 js 基础 】【 源码学习 】backbone 源码阅读(一)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  2. 【 js 基础 】【 源码学习 】backbone 源码阅读(二)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(source-code-study)进行参考交流,有详细的源码注释,以及知识总结,同时 ...

  3. 【 js 基础 】【 源码学习 】backbone 源码阅读(三)浅谈 REST 和 CRUD

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  4. go 源码学习之---Tail 源码分析

    已经有两个月没有写博客了,也有好几个月没有看go相关的内容了,由于工作原因最近在做java以及大数据相关的内容,导致最近工作较忙,博客停止了更新,正好想捡起之前go的东西,所以找了一个源码学习 这个也 ...

  5. 【 js 基础 】【 源码学习 】backbone 源码阅读(三)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  6. Jetty源码学习-编译Jetty源码二三事

    工作小几个月了,JDK基础和web应用框架学的的差不多了,开始学习Jetty源码,费了小半天才编译成功,把自己拆过的坑记录下来. 编译前的环境: MAVEN 3.3.Eclips eLuna Serv ...

  7. Java集合源码学习(三)LinkedList分析

    前面学习了ArrayList的源码,数组是顺序存储结构,存储区间是连续的,占用内存严重,故空间复杂度很大.但数组的二分查找时间复杂度小,为O(1),数组的特点是寻址容易,插入和删除困难.今天学习另外的 ...

  8. elasticsearch5.5.3 源码学习 idea下源码编译

    1.学习elasticsearch 源码,通过搜索“elasticsearch源码”,进行相关搜索.   2.因源码gradle编译,选择gradle-3.5可以编译通过,对应elasticsearc ...

  9. Java集合源码学习(四)HashMap分析

    ArrayList.LinkedList和HashMap的源码是一起看的,横向对比吧,感觉对这三种数据结构的理解加深了很多. >>数组.链表和哈希表结构 数据结构中有数组和链表来实现对数据 ...

随机推荐

  1. 打开word文档总是自动弹出控件工具条的解决办法:

    打开word文档总是自动弹出控件工具条的解决办法:1.查看是否word文档和模板中了'apmp宏病毒,按ALT+F11组合键,双击当前文档下属的ThisDocument,清空里面的内容:双击Norma ...

  2. Ubuntu 14.04不显示标题栏和侧边栏

    Ubuntu 14.04不显示标题栏和侧边栏 进入桌面后 Ctrl + Alt + F1 切换到 tty1,登录,然后运行: sudo apt-get install unity lightdm重启即 ...

  3. Espresso小试

    Espresso开源了,那就试着用一下, 1. 下载Espresso Espresso没有提供单独的jar包下载,建议clone整个项目或者下载zip包 git clone https://code. ...

  4. 2.C++语言特性

    一.普遍编程语言的特征 任何常用的编程语言都具备一组公共的语法特征,不同的语言仅在特征的细节上有所区别.所以,要想掌握一门语言,需要理解其语法特征的实现细节是第一步.  最基本的特征包括:       ...

  5. Dubbo(2)发布Dubbo服务

    主要参考Dubbo源码包里面的dubbo-demo源码: 1.项目结构: 2.pom.xml中的依赖: <project xmlns="http://maven.apache.org/ ...

  6. 服务容错保护断路器Hystrix之四:断路器监控(Hystrix Dashboard)-turbine集群监控

    turbine 英[ˈtɜ:baɪn] n. 汽轮机; 涡轮机; 透平机; OK,上文我们看了一个监控单体应用的例子,在实际应用中,我们要监控的应用往往是一个集群,这个时候我们就得采取Turbine集 ...

  7. 解决wordpress文章归档和分类目录小工具标题重复问题

    最近更新了wordpress,发现更新后小工具中的文章归档和分类目录出现了标题重复,经检查,是部分主题下,主题的代码已经输出了标题,而wordpress的代码又再次输出了一次.于是我们需要删除word ...

  8. Linux常用命令1-50(持续更新中)

    1:echo $PATH  (打印出PATH变量的值) 不同用户下面的PATH值有可能不一样 echo   有显示打印的意思 $         表示后面的是一个变量的意思 PATH  变量 /usr ...

  9. 00010 - cut选取命令详解

    定义 正如其名,cut的工作就是“剪”,具体的说就是在文件中负责剪切数据用的.cut是以每一行为一个处理对象的,这种机制和sed是一样的 剪切依据 cut命令主要是接受三个定位方法: 第一,字节(by ...

  10. Java调用R语言

    R是统计计算的强大工具,JAVA是做应用系统的主流语言.JAVA负责系统的构建,R用来做运算引擎,从而实现应用型和分析性相结合的系统. 一.Rserve(远程通信模式) Rserve是一个基于TCP/ ...