本文主要从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. DS二叉树--二叉树之父子结点

    题目描述 给定一颗二叉树的逻辑结构如下图,(先序遍历的结果,空树用字符‘0’表示,例如AB0C00D00),建立该二叉树的二叉链式存储结构. 编写程序输出该树的所有叶子结点和它们的父亲结点 输入 第一 ...

  2. SET NOCOUNT ON

    每次我们在使用查询分析器调试SQL语句的时候,通常会看到一些信息,提醒我们当前有多少个行受到了影响,这是些什么信息?在我们调用的时候这些信息有用吗?是否可以关闭呢? 答案是这些信息在我们的客户端的应用 ...

  3. spring 事务的配置学习

    1.spring事务管理器接口PlatformTransactionManager 接口中的方法 获取事务状态信息 -TransactionStatus getTransaction(Transact ...

  4. 关于Tomcat配置虚拟路径保存、访问图片

    在项目中往往需要上传一些图片文件之类,一般不建议直接保存在数据库内,往往是讲图片等资源保存在服务器的某个文件夹下,传统做法是上传到部署目录下,通过相对路径进行访问. 这样当我们系统需要进行升级,进行全 ...

  5. springboot工程添加404页面

    首先在/src/main/resources下创建文件夹/public/error 在文件夹里创建html页面,jsp页面不可以. <html> <body> <img ...

  6. 廖雪峰Java3异常处理-2断言和日志-4使用Log4j

    1.Log4j Log4j是目前最流行的日志框架.有两个版本 1.x:Log4j 2.x:Log4j2 Log4j下载地址https://www.apache.org/dyn/closer.lua/l ...

  7. ubuntu下修改mysql默认data路径

    由于ubuntu默认的mysql路径是在/var/lib/mysql下,很多时候我们如果没有挂载其它分区在/var的时候,随着网站逐渐浏览和添加内容,数据容量也会越来越大,自然磁盘空间也会比较吃紧.因 ...

  8. 00011 - find中的-print0和xargs中-0的奥妙

    默认情况下, find 每输出一个文件名, 后面都会接着输出一个换行符 ('\n'), 因此我们看到的 find 的输出都是一行一行的: [bash-4.1.5] ; ls -l total 0 -r ...

  9. Mongodb集群搭建之 Sharding+ Replica Sets集群架构

    1.本例使用1台Linux主机,通过Docker 启动三个容器 IP地址如下: docker run -d -v `pwd`/data/master:/mongodb -p 27017:27017 d ...

  10. Office365开发者计划——账户信息上显示已是订阅用户,点击其他授权信息,仍然需要激活

    一.申请过程跳过. 二.安装之后,点击账号,查看授权状态,如题所述. 解决办法: 链接地址:https://answers.microsoft.com/zh-hans/msoffice/forum/a ...