在一个Controller内,被@ModelAttribute标注的方法会在此controller的每个handler方法执行前被执行。

  被@ModelAttribute标注的方法的参数绑定规则和普通handler方法相同。

  可以理解为:

  1. 请求到达Controller后,不论其他handler方法的RequestMapping值是多少,请求都会路由至被@ModelAttribute标注的方法;
  2. 由springMVC再对request执行一次forward,路由至真正的handler方法。

一 @ModelAttribute用于注解方法

1 方法返回类型为void

  这种情况,@ModelAttribute只是单纯的作为请求路由的第一站,使用者可在方法内部操作Model和Request等参数实现功能。

  对于如下请求:

http://localhost:8080/TestModelAttributeController/testHandler.action?reqParam=123

  对应的Controller:

@Controller
@RequestMapping("/TestModelAttributeController")
public class TestModelAttributeController { @ModelAttribute
public void modelAttributeMethod(HttpServletRequest request, String reqParam, Model model){
model.addAttribute("reqParam",reqParam);
request.setAttribute("methodParam","Hello ModelAttribute");
} @RequestMapping("/testHandler")
public String testHandler(){
return "testModelAttribute";
}
}

   testModelAttribute.jsp如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>${reqParam}</h1>
<h1>${methodParam}</h1>
</body>
</html>

  最终可以在页面中看到:

  123

  Hello ModelAttribute

2 方法返回类型不为void

这种情况,@ModelAttribute会将返回值放到Model中,并将该类型名称的首字母小写作为model中的属性名。

请求地址和参数不变。

对应的Controller:

    @ModelAttribute
public User userModelAttributeMethod2(){
User user = new User();
user.setAge(31);
user.setName("James");
user.setEmail("123456@qq.com");
return user; //相当于model.addAttribute("user",user);
} @RequestMapping("/testHandler")
public String testHandler(Model model){
System.out.println(model.containsAttribute("user")); //true
return "testModelAttribute";
}

对应的jsp页面

<h1>${user.age}</h1>
<h1>${user.email}</h1>
<h1>${user.name}</h1>

  实际上,对于返回类型为void的方法,@ModelAttribute也会在model中添加一对键值对,“void”->"null"

3 方法返回类型不为void,且@ModelAttribute指定属性名称

  这种情况下,@ModelAttribute会将返回值放到Medel中,且对应的key值为@ModelAttribute置顶的属性名

  对应的Controller:

 @ModelAttribute("myUser")
public User userModelAttributeMethod2(){
User user = new User();
user.setAge(31);
user.setName("James");
user.setEmail("123456@qq.com");
return user; //相当于model.addAttribute("user",user);
} @RequestMapping("/testHandler")
public String testHandler(Model model){
System.out.println(model.containsAttribute("user")); //true
return "testModelAttribute";
}

  对应的jsp页面:

<h1>${myUser.age}</h1>
<h1>${myUser.email}</h1>
<h1>${myUser.name}</h1>

4 @ModelAttribute和@RequestMapping注解同一个方法

  这种情况下:

  1. 在controller处理其他请求时,不会再首先进入被@ModelAttribute和@RequestMapping同时注解的方法;
  2. 该方法的返回值不再是视图的逻辑名称,而是按照@ModelAttribute的规则被加入到Model中;
  3. @RequestMapping注解的value值具有两个作用
    • 作为URI,实现请求的路由;
    • 作为此次请求的逻辑视图名(严格来说此时视图的逻辑视图名是:controller的RequestMapping值+method的RequestMapping值)
@Controller
@RequestMapping("/TestModelAttributeController")
public class TestModelAttributeController { @RequestMapping("/testModelAttribute")
@ModelAttribute("result")
public String testModelAttribute(){
return "excellent";
}
<body>
<h1>${result}</h1>
</body>

如上Controller和jsp:

testModelAttribute方法的作用是:

  1. 处理路径为 /TestModelAttributeController/testModelAttribute*的请求
  2. 将键值对"result"->"excellent"放至model中,为视图渲染提供数据
  3. 返回逻辑视图名 /TestModelAttributeController/testModelAttribute

二 @ModelAttribute注解方法入参

@ModelAttribute("attrName")用在方法入参上时,作用为:

  • 从当前的隐式model对象中取key值attrName所对应的attrValue值,并将attrValue赋给被注解的参数。
  • 而且自动暴露为模型数据用于视图页面展示时使用

1 @ModelAttribute指定注解的value值attrName

  如下所示,myUser和newParam两个model属性对应的attrValue值,将被赋值给方法入参。

    @ModelAttribute("myUser")
public User userModelAttributeMethod2(Model model){
User user = new User();
user.setAge(31);
user.setName("James");
user.setEmail("123456@qq.com"); model.addAttribute("newParam","new parameter");
return user;
} @RequestMapping("/testHandler")
public String testHandler(@ModelAttribute("myUser") User user,@ModelAttribute("newParam") String newParam){
System.out.println(user);
System.out.println(newParam);
return "testModelAttribute";

2 @ModelAttribute注解value值缺省

  这时默认的attrName为类型名称的首字母小写。

  如下例,user能够从model中获取,但是 newParam从model中获取的值为null

    @ModelAttribute("user") //此处必须是 user
public User userModelAttributeMethod2(Model model){
User user = new User();
user.setAge(31);
user.setName("James");
user.setEmail("123456@qq.com"); model.addAttribute("newParam","new parameter");
return user;
} @RequestMapping("/testHandler")
public String testHandler(@ModelAttribute User user,@ModelAttribute String newParam){
System.out.println(user);
System.out.println(newParam);
return "testModelAttribute";
}

3 入参不使用@ModelAttribute注解

  这种情况下,简单类型参数不会从model中取值,简单类型定义由 org.springframework.beans.BeanUtils#isSimpleValueType指定,如下:

    /**
* Check if the given type represents a "simple" value type:
* a primitive, an enum, a String or other CharSequence, a Number, a Date,
* a URI, a URL, a Locale or a Class.
* @param clazz the type to check
* @return whether the given type represents a "simple" value type
*/
public static boolean isSimpleValueType(Class<?> clazz) {
return (ClassUtils.isPrimitiveOrWrapper(clazz) ||
Enum.class.isAssignableFrom(clazz) ||
CharSequence.class.isAssignableFrom(clazz) ||
Number.class.isAssignableFrom(clazz) ||
Date.class.isAssignableFrom(clazz) ||
URI.class == clazz || URL.class == clazz ||
Locale.class == clazz || Class.class == clazz);
}

  非简单类型会从model中取值,这时默认的attrName为类型名称的首字母小写。

  如下例,user能够从model中获取,但是newParam是简单类型,所以不会从model中取值。

4 使用request参数和@ModelAttribute同时为同一个入参赋值

这种情况需要根据入参的类型区别对待

  1. 对于非简单类型,spring会先使用@ModelAttribute为参数赋值,然后使用request的参数对入参的属性值进行覆盖;
  2. 对于简单类型,spring会使用@ModelAttribute为参数赋值,忽略request参数;

如下例所示,URL为:

这时user的name和age属性最终会被设置为request对应的参数值(也就是实现了对象合并),而reqParam的值最终会采用@ModelAttribute得到的值。

    @ModelAttribute("user") //此处必须是 user
public User userModelAttributeMethod2(Model model){
User user = new User();
user.setAge(31);
user.setName("James");
user.setEmail("123456@qq.com"); model.addAttribute("reqParam","from @ModelAttribute");
return user;
} @RequestMapping("/testHandler")
public String testHandler( @ModelAttribute("user") User user, @ModelAttribute("reqParam") String reqParam){
System.out.println(user); //user 对象的属性会被request的参数覆盖
System.out.println(reqParam); //reqParam参数的值始终是@ModelAttribute中的值,不会被覆盖
return "testModelAttribute";
}

三 @ModelAttribute注解方法返回值

  此时@ModelAttribute的作用是将返回值添加至model,而这时的逻辑视图名称为:controller的RequestMapping值+method的RequestMapping值。

  其实,这种用法和 @ModelAttribute和@RequestMapping注解同一个方法相同。

Spring MVC @ModelAttribute注解的更多相关文章

  1. spring mvc 基于注解的使用总结

    本文转自http://blog.csdn.net/lufeng20/article/details/7598801 概述 继 Spring 2.0 对 Spring MVC 进行重大升级后,Sprin ...

  2. Spring MVC学习总结(2)——Spring MVC常用注解说明

        使用Spring MVC的注解及其用法和其它相关知识来实现控制器功能. 02     之前在使用Struts2实现MVC的注解时,是借助struts2-convention这个插件,如今我们使 ...

  3. 基于spring mvc的注解DEMO完整例子

    弃用了struts,用spring mvc框架做了几个项目,感觉都不错,而且使用了注解方式,可以省掉一大堆配置文件.本文主要介绍使用注解方式配置的spring mvc,之前写的spring3.0 mv ...

  4. java spring mvc 全注解

    本人苦逼学生一枚,马上就要毕业,面临找工作,实在是不想离开学校.在老师的教导下学习了spring mvc ,配置文件实在繁琐,因此网上百度学习了spring mvc 全注解方式完成spring的装配工 ...

  5. spring mvc 基于注解 配置默认 handlermapping

    spring mvc 是类似于 Struts 的框架.他们都有一个最主要的功能就是URL路由.URL路由能将请求与响应请求处理逻辑的类(在Struts中即是action,在spring mvc 中即是 ...

  6. 全面解析Spring中@ModelAttribute注解的用法

    本文不再更新,可能存在内容过时的情况,实时更新请移步我的新博客:全面解析Spring中@ModelAttribute注解的用法: @ModelAttribute注解用于将方法的参数或方法的返回值绑定到 ...

  7. [Spring MVC] - @ModelAttribute使用

    在Spring MVC里,@ModelAttribute通常使用在Controller方法的参数注解中,用于解释model entity,但同时,也可以放在方法注解里. 如果把@ModelAttrib ...

  8. Spring MVC Framework 注解

    ControllerAdvice Spring MVC Framework会把 @ControllerAdvice注解内部使用 @ExceptionHandler.@InitBinder.@Model ...

  9. Spring MVC 基本注解

    1. Spring MVC 常用到的注解: @Controller @RequestMapping @RequestParam @RequestHeader @ModelAttribute @Path ...

随机推荐

  1. P1044-栈-洛谷luogu

    题目背景 栈是计算机中经典的数据结构,简单的说,栈就是限制在一端进行插入删除操作的线性表. 栈有两种最重要的操作,即poppop(从栈顶弹出一个元素)和pushpush(将一个元素进栈). 栈的重要性 ...

  2. ubuntu 系统判断优盘的指定文件存在

    有很多的时候 会出现没有用的优盘路径 如果代码中写的是绝对路径 就有可能读不到优盘的内容 ,以下代码就是可以解决这样的问题 我已经封装成一个类 upanpath.h #ifndef UPANPATH_ ...

  3. Volume is already attached by pod default/nginx-deployment-86dfb99868-szpkd. Status Running

    1.部署WordPress - mysql ,想扩容,修改deployment,结果报错: MountVolume.SetUp failed for volume "pvc-e" ...

  4. 使用Win PE修改其他硬盘中的系统注册表

    使用场景:原来装的机械硬盘系统盘为C盘,后来买了个SSD固态硬盘后,进入WinPE系统后,把原来的C盘整个复制到了固态硬盘,然后用BooticeX64.exe工具在UEFI启动中增加SSD固态硬盘中的 ...

  5. libgdx学习记录27——线段与线段相交检测

    给定p1, p2, p3, p4四个点,p1,p2为一条线段,p3,p4为一条线段,检测其是否有交点. 可分为三种情况: 1. L2与x轴平行 2. L2与y轴平行 3. L2与坐标轴不平行. (L1 ...

  6. UWP简单示例(三):快速开发2D游戏引擎

    准备 IDE:Visual Studio 图形 API:Win2D MSDN 教程:UWP游戏开发 游戏开发涉及哪些技术? 游戏开发是一门复杂的艺术,编码方面你需要考虑图形.输入和网络 以及相对独立的 ...

  7. java垃圾回收诡异现象

    在知乎上看到一篇提问,于是做了个实验帮助他解答,这里整理成一篇文章分享一下. 先看代码如下代码: /** * Created on 2017/12/16. * * -verbose:gc -XX:+U ...

  8. Linux Namespace : PID

    PID namespace 用来隔离进程的 PID 空间,使得不同 PID namespace 里的进程 PID 可以重复且互不影响.PID namesapce 对容器类应用特别重要, 可以实现容器内 ...

  9. 在Ubuntu18.04下将应用程序添加到启动器

    # 在启动器里面给应用程序添加一个快捷方式 在linux(ubuntu)平台下,很多小伙伴发现,自己去官网下载解压的软件不能自动添加到启动器,每次启动的时候需要再次进入软件目录输入命令,非常不方便.本 ...

  10. 分布式监控系统Zabbix3.4-针对MongoDB性能监控操作笔记

    公司在IDC机房的一台服务器上部署了MongoDB,由于所存储的业务数据比较重要,所以对MongoDB的监控显得尤为重要!Zabbix监控MongoDB性能的原理:通过echo "db.se ...