一、@ModelAttribute 注解

对方法标注 @ModelAttribute 注解,在调用各个目标方法前都会去调用 @ModelAttribute 标记的注解。本质上来说,允许我们在调用目标方法前操纵模型数据。

1.在 @ModelAttribute 标注的方法处向模型中存入数据

说明一下:在@ModelAttribute 标注的方法处,可以入参的类型和目标方法处允许的入参类型一致,如 @RequestParam 标注的请求参数等等。

有两种方式:

目标方法:

@RequestMapping("/updateStudent")
public String update(Student student) {
System.out.println("student: " + student);
return "success";
}

(1)通过向入参处添加 Model 类型或 Map 类型的参数(不推荐)

@ModelAttribute
public void getStudent(@RequestParam(value = "id", required = false) String idStr, Map<String, Object> map) {
try {
Integer id = Integer.parseInt(idStr);
System.out.println("student id: " + id);
map.put("student", new Student(1, "lisi", 23));
} catch(NumberFormatException ignored) {
}
}

在调用目标方法前,"student" 会被放入到 Model 中。至于说为什么不推荐此种用法,是因为,最终还会向 model 中添加一个 key 为 void,值为 null 的数据。如图:

(2)通过 @ModelAttribute 注解的 value 属性和 @ModelAttribute 标注的方法返回值(推荐)

@ModelAttribute("student")
public Student getStudent(@RequestParam(value = "id", required = false) String idStr, Map<String, Object> map) {
Student student = null;
try {
Integer id = Integer.parseInt(idStr);
System.out.println("student id: " + id);
student = new Student(1, "lisi", 23);
} catch(NumberFormatException ignored) {
}
return student;
}

在调用目标方法前,model 中的数据:

model 中只有一个键值对。这种写法更加优雅。

总结:SpringMVC 在调用目标方法前,将 @ModelAttribute 注解的 value 属性值作为 key , 返回值作为 value,存入到 model 中。

源码分析:

org.springframework.web.bind.annotation.support.HandlerMethodInvoker#invokeHandlerMethod

 public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception { Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
try {
boolean debug = logger.isDebugEnabled();
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
if (attrValue != null) {
implicitModel.addAttribute(attrName, attrValue);
}
}
//开始调用标注有 @ModelAttribute 注解的方法
for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
}
String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
continue;
}
ReflectionUtils.makeAccessible(attributeMethodToInvoke);
Object attrValue = attributeMethodToInvoke.invoke(handler, args);
if ("".equals(attrName)) {
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
}
if (!implicitModel.containsAttribute(attrName)) {
implicitModel.addAttribute(attrName, attrValue);
}
}
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
}
ReflectionUtils.makeAccessible(handlerMethodToInvoke);
//调用目标方法
return handlerMethodToInvoke.invoke(handler, args);
}
catch (IllegalStateException ex) {
// Internal assertion failed (e.g. invalid signature):
// throw exception with full handler method context...
throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
}
catch (InvocationTargetException ex) {
// User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
ReflectionUtils.rethrowException(ex.getTargetException());
return null;
}
}

行号14 处的 for 循环就是处理 @ModleAttribute 标注的方法的,在40行处调用目标方法——在调用目标方法前调用 @ModelAttribute 标注的方法。

在 16 行处已经对请求参数做了一次解析——在@ModelAttribute 标注的方法处,可以入参的类型和目标方法处允许的入参类型一致

20行、25行、31行——第二种方式,同时也明白如果 model 中包含相同的 key 时,是不会替换的。

2.在目标方法处读取模型中的数据

@ModelAttribute("student")
public Student getStudent() {
return new Student(1, "lisi", 23);
} @ModelAttribute("student2")
public Student getStudent2() {
return new Student(2, "wangwu", 33);
}

(1)在目标方法入参处不使用 @ModelAttribute 注解

@RequestMapping("/updateStudent")
public String update(Student student2) {
System.out.println("student: " + student2);
return "success";
}

控制台输出:

student: Student{id=23, studentName='lisi', age=23}

(2)在目标方法入参处使用 @ModelAttribute 注解

@RequestMapping("/updateStudent")
public String update(@ModelAttribute("student2") Student student2) {
System.out.println("student: " + student2);
return "success";
}

控制台输出:

student: Student{id=23, studentName='wangwu', age=33}

(3)源码分析

org.springframework.web.bind.annotation.support.HandlerMethodInvoker#resolveHandlerArguments

这个方法行数太多了,我们只看关注点:

289行:如果目标方法入参有标记 @ModelAttribute ,获取它 的 value 属性。

else if (ModelAttribute.class.isInstance(paramAnn)) {
ModelAttribute attr = (ModelAttribute) paramAnn;
attrName = attr.value();
annotationsFound++;
}

361行:

else if (attrName != null) {
WebDataBinder binder =
resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
if (binder.getTarget() != null) {
doBind(binder, webRequest, validate, validationHints, !assignBindingResult);
}
args[i] = binder.getTarget();
if (assignBindingResult) {
args[i + 1] = binder.getBindingResult();
i++;
}
implicitModel.putAll(binder.getBindingResult().getModel());
}

不论是对目标方法入参有没有标注 @ModelAttribute 注解,最终都会执行到这里。

看标红的地方:在这里进行解析的。

private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception { // Bind request parameter onto object...
String name = attrName;
if ("".equals(name)) {
name = Conventions.getVariableNameForParameter(methodParam);
}
Class<?> paramType = methodParam.getParameterType();
Object bindObject;
if (implicitModel.containsKey(name)) {
bindObject = implicitModel.get(name);
}
else if (this.methodResolver.isSessionAttribute(name, paramType)) {
bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
if (bindObject == null) {
raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
}
}
else {
bindObject = BeanUtils.instantiateClass(paramType);
}
WebDataBinder binder = createBinder(webRequest, bindObject, name);
initBinder(handler, name, binder, webRequest);
return binder;
}

注意:

String name = attrName;
if ("".equals(name)) {
  name = Conventions.getVariableNameForParameter(methodParam);
}

如果没有指定,则通过  Conventions.getVariableNameForParameter(methodParam) 获取一个默认值。

if (implicitModel.containsKey(name)) {
  bindObject = implicitModel.get(name);
}

从 model中获取,最后执行绑定。

(4)总结:使用在目标方法入参处的 @ModelAttribute 只能起到一个 指定 attrName 的作用,即从 Model 获取数据的 key。

<1>目标方法处的实体形参命名与 @ModelAttribute 方法标注的方法返回值之间没有任何关系,只是类型有关系。

<2>在目标方法入参处不使用 @ModelAttribute 注解的情况:

不需要通过 @ModelAttribute 注解来指定需要使用哪个 @ModelAttribute 标注的方法的 value 属性值。存在多个的话,使用默认值。

<3>在目标方法入参处需要使用 @ModelAttribute 注解的情况:

存在多个 @ModelAttribute 标注的方法,返回值为同一个类型A,且 @ModelAttribute 的 value 属性值不同,在目标方法处,需要以 A 实体作为入参,但是需要不使用默认的 a ,而是需要使用指定

的 a2。这个时候,就需要在目标方法的入参处使用 @ModelAttribute,通过 value 属性来指定使用哪个。

二、@SessionAttribute

1.官方说明

2.对 SessionAttribute 这里有篇帖子总结的非常好,我这里就不再赘述。

http://blog.sina.com.cn/s/blog_6d3c1ec601018cx1.html

3.我自己的理解:

@SessionAttribute 指的是 springmvc 的 session。向其中添加值得时候,同时会向 http session 中添加一条。在 sessionStatus.setComplete(); 的时候,会清空 sprinmvc

的 session,同时清除对应键的 http session 内容,但是通过,request.getSession.setAttribute() 方式添加的内容不会被清除掉。

其他情况下,springmvc session 和 http session使用情况相同。

SpringMVC基础——@ModelAttribute和@SessionAttribute的更多相关文章

  1. SpringMVC基础配置及使用

    SpringMVC基础配置及使用 SpringMVC:1.SpringMVC和Spring的关系:    软件开发的三层架构: web层[表示层.表现层]---->Service层----> ...

  2. springMVC基础controller类

    此文章是基于 搭建SpringMVC+Spring+Hibernate平台 功能:设置请求.响应对象:session.cookie操作:ajax访问返回json数据: 创建springMVC基础con ...

  3. SpringMVC基础入门

    一.SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC需要的jar包. 2.添加Web.xml配置文件中关于SpringMVC的配置 1 2 3 4 5 6 ...

  4. SpringMVC基础入门,创建一个HelloWorld程序

    ref:http://www.admin10000.com/document/6436.html 一.SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC需要 ...

  5. SpringMVC基础01——SpringMVC的知识结构体系

    1.前言 目前在各大互联网公司使用最热门的技术莫过于SpringBoot以及在其基础之上的SpringCloud,然而学习这些技术的基础在于学好Spring和SpringMVC,准确来说SpringM ...

  6. JavaEE SpringMVC 基础概念(如需详细资料请留言)

    SpringMVC Web开发核心思想: 模型与视图相分离: 控制逻辑与业务逻辑相分离: 视图选择与具体视图技术相分离: 非侵入式开发(尽量少使用框架特定API),易于测试. SpringMVC是什么 ...

  7. springMVC源码分析--@SessionAttribute用法及原理解析SessionAttributesHandler和SessionAttributeStore

    @SessionAttribute作用于处理器类上,用于在多个请求之间传递参数,类似于Session的Attribute,但不完全一样,一般来说@SessionAttribute设置的参数只用于暂时的 ...

  8. SpringMVC基础(一)_控制器

    Spring MVC Spring MVC 基于模型-视图-控制器(Model-View-Controller)模式实现,它能够帮你构建灵活和松耦合的应用程序. 1.Spring MVC的请求追踪 每 ...

  9. SpringMVC基础-controller方法中的参数注解

    @PathVariable  映射 URL 绑定的占位符 带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义 通过 ...

随机推荐

  1. Android 多线程-----AsyncTask详解

    您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...

  2. WPF 的datagrid 列名中没有显示下划线是怎么回事?

    引用:http://www.cnblogs.com/sqzhuyi/archive/2011/07/26/sqlserverclient.html#2831497 功能介绍:输入查询语句,显示列表 出 ...

  3. easyui datagrid去掉加载提示

    掉这个等待效果的方法:查了easyui的api,datagrid没有去掉这个遮罩层的方法或者属性,在网上找了半天也没人碰到相同的问题(可能比较easy就解决了吧).还好easyui是开源的,就研究它的 ...

  4. Ios开发之sqlite

    Sqlite是ios数据存储的一个重要手段,今天我们就一块来看一下,怎样使用sqlite将数据存储到沙盒中去. 第一步:导入一个框架libsqlite3.0.dylib 选中TARGETS在Gener ...

  5. 笔记 - 本地拦截genymotion或者Android模拟器的网络请求

    我们在主机上面运行了Burp或者fiddler,那么代理已经监听在本机的8080端口了. 那么我们需要在模拟器中进行如下设置: 1.在设置中,长按当前连接的wifi网络,弹出如下: 2. 点击修改网络 ...

  6. Android开发(三十二)——延时

    模拟延时 private class GetDataTask extends AsyncTask<Void, Void, String[]> { @Override protected S ...

  7. 打包时Xcode报:此证书的签发者无效Missing iOS Distribution signing identity

    问题描述 今天准备打包上传AppStore,结果Xcode报以下错误:Missing iOS Distribution signing identity for XXXXXX 查看证书后发现,Deve ...

  8. dede教程之后台登录是自动跳出解决方法

    有时也不知道什么原因,登录后台时输入全部正确点确认按钮时却会自动跳出.必须输入http://你的域名/dede/login.php才可以登录.通过尝试最终解决了问题,下面分享出来: 1.打开根目录da ...

  9. C#集合-列举(Enumeration)

    在计算机这个范畴内存在许多种类的集合,从简单的数据结构比如数组.链表,到复杂的数据结构比如红黑树,哈希表.尽管这些数据结构的内部实现和外部特征大相径庭,但是遍历集合的内容确是一个共同的需求..NET ...

  10. JS - IE中没有console定义

    由于IE中没有Console相关定义,所以不能使用它输出打印信息,且会出现脚本中断. 所以在IE中务必去掉(注释掉)console相关脚本代码.