Spring @ModelAttribute
正文开始之前,先介绍个东西,Spring能够自动将请求参数封装到对应JavaBean上!
代码比较简单,也没有什么配置要记录,只是开启了<mvc:annotation-driven/>,可以看到达到了这样的效果:
请求中属性name age 自动映射到 User对象上,返回视图时 属性又自动封装填充到 request属性域中. 填充的属性 键值key默认为类名首字母小写.

记录下,请求中参数是如何绑定到User对象上并且填充到request属性域中.
Spring抽象出来一个接口HandlerMethodArgumentResolver,这个接口主要实现两个功能:判断我们能否完成对这种参数类型的转换工作,以及我们如何去完成参数类型转换的工作? 等等,完成什么参数类型转换工作? 当然是完成@RequestMapping方法入参的参数转换工作啊!
那我们肯定需要一堆的HandlerMethodArgumentResolver这个接口实现类吧,肯定要有顺序吧,肯定用ArrayList来存储这个实现类。
Spring<mvc:annotation-driven/>肯定偷偷帮我们注册了一系列的HandlerMethodArgumentResolver实现类吧!没错,SpringMvc偷偷注册了十来个这样的HandlerMethodArgumentResolver,具体怎么工作的这里不叙述了,
其中专门用来解析自定义对象JavaBean的是ArrayList的最后一个HandlerMethodArgumentResolver :ServletModelAttributeMethodProcessor, 前面HandlerMethodArgumentResolver 都没用,才考虑到我,┭┮﹏┭┮
(其实也不是最后一个,因为注册了两个ServletModelAttributeMethodProcessor,只是这种情况我们用的是最后一个).
上面提及的两个工作,肯定是先判断我们能否完成对这种参数类型的转换工作:
我们现在讨论的不带 ModelAttribute注解,所以前面条件返回false , 后面条件 : this指代ServletModelAttributeMethodProcessor,annotationNotRequired为 true (这就是注册两个ServletModelAttributeMethodProcessor的原因,排在前面只 解析@ModelAttribute的参数,而排在后面的解析自定义 JavaBean对象的转换)
annotationNotRequired为true的情况下,后面条件判断的是 进一步限定 参数类型, 不是Date、数组、URI、URL、Number这种类型的,我才有必要进行 属性映射到对象上;

代码片段位于:org.springframework.web.method.annotation.ModelAttributeMethodProcessor#supportsParameter
这么看来 自定义对象完全满足使用 ServletModelAttributeMethodProcessor 来进行参数转换,下一步就是请求参数 转换到 JavaBean对象的事儿了.
逐行简单介绍下作用:Line100 检测方法入参parameter上是否有ModelAttribute注解,有就以@ModelAttribute(value=”xx”)的value作为name,没有就以parameter的class属性,类名转小写作为name 例如User--->user
Line101. 首先判断现在的ModelMap中是否有该name属性, 有就直接取出来作为attribute(ModelMap可以通过@ModelAttribute标注在普通方法上预先绑定一些属性);
没有就尝试创建,首先看是不是可以通过 URI中的PathVariable、request中直接getAttribute获取啊,都行不通的情况就直接构造这个class的实例,构造总要有构造器,采用默认的空参构造器,你没有空参构造器就会抛出异常!
这也告诉我们,参数类型需要有默认的空参构造器;
Line104 判断下这个属性是不是被列入黑名单,bindingDisabled, 加入黑名单方式目前暂知 通过@ModelAttribute(binding=false)设置,暂时不考虑这种情况;
Line111 WebDataBinder对象,这里也涉及了@InitBinder注解的解析,可以看之前的博客记录; target就是存的上面的attribute
Line112 attribute不为空,Line114行完成了属性的绑定,name、age映射到User上,映射方式按照名称映射,支持 . 进行级联等复杂映射,映射方式具体API可以看我下面的例子.
这里完成调用了ConversionService进行一定的转换,比如支持@DateTimeFormat等等.
Line116 @Valid注解解析过程,这里跳过; Line127 底层使用converter进行类型转换,我想不明白为啥还要再转换一次? 上面已经完成了请求参数绑定到JavaBean中.
完成请求方法参数--->JavaBean以后,ModelMap中的值最终都会存到ModelAndView视图的model属性, InternalResourceView的renderMergedOutputModel方法进行将 model属性一个个存入request的属性中;
跳转到对应的视图比如jsp一般都是request.getRequestDispatcher进行跳转; 假如@RequestMapping方法返回值不是前往某个视图JSP, 不会将属性存入request中。
另外注意:存储在request中的 key为 name,当前情况也就是 类名首字母小写。

代码片段位于:org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
展示下如何利用Spring Api完成这样的请求参数绑定生成具体的实例
public class SimpleTest {
public static void main(String[] args) {
//接受Pojo对象
BeanWrapperImpl bw = new BeanWrapperImpl(new Form7Pojo());
PropertyValues pvs=new MutablePropertyValues();
((MutablePropertyValues) pvs).add("name","HelloWorld BeanWrapper");
((MutablePropertyValues) pvs).add("age",24);
bw.setPropertyValues(pvs,false,false);
Object target = bw.getWrappedInstance();
System.out.println(target);
BeanWrapperImpl bw2 = new BeanWrapperImpl(new Address());
bw2.setAutoGrowNestedPaths(true);
PropertyValues pvs2=new MutablePropertyValues();
((MutablePropertyValues) pvs2).add("city.name","南京");
((MutablePropertyValues) pvs2).add("location","鼓楼区");
bw2.setPropertyValues(pvs2,false,false);
Object target2 = bw2.getWrappedInstance();
System.out.println(target2);
}
}
@Setter
@Getter
@ToString
public class Form7Pojo {
private String name;
private Integer age;
}
@Setter
@Getter
@ToString
public class Address {
private City city;
private String location;
}
@Setter
@ToString
public class City {
private String name;
}
记录@ModelAttribute用法:
用法一: 单单用在 @RequestMapping中的方法参数中, 作用将当前对象以 “user2”的名字存入request ,展示在视图界面,默认是 “user”的名字.

用法二. 单单用在 @Controller方法中,不加@RequestMapping注解 ,作用: 该@ModelAttribute方法会在每个@RequestMapping方法执行之前执行一次, 在每个@RequestMapping方法中获取的方式:注入modelMap属性,modelMap.get(“skill”)的方式获取属性。

效果等同于如下方法:

用法三: 这种方式比较奇葩,先直接说结论,跳转到默认的JSP页面,比如我的默认JSP页面就是 /WEB-INF/jsp/demo5.jsp,request中存储属性{professionalSkill=hello}.
这个时候返回值类型String已经不重要了,反正都要存到request的attribute属性域中,key就是 @ModelAttribute注解的 value , value就是@RequestMapping方法返回值;
至于为啥跳转到/WEB-INF/jsp/demo5.jsp呢, DispatcherServlet的applyDefaultViewName方法是计算默认展示界面

SpringMvc计算默认展示界面规则:视图解析器前缀 + 请求相对路径 + 视图解析器后缀 ,请求相对路径就是/demo5

验证:下面是一个不含视图名的ModelAndView返回给前端,结果如下(因为Controller上有@RequestMapping为/modelAttri ):

用法四. 比如demoa方法,入参a就是demoaaaaaaa,其实通过注入ModelMap,然后get(“strA”)方法一样可以达到效果; 但是JSP页面上仍然可以使用 ${strA} ${strB}访问到对应属性;

简单记录下:SpringMvc @ModelAttribute注解解析位置:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getModelFactory

总结:
感觉@ModelAttribute不是个很实用的注解,映射到JavaBean的时候吧,不需要它也能存入到request中,用它大概就是为了自定义存入request的key值吧.
在每个@RequestMapping方法之前做些处理吧,感觉有点像拦截器,但是每个@ModelAttribute都要执行一遍,有点多余哈,还不能指定只执行一个,有点搞不明白能用来做啥┭┮﹏┭┮.
Spring @ModelAttribute的更多相关文章
- spring @ModelAttribute 注解
@ModelAttribute // 表示请求该类的每个Action前都会首先执行它,也可以将一些准备数据的操作放置在该方法里面. public void setReqAndRes(HttpServl ...
- Spring ModelAttribute注解失效原因
public String test(@RequestParam(value = "test") @ModelAttribute("test") String ...
- Spring MVC 中使用 Swagger2 构建动态 RESTful API
当多终端(WEB/移动端)需要公用业务逻辑时,一般会构建 RESTful 风格的服务提供给多终端使用. 为了减少与对应终端开发团队频繁沟通成本,刚开始我们会创建一份 RESTful API 文档来记录 ...
- springMVC @ModelAttribute学习
springMVC @ModelAttribute学习 博客分类: Spring @ModelAttribute 绑定请求参数到命令对象 @ModelAttribute一个具有如下三个作用: ①绑 ...
- spring中JavaConfig相关的注解
在spring3.0中增加配置spring beans的新方式JavaConfig,可以替换spring的applicataion.xml配置.也即@Configuration对等<beans/ ...
- Spring MVC 对于@ModelAttribute 、@SessionAttributes 的详细处理流程
初学 Spring MVC , 感觉对于 @ModelAttribute 和 @SessionAttributes 是如何被Spring MVC处理的,这一流程不是很清楚, 经过Google资料,有了 ...
- [Spring MVC] - @ModelAttribute使用
在Spring MVC里,@ModelAttribute通常使用在Controller方法的参数注解中,用于解释model entity,但同时,也可以放在方法注解里. 如果把@ModelAttrib ...
- spring3 jsp页面使用<form:form modelAttribute="xxxx" action="xxxx">报错,附连接数据库的spring MVC annotation 案例
在写一个使用spring3 的form标签的例子时,一直报错,错误信息为:java.lang.IllegalStateException: Neither BindingResult nor plai ...
- spring mvc:@RequestParam与@ModelAttribute异同
关于spring mvc中的两个注解:@RequestParam.@ModelAttribute区别,原先并没有特别注意,直到最近找别人开发的一个小模块的bug时,才有意识的比较了两者的区别. 1.@ ...
随机推荐
- Miller_raibin算法随机化检测素数 & Pollar_rho 算法分解大数
这几天一直再学习这些内容,也没有发一些博客,现在我觉得差不多了 首先基础是Miller_raibin随机化检测素数,顾名思义,随机化也就是有几率不对,但是很低,适用于大数快速检测,因为大数已经超出了我 ...
- 用Html创建简历
用最基本的html实现简历的创建 代码 <!doctype html> <html> <head><meta charset="utf-8" ...
- cxGrid 怎样才能让不自动换行 WordWrap:=false
cxGrid 怎样才能让不自动换行 WordWrap:=false 2014-12-26 02:04:03| 分类: delphi|举报|字号 订阅 下载LOFTER我的照片书 | ...
- Spring Junit集成测试
例子如下: package com.junge.demo.spring; import static org.junit.Assert.assertEquals; import java.util.L ...
- C#把汉字转换成16进制(HEX)并向串口发送数据
报警器实例:(有发送,无返回获取) using System; using System.Collections.Generic; using System.Linq; using System.Te ...
- Unity 环境区域网格化
在使用A星算法和物体布局的过程中,常常会使用的网格的概念,即建立在网格的基础上,会使得游戏的相关编程变得简单的多. 格子的代码: using System.Collections; using Sys ...
- Android开发教程 - 使用Data Binding(二)集成与配置
本系列目录 使用Data Binding(一)介绍 使用Data Binding(二)集成与配置 使用Data Binding(三)在Activity中的使用 使用Data Binding(四)在Fr ...
- 【文文殿下】[APIO2010]特别行动队 题解
基本上是一个斜率优化裸题了 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int max ...
- [翻译]Elasticsearch重要文章之四:监控每个节点(jvm部分)
http://zhaoyanblog.com/archives/753.html 操作系统和进程部分 操作系统和进程部分的含义是很清楚的,这里不会描述的很详细.他们列出了基本的资源统计,例如CPU和负 ...
- Mac 下查看端口是否被占用
1. lsof -i :8080 2. netstat -anp tcp | grep 8080 3. nc -w 10 -n -z 127.0.0.1 8070-8090