详见:《Spring MVC+ MyBatis快速开发与项目实战》第六章
@Controller
上述代码定义了一个AyUserController 控制层,使用@Controller 注解进行表示,使用 @GetMapping 注解来映射一个请求, value=“/hello "。为了保证Spring 能够找到控制层,需要 额外进行配置: 在appIi cationContext.xm I 配置文件中配置<context:component-scan />元素,如果 己经配置可以略过,具体代码如下:
<context:component-scan base-package="com.ay.controller"/>
<context: component-san />元素的功能是,启动包扫描功能,使加有@Controller 、 @Service 、@Respository 、@Component 等注解的类能够成为Spring 的bean 。base-package 属性 指定了需要扫描的类包, 类包及其包含的子包中的所有类都会被处理。
此外, 还需要在web.xml 文件中配置Spring MVC 的前端控制器DispatcherServlet , 以及在 spring-mvc.xm l 配置文件中配置InternalResourceViewResolver 视图解析器。
<!--servlet-->
<!--DispatcherServlet是前直控制器,主要用于拦截匹配的请求,
拦截匹配规则要自己定义,把拦截下来的请求,依据相应的规则分发到目标Controller来
处理,是配置SpringMVC的第一步-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--整个项目的局部变量, 相当于设定了一个固定值,只能在当前的Servlet中被使用
param-name是键,相当于就是参数名,param-value是值,相当于参数值。
容器启动时会加载配直文件spring-mvc.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring/spring-mvc.xml</param-value>
</init-param>
<!--表示启动容器时初始化该Servlet.
当值为0或者大于0 时,表示容器在应用启动时力口载并初始化这个Servlet.
如果值小于0或未指定时,则指示容器在该Servlet被选择时才加载.
正值越小,Servlet的优先级越高,应用启动时就越先加载。值相同时,容器就会自己选择顺序来加载。-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--标签声明了与该Servlet相应的匹配规则,每个<url-pattern>标签代表1 个匹配规则。-->
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<!--URL 匹配规则,表示哪些请求交给Spring MVC处理,“/”表示拦截所有的请求(默认方式)。
以“*.”开头的字符串被用于扩展名匹配,如*.do
以“/”字符开头,并以“/*”结尾的字符串用于路径匹配,如:/test/*。
当一个URL与多个Servlet 的匹配规则可以匹配时,则按照“精确路径>最长路径>扩展名” 这样的优先级匹配到对应的Servlet.-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置视图解析器:对模型视图名称的解析-->
<!--最常用的视图解析器,当控制层返回“hello”时,
InternalResourceViewResolver解析器会自动添加前级和后缀,最终路径结果为:WEB-INF/views/hello.jsp-->
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/views"/>
<property name="suffix" value=".jsp"/>
<property name="exposeContextBeansAsAttributes" value="true"/>
</bean>
@RequestMapping
@RequestMapping 是Spring Web 应用程序中最常被用到的注解之一, 这个注解会将HTTP 请求映射到MVC 和RES 控制器的处理方法上。@RequestMapping 注解可以在控制器类的级别 或方法的级别上使用,在类级别上的注解会将一个特定请求或者请求模式映射到一个控制器之 上,之后你还可以另外添加方法级别的注解来进一步指定到处理方法的映射关系。先来看一个 具体示例:
@RequestMapping 注解除了value 属性外, 还有很多其他的属性, 具体如表6-1 所示。
value
可以将多个请求映射到一个方法上,只需要添加一个带有请求路径值列表的@RequestMapp in g 注解就可以了。如下图:
method
Spring MVC 的@RequestMapping 注解能够处理HTTP 请求的方法,比如GET 、PUT 、POST 、 DELETE 以及PATCH ,所有的请求默认是HTTP GET 类型的。为了将一个请求映射到一个特定 的HTTP 方法上,需要在@RequestMapping 中使用method 属性来声明HTTP 请求所使用的方法 类型,如下所示:
在上述代码中, @RequestMapping 注解中的method 元素声明了HTTP 请求方法的类型。所 有的处理方法会处理同一个URL (/user )进来的请求,但具体要看指定的HTTP 方法类型来决 定用哪个方法处理。例如,一个POST 类型的请求/user 会交给post()方法来处理,而一个 DELETE 类型的请求/user 则会由delete()方法来处理。
HTTP协议中请求的8中方法:
OPTIONS获取服务器支持的HTTP请求方法;
HEAD跟get很像,但是不返回响应体信息,用于检查对象是否存在,并获取包含在响应消息头中的信息。
GET向特定的资源发出请求,得到资源。
POST向指定资源提交数据进行处理的请求,用于添加新的内容。
PUT向指定资源位置上传其最新的内容,用于修改某个内容。
DELETE请求服务器删除请求的URI所标识的资源,用于删除。
TRACE回馈服务器收到的请求,用于远程诊断服务器。
CONNECT用于代理进行传输,如使用ssl
produces、consumes
可以使用@RequestMapping 注解的produces 和consumes 属性来指定处理请求的提交内容 类型( Content句pe ) 和返回的内容类型, 返回的类型必须是request 请求头( Accept )中所包含 的类型。具体可以看下面的实例:
上述代码中, getProduces()方法会返回JSON 类型的响应( application/json ) , getConsumes() 处理方法可以同时处理请求中的JSON 和XML 内容。
header
@RequestMapping 注解提供了一个header 元素来根据请求中的消息头内容缩小请求映射的 范围。该属性指定request 中必须包含某些指定的header 值, 才能让该方法处理请求。具体实例 如下:
上述代码中, @RequestMapping 注解的headers 属性将映射范围缩小到post()方法。post() 方法只会处理/user/head 请求路径, 并且contentype 被指定为text/plain 的请求。当然,还可以 指定多个消息头。具体代码如下所示:
上述代码中, post()方法能同时接受请求类型为text/plain 和text/html 的请求。
params
@RequestMapping 可以通过params 属性帮助进一步缩小请求映射的定位范围。使用params 属性,可以让多个处理方法处理同一个URL 的请求,而这些请求的参数是不一样的。可以用 myParams = myValue 这种格式来定义参数, 也可以使用通配符来指定特定的参数值在请求中是 不支持的。具体实例代码如下所示:
上述代码中, getParams()和getParamsDifferent() 两个方法都能处理相同的一个URL (/user/fetch ),但会根据params 属性的配置不同而决定执行哪一个方法。例如,当URL 是/user /fetch?id=10, getParams()方法会执行,因为id 的值为10 。对于user/fetch?personld=20 请求URL, getParamsDifferent()方法会执行,因为id 值为2 0 。
@GetMapping
@GetMapping 是@RequestMapping(method = RequestMethod.GET)的缩写,是组合注解。其他的@PostMapping、@DeleteMapping、@PutMapping等类似。
@ModelAttribute
@ModelAttribute使用大致有有两种,一种是是直接标记在方法上,一种是标记在方法的参数中,两种标记方法产生的效果也各不相同,这里就列举下两种标记所产生的效果
首先先做点简单的准备工作,写一个只包含一个button的jsp页面,这里可以看见,只是写了个简单按钮事件,跳转的modelTest.do这个路径
<%
标记在方法上
下面,再让我们写一个controller控制器,这里我们再控制器中写两个方法,其中一个使用@RequestMapping方法路径标记为modelTest.do,另外一个方法不标记路径,使用@ModelAttribute标记
package com.lovo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
部署后运行,点击页面测试按钮,查看控制台输出,这个时候你会发现,后台控制器并没有直接进入modelTest.do的路径,而是先执行了被@ModelAttribute标记的init方法。应该这么理解,当同一个controller中有任意一个方法被@ModelAttribute注解标记,页面请求只要进入这个控制器,不管请求那个方法,均会先执行被@ModelAttribute标记的方法,所以我们可以用@ModelAttribute注解的方法做一些初始化操作。当同一个controller中有多个方法被@ModelAttribute注解标记,所有被@ModelAttribute标记的方法均会被执行,按先后顺序执行,然后再进入请求的方法。
下面方法做一些变形,变形为带有参数的返回,这样也是实际开发中经常会操作的 首先像创建个pojo对象,对象包含userName,sex两个属性。并对JSP及控制器代码做一些修改
页面使用EL表达式接受返回参数
<script type="text/javascript">
$(function(){
$("#modelTest").on("click",function(){
window.location.href="<%=basePath%>model/modelTest.do";
})
});
</script>
<body>
<input type="button" id="modelTest" value="测试">
<input type="text" value="${pojo.userName }">
<input type="text" value="${pojo.sex }">
</body>
@ModelAtterbute方法无返回值情况
访问ModelTest.jsp页面并点击测试
出现如下结果
从执行结果看出,当访问请求时,会首先访问init方法,然后再对test方法进行访问,并且是同一个请求,因为model模型数据的作用域与request相同,所以可以用此标记直接标记在方法上对实际要访问的方法进行一些初始化操作
@ModelAttribute标记方法有返回值
@Controller @RequestMapping(value="model") public class ModelAttributeTest { @ModelAttribute public String init(Model mode) { System.out.println("进入init方法"); PojoTest pojo=new PojoTest(null, "小明", "男"); mode.addAttribute("pojo", pojo); return "model/befor.do"; } @RequestMapping(value="befor.do") public String befor(){ System.out.println("进入befor方法"); return "index"; } @RequestMapping(value="modelTest.do") public String modelTest() { System.out.println("进入modelTest方法"); return "modelTest"; } }
这里稍微做了点变形,可以看到在被@ModelAttribute方法中设值了返回路径为befor方法,但是在在代码运行的过程中并不会跳转befor方法,而是在代码执行完成return之前直接跳转了实际请求的方法。
当@RequestMapping标记和@ModelAttribute同时标记在一个方法上
@Controller @RequestMapping(value="model") public class ModelAttributeTest { @RequestMapping(value="modelTest.do") @ModelAttribute(value="pojo") public String modelTest() { System.out.println("进入modelTest方法"); return "modelTest"; } }
点击测试页面发现进入控制器后返回,页面报404,这是因为当两个注解标记到同一个方法上时,逻辑视图名并不是返回值,而是返回请求的路径,根据model/modelTest.do生成逻辑视图。在这里我们修改下代码,把controller上的@RequestMapping标记去掉,并修改下页面的请求路径,让生成的视图路径和访问的页面路径相同
@Controller public class ModelAttributeTest { @RequestMapping(value="modelTest.do") @ModelAttribute(value="pojo") public String modelTest() { System.out.println("进入modelTest方法"); return "modelTest"; } }
<script type="text/javascript"> $(function(){ $("#modelTest").on("click",function(){ window.location.href="<%=basePath%>modelTest.do"; }) }); </script> <body> <input type="button" id="modelTest" value="测试"> <input type="text" value="${pojo }"> </body>
点击测试页面,会发现当两个注解同时注解到一个方法上时,方法的返回值会变成model模型的返回值,key是标记的名
标记在参数前
从from表单或url地址中取值,这里就以url地址为例,为了避免url地址中文乱码问题,这里调用了encodeURL函数
<script type="text/javascript"> $(function(){ $("#modelTest").on("click",function(){ window.location.href="<%=basePath%>model/modelTest.do?userName="+encodeURI('小明')+"&sex="+encodeURI('男'); }) }); </script> <body> <input type="button" id="modelTest" value="测试"> <input type="text" value="${pojo.userName }"> <input type="text" value="${pojo.sex }"> </body>
@Controller @RequestMapping(value="model") public class ModelAttributeTest { @RequestMapping(value="modelTest.do") public String modelTest(@ModelAttribute("pojo") PojoTest pojo) { try { pojo.setUserName(new String(pojo.getUserName().getBytes("iso-8859-1"),"utf-8")); pojo.setSex(new String(pojo.getSex().getBytes("iso-8859-1"),"utf-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(pojo); return "modelTest"; } }
点击页面测试,页面文本框会显示URL地址传递过来的参数,因为SpringMVC会自动匹匹配页面传递过来的参数的name属性和后台控制器中的方法中的参数名,如果参数名相同,会自动匹配,如果控制器中方法是封装的bean,会自动匹配bean中的属性,其实这种取值方式不需要用@ModelAttribute注解,只要满足匹配要求,也能拿得到值
从model对象中取值
@Controller @RequestMapping(value="model") public class ModelAttributeTest { @ModelAttribute("pojo") public PojoTest init( PojoTest pojo) { pojo.setSex("男"); return pojo; } @RequestMapping(value="modelTest.do") public String modelTest(@ModelAttribute("pojo") PojoTest pojo) { pojo.setUserName("小明"); return "modelTest"; } }
点击测试发现,modelTest拿到inint方法中的pojo对象,合并两次set的参数后返回页面
参数绑定注解
@RequestParam
@RequstParam 注解用于将制定的请求参数赋值给方法中的形参。@RequstParam 注解可以 使用的属性如表6-4 所示。
当浏览器中输入URL 请求http: //localhost: 8080/user/findBy Id?id= 1 时, 请求中的id=1 会将值 赋给findByld 方法中的id 变量。@RequstParam 注解可以使用required 属性指定参数是否必须传值。
如果参数没有接收到任何值, 可以使用default Value 指定参数的默认值。具体代码如下所示:
@PathVariable
@Path Variable 注解可以将URL 中动态参数绑定到控制器处理方法的入参中。 @PathVariable 注解只有一个value 属性, 类型为String,表示绑定的名称,如果缺省则默认绑定 同名参数。具体示例如下所示:
当在浏览器中输入请求URL: http://localhost: 8080/user/ owners/ 123 /pets /456 时,则自动将动 态参数{ownerld }和{ petld }的值123 和456 绑定到@PathVariable注解的同名参数上,即ownerld = 123, petld = 456 。
URL 中的动态参数除了可以绑定到方法上,还可以绑定到类上, 具体示例如下所示:
@RequestHeader
@RequestHeader 注解可以将请求的头信息数据映射到处理的方法参数上,@RequestHeader 注解的属性如表6-5 所示。
一般请求头信息如下所示:
下面通过@RequestHeader 注解获取Accept- Encoding 和Keep-Alive 信息,具体示例如下 所示:
当在浏览器中输入请求URL: http :// localhost: 8080/user/requestHeader 时,则自动将请求头 “Accept-Encoding ”和“ Accept p’的值赋到encoding 和accept 变量上,由于请求头 “Accept-Encoding” 和“Accept ”的数据类型是数组,所有定义encoing 和accept 变量为 String[]类型。
@CookieValue
@CookieValue 注解用于将请求的Cookie 信息映射到处理的方法参数上, @CookieValue 注 解的属性如表6 - 6 所示。
当在浏览器中输入请求URL: http://localhost: 8080/us er/cookieValue 时,则自动将 JSESSIONID 值设置到Cookie 参数上。
@ModelAttribute
@ModelAttribute 注解主要是将请求参数绑定到Model 对象上。@ModelAttribute 注解只有 一个value 属性,类型为String , 表示绑定的属性名称。当Controller类中有任意一个方法被 @ModelAttribute注解标记,页面请求只要进入这个控制器, 不管请求那个方法,均会先执行被 @ ModelAttribute标记的方法,所以可以用@ModelAttribute 注解的方法做一些初始化操作。当 同一个Controller 类中有多个方法被@ModeLAttribute 注解标记,所有被@ModelAttribute标记的 方法均会被执行,按先后顺序执行,然后再进入请求的方法。具体实例代码如下所示:
当在浏览器输入访问URL: http: //localhost: 8080/user/findById/I 时,便可以在控制台看到打 印信息:
@ModeIAttribute 注解有很多的额外使用方式,下面逐一进行介绍。
@ModelAtterbute方法无返回值的情况
上述代码中@ModelAttribute 注解标记的init 方法无任何返回值,在in it 方法中创建一个用 户对象AyUser 并设置id 和name 的值,最后调用Model 对象的addAttribute 方法设置到Model 对象中。对应前端srcmain webapp WEB-INFviewshello.j sp 页面代码如下所示:
当在浏览器中输入请求URL: http://localhost:8080/user/hello 时,浏览器显示“ hello , ay ”。 从执行结果可以看出,当执行请求时,首先访问init 方法, 然后再访问hello 方法, 并且是 同一个请求。因为model 模型数据的作用域与request 相同,所以可以用@@ModelAttribute 注解直 接标记在方法上对实际要访问的方法进行一些初始化操作。
@ModelAttribute标记方法有返回值
上述代码中,@ModelAttribute 注解标注的init 方法带有返回值,@ModelAttribute(”name”) 的va lue 属性值为key ,而init方法值为value , 类似于:
model.addAttribute("name", name);
对应前端srcmainwe bappWEB-INF viewshello.jsp 页面代码如下所示:
当在浏览器中输入请求URL: http ://localbost:8080/user/hello 时,浏览器显示“ hello, ay"
@ModelAttribute注解和@RequestMapping 注解同时标记在一个方法上。
上述代码中,@ModelAttribut e 注解和@RequestMapping 注解同时标记在hello 方法上,同 时我们把AyUserController 类上的@RequestMapping 注解去掉。此时, hello 方法的返回值 “ ay ” 并不是视图名称,而是model 属性的值,视图名称是@RequestMapping 的value 值 “ hello”。Model 的属性名称由@ModelAttribute的value 值指定,相当于在request 中封装了name ( key ) = ay ( value ) 。对应前端srcmainwebappWEB-INFv iewshello.jsp 页面代码如下所示:
当在浏览器中输入请求URL : http://loca lhost: 8080/hello 时,浏览器显示“hello, ay ”
使用@ModelAttribute注解方法的参数
当在浏览器中输入请求URL: http://l ocalhost: 8080/user/hello?id=1 &name=ay 时,会优先执 行init 方法, 把id 和name 的值赋值给init 方法绑定的参数, 同时构造AyUser对象返回。这里 model的属性名称就是@ModelAttribute("ayUser”)的value 值“ ayUser ”, model 的属性值就是 init 方法的返回值。hello 方法的参数AyUser 使用了注解@ModelAttribute(”ayUser),表示参数 ayUser 的值就是init 方法中的model 属性。 对应前端srcmainwebapp WEB-INFviewshello.j sp 页面代码如下所示:
@SessionAttribute 和@SessionAttributes
@ModelAttribute 注解作用在方法或者方法的参数上,表示将被注解的方法的返回值或者 是被注解的参数作为Model 的属性加入到Model 中, Spring 框架会将Model 传递给前端。Model 的生命周期只存在于HTTP 请求的处理过程中,请求处理完成后, Model就销毁了。如果想让 参数在多个请求间共享,那么需要用到@SessionAttributes 注解。@SessionAttributes 注解只能声 明在类上,不能声明在方法上。
@Sess ionAttributes 注解常用的属性
在浏览器中输入请求URL : http: // localhost:8080/user/redirect 时,由于AyUserController 类 上添加注解@SessionAttributes (” ayUser”) ,方法redirectTest 在执行过程中,将AyUser 对象存放 到Model对象的同时,也会把对象存放到HttpSession 作用域中。redirectTest 方法执行完成之 后,会重定向到hello 方法。在hello 方法中, HttpSession 对象会将@SessionAttribute s 注解的属 性写入到新的Model 中, 所以可以通过Mod e lMap 获取的AyUser 对象打印信息。
除了使用ModelMap 来接收HttpSession 对象中的值外,还可以使用@SessionAttribute 注 解, 具体实例如下所示:
上述代码中, 在hello 方法中使用@Sess ionAttribute 注解来获取@SessionAttribut es ("ayUser ”) 注解中的“ ayUser ”对象并赋值给AyUser 对象,这样就可以很方便地在hello 方法中使用AyUser 对象。
如果想删除HttpSession 对象中共享的属性,可以通过SessionStatus .setC omplete (),这句只 会删除通过@SessionAttribut e 保存到HttpSession 中的属性。具体实例如下所示:
还可以设置多个对象到HttpSession 中,具体代码如下:
@ResponseBody
@ResponseBody 注解用于将Controller 方法返回的对象, 通过HttpMessageConverter 转换为 指定格式后,写入到Response对象的body 数据区。@Responsebody 注解将方法的返回结果直接 写入Hπp 响应正文( ResponseBody )中,一般在异步获取数据时使用。在使用@RequestMapping 注解时,返回值通常被解析为跳转路径,在加上@Responsebody 后返回结果就不会被解析为跳转 路径, 而是直接写入到HTTP 响应正文中。
使用@ResponseBody 和@RequestBody 注解之前,需要在pom.xml 文件中引入Jackson 相关 的依赖包, 具体示例代码如下:
Spring MVC 主要是利用类型转换器messageConverters 将前台信息转换为开发者需要的格 式。然后在相应的Controller 方法接受参数前添加@RequestBody 注解, 进行数据转换, 或者在 方法的返回值类型处添加@ResponseBody 注解, 将返回信息转换成相关格式的数据。
-
返回普通的字符串
在浏览器中输入访问路径: http ://localhost:8080/user/hello , 方法返回的不是视图,而是把 字符串“ I am not view ”直接写入HTTP 响应正文中,返回给浏览器。
-
返回集合对象
在浏览器输入访问路径: http://localhost: 8080/user/hel lo , 方法返回的不是视图,而是把 JSON 字符串“ {”name":" ay”,"age":”3 ”}” 直接写入HTTP 响应正文中, 返回给浏览器。
@RequestBody
@RequestBody 注解用于读取Request 请求的body 部分数据,使用系统默认配置的 HttpMessageConverter 进行解,析,然后把相应的数据绑定到Controller 方法的参数上。