1. Spring2.5中引入注解对处理器(headler)支持
@Controller:用于标识是处理器类;@RequestMapping:请求到处理器功能方法的映射规则;
@RequestParam:请求参数到处理器功能处理方法的方法参数上的绑定;
@ModelAttribute:请求参数到命令对象的绑定; @InitBinder:自定义数据绑定注册支持,用于将请求参数转换到命令对象属性的对应类型; @SessionAttributes:用于声明session 级别存储的属性,放置在处理器类上,通常列出模型属性(如@ModelAttribute)对应的名 称,则这些属性会透明的保存到session 中
2. Spring3引入了更多的注解,其中包含了对RESTful架构风格的支持
@CookieValue: cookie数据到处理器功能处理方法的方法参数上的绑定;@RequestHeader: 请求头数据到处理器功能处理方法的方法参数上的绑定;
@RequestBody: 请求的body体的绑定
@ResponseBody: 处理器功能处理方法的返回值作为响应体
@ResponseStatus: 定义处理器功能处理方法/异常处理器返回的状态码和原因;
@ExceptionHandler: 注解式声明异常处理器;
@PathVariable: 请求URI 中的模板变量部分到处理器功能处理方法的方法参数上的绑定,从而支持RESTful架构风格的URI;
3. Spring3中引入的mvc命名空间
mvc这个命名空间是在Spring3中引入的,其作用是用来支持mvc的配置。需要在<bean>中声明出这个命名空间及其对应的schemaLocation中的值。<mvc:annotation-driven>自动注册基于注解风格的处理器和适配器:
在spring2.5中是 DefaultAnnotationHandlerMapping和 AnnotationMethodHandlerAdapter
在spring3中是 RequestMappingHandlerMapping和 RequestMappingHandlerAdapter.
同时还支持各种数据的转换器.
< mvc:interceptors>标签,配置自定义的处理器拦截器,例如:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<ref bean="handlerInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
<mvc:view-controller>标签,可以配置在收到相应请求后直接选择相应的视图,不需要写控制器的代码了,例如:
<mvc:view-controller path="/hello" view-name="test">
</mvc:view-controller>
<mvc:resources>标签,逻辑静态资源路径到物理静态资源路径的对应.例如:
<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:default-servlet-handler>
当在web.xml中DispatcherServlet使用<url-pattern>/</url-pattern> 映射的时候,会静态资源也映射了,如果配置了这个mvc标签,那么再访问静态资源的时候就转交给默认的Servlet来响应静态文件,否则报404 找不到静态资源错误。
4. @Controller和@RequestMapping注解
4.1 声明处理器 @Controller
public class HelloWorldController {
//代码。。。。
}
4.2
映射处理器中的【功能处理方法】
@Controller
public class HelloWorldController {
@RequestMapping("/home")
public ModelAndView home(){
ModelAndView mv = new ModelAndView("index");
return mv;
}
} // 表明该方法映射的url路径为/home
4.3 @RequestMapping也可以写在处理器类上
@RequestMapping("/test")
@Controller
public class HomeController {
@RequestMapping("/home")
public ModelAndView home(){
ModelAndView mv = new ModelAndView("index");
return mv;
}
} //表明该方法映射的url路径为/test/home
注意:功能处理方法的方法可以是String类型,表示逻辑视图的名字,可以不用返回ModelAndView对象, 例如:
@Controller
public class HelloWorldController {
@RequestMapping("/home")
public String home(){
return "index";
}
}
5. 请求映射
假设浏览器发送了一个请求如下:-------------------------------
POST /login HTTP1.1 (请求方式 URL 协议版本号)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 (请求头信息)
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,en;q=0.8,zh;q=0.5,en-US;q=0.3
Connection: keep-alive
Cookie: JSESSIONID=DBC6367DEB1C024A836F3EA35FCFD5A2
Host: 127.0.0.1:8989
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
(回车换行。。。)
username=tom&password=123 (请求正文)
--------------------------------
http协议的请求格式如下:
---------------------------------
请求方法 URL 协议版本号
请求头信息
请求头信息
请求头信息
..
回车换行
请求正文
---------------------------------
从格式中我们可以看到【请求方法、URL、请求头信息、请求正文】这四部分一般是可变的,因此我们可以把请求中的这些信息在处理器的【功能处理方法】中进行的映射,因此请求的映射分为如下几种:
URL路径映射 :使用URL映射到处理器的功能处理方法;
请求方法映射限定: 例如限定功能处理方法只处理GET请求;
请求参数映射限定: 例如限定只处理包含username参数的请求;
请求头映射限定: 例如限定只处理"Accept=application/json"的请求。
6. URL路径映射
6.1 普通URL路径映射@RequestMapping(value="/test")
@RequestMapping("/hello") 注解中只出现一个参数且参数名为value的话,可以将参数名去掉
@RequestMapping(value={"/test", "/user/hello"}) 多个URL路径可以映射到同一个处理器的功能处理方法。
2)URI模板模式映射
@RequestMapping(value="/users/{userId}")
{XXX}占位符, 请求的URL可以是"/users/123456"或"/users/abcd",之后可以通过@PathVariable可以提取URI模板模式中的{XXX}中的值
@RequestMapping(value="/users/{userId}/create")
这样也是可以的,请求的URL可以是"/users/123/create"
@RequestMapping(value="/users/{userId}/topics/{topicId}")
这样也是可以的,请求的URL可以是"/users/123/topics/123"
3)Ant风格的URL路径映射
@RequestMapping(value="/users/**")
可以匹配"/users/abc/abc",但"/users/123"将会被【URI模板模式映射中的"/users/{userId}"模式优先映射到】
@RequestMapping(value="/product/?")
可匹配"/product/1"或"/product/a",但不匹配"/product"或"/product/aa";
?代表有且只有一个字符
@RequestMapping(value="/product*")
可匹配"/productabc"或"/product",但不匹配"/productabc/abc";
*代表0~n个字符
@RequestMapping(value="/product/*")
可匹配"/product/abc",但不匹配"/productabc";
@RequestMapping(value="/products/**/{productId}")
可匹配"/products/abc/abc/123"或"/products/123",也就是Ant风格和URI模板变量风格可混用;
**代表所有的子路径
4)正则表达式风格的URL路径映射
从Spring3.0 开始支持正则表达式风格的URL路径映射,格式为{变量名:正则表达式},之后通过@PathVariable可以提取{XXX:正则表达式匹配的值}中的XXX这个变量的值。
@RequestMapping(value="/products/{categoryCode:\\d+}-{pageNumber:\\d+}")
可以匹配"/products/123-1",但不能匹配"/products/abc-1",这样可以设计更加严格的规则。
@RequestMapping(value="/user/{Id:^\\d{4}-[a-z]{2}$}")
可以匹配"/user/1234-ab"
注意:\d表示数字,但是\在java的字符串中是特殊字符,所以需要再加一个\进行转义即可
(参照之前js的学习文档,和java的正则几乎一致,js正则中的一个/变为java中的俩个/即可)
括号:
[abc] 查找方括号之间的任何字符。
[^abc] 查找任何不在方括号之间的字符。
[0-9] 查找任何从 0 至 9 的数字。
[a-z] 查找任何从小写 a 到小写 z 的字符。
[A-Z] 查找任何从大写 A 到大写 Z 的字符。
[A-z] 查找任何从大写 A 到小写 z 的字符。
(red|blue|green) 查找任何指定的选项。
元字符:
. 查找单个任意字符,除了换行和行结束符.如果要表示.这个字符,需要转义
\w 查找单词字符。 字母 数字 _
\W 查找非单词字符。非 字母 数字 _
\d 查找数字。
\D 查找非数字字符。
\s 查找空白字符。
\S 查找非空白字符。
\b 匹配单词边界。
\B 匹配非单词边界。
\0 查找 NUL 字符。
\n 查找换行符。
\f 查找换页符。
\r 查找回车符。
\t 查找制表符。
\v 查找垂直制表符。
量词:
n+ 匹配任何包含至少一个 n 的字符串。
n* 匹配任何包含零个或多个 n 的字符串。
n? 匹配任何包含零个或一个 n 的字符串。
n{X} 匹配包含 X 个 n 的序列的字符串。
n{X,Y} 匹配包含 X 到 Y 个 n 的序列的字符串。
n{X,} 匹配包含至少 X 个 n 的序列的字符串。
n$ 匹配任何结尾为 n 的字符串。
^n 匹配任何开头为 n 的字符串。
?=n 匹配任何其后紧接指定字符串 n 的字符串。
?!n 匹配任何其后没有紧接指定字符串 n 的字符串。
正则表达式风格的URL路径映射是一种特殊的URI模板模式映射
URI模板模式映射不能指定模板变量的数据类型,如是数字还是字符串;
正则表达式风格的URL路径映射,可以指定模板变量的数据类型,可以将规则写的相当复杂。
7、请求方法映射限定
一般获取数据为GET请求方法,提交表单一般为POST请求方法。但之前URL路径映射方式对任意请求方法都是接受的,因此我们需要某种方式来告诉相应的功能处理方法只处理如GET方式的请求或POST方式的请求。
@RequestMapping(value="/user/{userId:\\d+}",method=RequestMethod.GET)
可以匹配"/user/100",并且请求方式只能是GET
@RequestMapping(value="/hello", method={RequestMethod.POST,RequestMethod.GET})
可以匹配"/hello",并且请求方式只能是POST或者GET
注意:
1、一般浏览器只支持GET、POST请求方法,如想浏览器支持PUT、DELETE 等请求方法只能模拟。(jquery中的ajax函数可以发送这些方式的请求)
2、除了GET、POST,还有HEAD、OPTIONS、PUT、DELETE、TRACE(观察servlet源码也可获知)
3、DispatcherServlet默认开启对GET、POST、PUT、DELETE、HEAD 的支持;
4、如果需要支持OPTIONS、TRACE,请添加DispatcherServlet 在web.xml 的初始化参数:dispatchOptionsRequest 和
dispatchTraceRequest 为true。(查看源码,在DispatcherServlet的父类中可以找到这个俩个属性)
8.请求参数映射限定
1)请求数据中有指定参数名
@RequestMapping("/test")
@Controller
public class HomeController {
@RequestMapping(value="param1",params="create",method=RequestMethod.GET)
public ModelAndView test1(){
return null;
}
@RequestMapping(value="param2",params="create",method=RequestMethod.POST)
public ModelAndView test2(){
return null;
}
}
可以匹配的路径为:/test?create
如果是get 方式的请求则访问test1方法
如果是post方式的请求则访问test2方法
2)请求数据中没有指定参数名
@RequestMapping(params="!create", method=RequestMethod.GET)
3)请求数据中指定参数名=值
@RequestMapping(params="username=tom")
4)请求数据中指定参数名!=值
@RequestMapping(params="username!=tom")
5)组合使用是"且"的关系
@RequestMapping(params={"create","username=tom"})
9.请求头数据映射限定
1)请求头数据中有指定参数名
@RequestMapping(value="/header/test1", headers="Accept")
表示请求的URL必须为"/header/test1"且请求头中必须有Accept参数才能匹配。
@RequestMapping(value="/header/test1", headers="my_test")
表示请求的URL必须为"/header/test1"且请求头中必须有my_test参数才能匹配
2)请求头数据中没有指定参数名
@RequestMapping(value="/header/test2", headers="!abc")
表示请求的URL必须为"/header/test2"且请求头中必须没有abc参数才能匹配
3)请求头数据中指定参数名=值
@RequestMapping(value="/header/test3", headers="Content-Type=application/json")
表示请求的URL必须为"/header/test3"且请求头中必须有"Content-Type=application/json"参数即可匹配。
4)请求头数据中指定参数名!=值
@RequestMapping(value="/header/test4", headers="Accept!=text/plain")
表示请求的URL必须为"/header/test4"且请求头中必须有Accept参数,但是值不等于text/plain即可
5)组合使用是"且"的关系
@RequestMapping(value="/header/test5", headers={"Accept!=text/plain", "abc=123"}):
表示请求的URL必须为"/header/test5"且请求头中必须有"Accept"参数但值不等于"text/plain"且请求中必须有参数"abc=123"即可匹配。
6)consumes属性和produces属性
consumes 指定处理请求的提交内容类型
@RequestMapping(value="/test",consumes="application/json")
方法仅处理请求中,Content-Type为"application/json"类型的请求
produces 指定返回的内容类型
@RequestMapping(value= "/test", produces="application/json")
表示将功能处理方法将产生json格式的数据,此时根据请求头中的Accept进行匹配,如请求头"Accept:application/json"时即可匹配;
例如:
@Controller
public class HomeController {
@RequestMapping(value="/test",consumes="application/json")
public ModelAndView test1(){
System.out.println("test1....");
return null;
}
@RequestMapping(value="/test",produces="application/json")
public ModelAndView test2(){
System.out.println("test2....");
return null;
}
}
测试类中的main函数代码:
此处我们使用Spring提供的Http客户端API创建了请求并设置了请求的Content-Type和编码并在响应体中写回了json数据
public static void main(String[] args) {
try {
String url = "http://127.0.0.1:8888/SpringMVC/test";
//创建HttpRequest
ClientHttpRequest request =
new SimpleClientHttpRequestFactory().
createRequest(new URI(url), HttpMethod.POST);
//设置请求头的内容类型头和内容编码
//request.getHeaders().set("Content-Type", "application/json;charset=UTF-8");
request.getHeaders().set("Accept", "application/json;charset=UTF-8");
//写出请求内容体
String jsonData = "{\"username\":\"zhang\", \"password\":\"123\"}";
request.getBody().write(jsonData.getBytes("UTF-8"));
//发送请求并得到响应
ClientHttpResponse response = request.execute();
System.out.println(response.getStatusCode());
System.out.println(response.getHeaders());
} catch (Exception e) {
e.printStackTrace();
}
}
注意:在上面代码中,如果想获得响应正文中的数据,可以编写一下代码:
//得到响应体的编码方式
Charset charset = response.getHeaders().getContentType().getCharSet();
//得到响应体的内容
InputStream is = response.getBody();
byte bytes[] = new byte[(int)response.getHeaders().getContentLength()];
is.read(bytes);
String data = new String(bytes, charset);
System.out.println("charset : " + charset + ", data : " + data);
10.数据绑定
1)功能处理方法支持的参数类型
注意下面这些参数都可以在功能处理方法中直接声明并且没有指定顺序,spring会自动注入的
第一种
ServletRequest/HttpServletRequest 和 ServletResponse/HttpServletResponse
SpringWebMVC框架会自动帮助我们把相应的Servlet请求/响应作为参数传递过来。
第二种
InputStream/OutputStream 和 Reader/Writer
分别对应的是request.getInputStream();
response.getOutputStream();
request.getReader();
response.getWriter()。
InputStream/OutputStream 和 Reader/Writer两组不能同时使用,只能使用其中的一组。
第三种
WebRequest/NativeWebRequest
WebRequest是SpringMVC提供的统一请求访问接口,不仅仅可以访问请求相关数据(如参数区数据、请求头数据,但访问不到Cookie区数据),还可以访问会话和上下文中的数据;NativeWebRequest继承了WebRequest,并提供访问本地ServletAPI的方法。
例如:
public String webRequest(WebRequest webRequest, NativeWebRequest nativeWebRequest) {
System.out.println(webRequest.getParameter("test"));
webRequest.setAttribute("name", "value",WebRequest.SCOPE_REQUEST);
System.out.println(webRequest.getAttribute("name", WebRequest.SCOPE_REQUEST));
HttpServletRequest request =
nativeWebRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response =
nativeWebRequest.getNativeResponse(HttpServletResponse.class);
return "success";
}
webRequest.getParameter() 访问请求参数区的数据webRequest.getHeader() 访问请求头数据
webRequest.setAttribute/getAttribute:到指定的作用范围内取/放属性数据,Servlet定义的三个作用范围分别使用如下常量代表:
SCOPE_REQUEST :代表请求作用范围;
SCOPE_SESSION :代表会话作用范围;
SCOPE_GLOBAL_SESSION :代表全局会话作用范围,即ServletContext上下文作用范围。
得到本地的Servlet API。
nativeWebRequest.getNativeRequest
nativeWebRequest.getNativeResponse
第四种
HttpSession
public String session(HttpSession session) {
System.out.println(session);
//可以直接在view中获取并取到
session.setAttribute("name", "zhangsan");
return "success";
}
注意:session访问不是线程安全的,如果需要线程安全,需要设置AnnotationMethodHandlerAdapter或RequestMappingHandlerAdapter的synchronizeOnSession属性为true,即可线程安全的访问session。
在spring2.5中是AnnotationMethodHandlerAdapter
在spring3中是RequestMappingHandlerAdapter.
第五种
命令/表单对象
SpringMVC能够自动将请求参数绑定到功能处理方法的命令/表单对象上。
这里说的命令/表单对象并不需要实现任何接口,仅是一个拥有若干属性的POJO类对象
例如:
@RequestMapping(value = "/create"
public String toCreateUser(User user) {
return null;
}
如果提交的表单(包含username和password文本域),将自动将请求参数绑定到命令对象user中去。
第六种
Model、Map、ModelMap
SpringMVC提供Model、Map或ModelMap让我们能去封装/处理模型数据。
例如:
@RequestMapping(value="/model")
public String test(Model m1, Map m2, ModelMap m3) {
m1.addAttribute("a", "a");
m2.put("b", "b");
m3.put("c", "c");
System.out.println(m1 == m2);
System.out.println(m2 == m3);
return "success";
}
虽然此处注入的是三个不同的类型(Model model, Map model2, ModelMap model3),但三者是同一个对象
第七种
HttpEntity<T>和ResponseEntity<T>
例如:HttpEntity的使用
@RequestMapping(value="test")
public String test(HttpEntity<String> httpEntity){
//获得请求中的所有的header部分
HttpHeaders headers = httpEntity.getHeaders();
//获得请求中的所有的body部分
String body = httpEntity.getBody();
}
例如:ResponseEntity的使用,可以自定义响应的各个部分
@RequestMapping("/test")
public ResponseEntity<String> test(){
//创建响应头对象
HttpHeaders headers = new HttpHeaders();
//创建MediaType对象
MediaType mt = new MediaType("text","html",Charset.forName("UTF-8"));
//设置ContentType
headers.setContentType(mt);
//准备好相应体
String content = new String("hello world");
//根据响应内容/响应头信息/响应状态码创建响应对象
ResponseEntity<String> re = new ResponseEntity<String>(content,headers,HttpStatus.OK);
//返回ResponseEntity对象
return re;
}
第八种
除了以上几种类型参数之外,还支持一些其他的类型,如:BindingResult、Errors、Locale、Principal、UriComponentsBuilder、SessionStatus、RedirectAttributes等
11)@RequestParam绑定单个请求参数值
用于将请求参数区数据映射到功能处理方法的参数上
public String test(@RequestParam String username)
请求中包含username 参数(如/test?username=tom),则自动传入。
如果url为/test?name=tom,即俩边参数名不一致,那么就需要手动指定
public String test(@RequestParam("name") String username)
@RequestParam注解中主要有哪些参数
value:参数名字,即入参的请求参数名字
required:是否必须,默认是true
defaultValue:默认值,表示如果请求中对应参数时则采用此默认值,默认值可以是spring中的SpEL 表达式,如
"#{systemProperties['java.vm.version']}"。
注意:systemProperties是spring自动方法ioc容器中的一个Properties对象,里面方法中很多系统变量,要取值只需#{systemProperties['key']}即可
@RequestMapping(value="/home2")
public String test(@RequestParam(defaultValue="#{systemProperties}") Object p){
System.out.println("p = "+p);
System.out.println("p = "+p.getClass());
System.out.println(((Properties)p).get("user.home"));
return "index";
}
SpEL表达式的使用,例如:
取名字为stu的bean的name字段的值,这里指的是property
public String test(@RequestParam(defaultValue="#{stu.name}") String username)
对其他bean中某个方法的引用
public String test(@RequestParam(defaultValue="#{stu.sayHello()}") String username)
public String test(@RequestParam(defaultValue="#{stu.sayHello('tom')}") String username)
表达式(?.)可以确保在sayHello()返回不为空的情况下调用toUpperCase()方法,如果返回空那么不继续调用后面的方法
public String test(@RequestParam(defaultValue="#{stu.sayHello()?.toUpperCase()}") String username)
如果要调用的某个类是外部类,而不是spring中定义的bean,使用表达式T()
public String test(@RequestParam(defaultValue="#{T(java.lang.Math).random()}") String username)
12)@PathVariable绑定URI模板变量值
用于将请求URL中的模板变量映射到功能处理方法的参数上。
@RequestMapping(value="/users/{userId}/topics/{topicId}")
public String test(@PathVariable int userId,
@PathVariable int topicId){
return "index";
}
如请求的 URL为"/users/123/topics/456",则自动将URL 中模板变量{userId}和{topicId}绑定到通过@PathVariable注解的同名参数上,即入参后userId=123、topicId=456。
如果参数不同名,则需要自己手动指定
public String test(@PathVariable("uid") int userId,
@PathVariable("tid") int topicId){
return "index";
}
13)@CookieValue绑定Cookie数据值
用于将请求的Cookie数据映射到功能处理方法的参数上。
public String test(@CookieValue(value="JSESSIONID", defaultValue="") String sessionId)
如上配置将自动将JSESSIONID 值入参到sessionId 参数上,defaultValue 表示Cookie中没有JSESSIONID时默认为空。
传入参数类型也可以是javax.servlet.http.Cookie类型。
public String test(@CookieValue(value="JSESSIONID", defaultValue="") Cookie cookie)
14)@RequestHeader 绑定请求头数据
用于将请求的头信息区数据映射到功能处理方法的参数上。
@RequestMapping(value="/header")
public String test(@RequestHeader("User-Agent") String userAgent,
@RequestHeader(value="Accept") String[] accepts)
如上配置将自动将请求头"User-Agent"的值,入参到userAgent参数上,并将"Accept"请求头值入参到accepts参数上。
15)@ModelAttribute 绑定请求参数到命令对象
该注解具有如下三个作用:
1.绑定请求参数到命令对象
例如在用户登录时,我们需要捕获用户登录的请求参数(用户名、密码)并封装为用户对象,此时我们可以使用@ModelAttribute绑定多个请求参数到我们的命令对象。
public String test(@ModelAttribute("my_user") User u)
和上面接收到的"第五种"情况一样,只是此处多了一个注解@ModelAttribute("my_user"),它的作用是将该绑定的命令对象以"my_user"为名称添加到模型对象中供视图页面展示使用。我们此时可以在视图页面使用${my_user.username}来获取绑定的命令对象的属性。
如果不写@ModelAttribute("my_user")这个注解,那么默认添加到模型中的名字是该类型的类名首字母小写,例如这里便是user,页面中取值就需要这样${user.username}
2.在功能处理方法执行前,向模型中添加数据
@ModelAttribute("cityList")
public List<String> cityList(String username) {
return Arrays.asList("北京", "山东");
}
如果当前模型中没有名字为cityList的数据时,如上代码会在执行处理器中任意功能处理方法之前执行,并将返回值自动添加到模型对象中,在功能处理方法中调用Model对象中的方法containsAttribute("cityList")将会返回true。
注意:模型中数据的作用范围是request级别的
注意:此方法中依然是可以接收本次请求的参数的,默认和客户端所传参数名字保持一致,也可以使用@RequestParam指定参数名
注意:如何有俩个同名的命令对象,如下
@ModelAttribute("user")
public User getUser(String username) {
User user = new User();
user.setUsername("briup");
return user;
}
@RequestMapping(value="/model")
public String test1(@ModelAttribute("user") User user, Model model){
//输出结果为briup
System.out.println(user.getUsername());
//返回值是true
System.out.println(user == model.asMap().get("user"));
return "index";
}
说明springMVC对于模型中重名的对象,不会重复创建,默认模型中已经有了这个名字的对象,那么就直接拿出来使用
3.把功能处理方法的返回值添加到模型数据中
@RequestMapping(value="/index")
public @ModelAttribute("u") User test3(){
User user = new User();
user.setUsername("tom");
user.setPassword("123");
return user;
}
注意:这时候SpringMVC会根据RequestToViewNameTranslator进行逻辑视图名的翻译,这个例子中也就会把"index"作为逻辑视图名进行解析
注意:对于集合类型(Collection接口的实现者们,包括数组),生成的模型对象属性名为"简单类名(首字母小写)"+"List",如List<String>生成的模型对象属性名"stringList",List<User>生成的模型对象属性名为"userList"。
例如:
public @ModelAttribute List<String> test()
public @ModelAttribute List<User> test()