一、SpringMVC 使用 @PathVariable、@RequestParam、@RequestHeader、@CookieValue 等来解决参数获取问题。1. @PathVariable:映射 URL 绑定的占位符,可以借助于传入到方法参数列表中的 @PathVariable 注解获取到 URL 映射中的参数值。如:<a href="handler01/1">test pathvariable</a>@RequestMapping("/handler01/{id}")public String testPathVariable(@PathVariable("id") String id) { System.out.println("id:" + id); return "success";}说明:URL 绑定占位符使 SpringMVC 对 REST 提供了支持。对于具体的 SpringMVC 的 REST 风格的例子会在以后的文章里介绍。
2.@RequestParam 官方文档是这样描述的:
* Annotation which indicates that a method parameter should be bound to a web * request parameter. Supported for annotated handler methods in Servlet and * Portlet environments. * * <p>If the method parameter type is {@link Map} and a request parameter name * is specified, then the request parameter value is converted to a {@link Map} * assuming an appropriate conversion strategy is available. * * <p>If the method parameter is {@link java.util.Map Map<String, String>} or * {@link org.springframework.util.MultiValueMap MultiValueMap<String, String>} * and a parameter name is not specified, then the map parameter is populated * with all request parameter names and values. |
说明一下:(1)该注解表明 web 请求参数绑定到目标 handler 方法的入参。(2)如果方法的入参类型是一个 Map,不包含泛型类型,并且请求参数名称是被指定的(如:public String testRequestParam5(@RequestParam("userName") Map map)),请求参数会被转换为一个 Map,前提是存在转换策略。这里所说的转换策略,通常是指 请求参数 到 Map 的类型转换,如请求参数为 userName=a|12,b|34 这样的数据,需要通过一个转换策略(类型转换器)来完成 a|12,b|34 到 map 的转换。在我们一般开发的过程中,不包含这种情况。是一种扩展。关于类型转换会在后面的文章中介绍。(3)如果方法的入参是一个 Map 且指定了泛型类型 Map<String,String> 或者是 org.springframework.util.MultiValueMap 类型的 MultiValueMap<String, String>并且没有指定请求参数,那么这个 Map 类型的参数会将所有的请求参数名称和值填充(populate)到其中。如:请求:<a href="testRequestParam4?userName=jack&age=23">test request param4</a>handler 方法:@RequestMapping("/testRequestParam4")public String testRequestParam4(@RequestParam Map<String, String> map) { System.out.println("map:" + map); return "success";}控制台输出:map:{userName=jack, age=23}上面整体介绍了 @RequestParam,下面详细看看它的API:包含三个属性:(1)value 属性,默认为 ""官方文档说明:The name of the request parameter to bind to.解释的已经很明白了,不再赘述。(2)required 属性,默认为 true官方文档说明:Whether the parameter is required.见名知意,该请求参数是否是必须的。为 true 的请求下,若请求参数中没有,则会抛出一个异常。为 false 的情况下,如果请求参数中没有,则方法入参对应值为 null。另外,提供一个 defaultValue 属性,则会是此属性设置为 false。(3)defaultValue 属性当没有提供对应的请求参数,或者请求参数为空时,会使用此属性对应的值。当设置此属性的时候,会将 required 属性设置为 false。下面提供几个常见请求情况的例子:(1)请求为:<a href="testRequestParam?userName=abc">test request param</a> handler 方法:@RequestMapping("/testRequestParam")public String testRequstParam01(@RequestParam("userName") String userName) { System.out.println("userName: " + userName); return "success";}(2)请求为:<a href="testRequestParam2?
userName=jack&
userName=lucy">test request param2</a>handler 方法:@RequestMapping("/testRequestParam2")public String testRequestParam02(@RequestParam("userName") List<String> userNames) { System.out.println("userNames:" + userNames); return "success";}控制台输出:userNames:[jack, lucy](3)请求为:<a href="testRequestParam4?userName=jack&age=23">test request param4</a>handler 方法:@RequestMapping("/testRequestParam4")public String testRequestParam4(@RequestParam Map<String, String> map) { System.out.println("map:" + map); return "success";}控制台输出:map:{userName=jack, age=23}主要就分为这三种情况,其中第一种最为常用,第二种和第三种很少能想到,若能想到的话,能为我们开发节省不少时间。3.@RequestHeader官方文档中是这样描述的:
Annotation which indicates that a method parameter should be bound to a web request header. Supported for annotated handler methods in Servlet and Portlet environments. |
和 @RequestParam 描述类似,只不过绑定的是 web 请求头信息到方法入参。定义的三个属性和 @RequestParam 一样,默认值和使用的方法也一样。由于用的比较少,这里只做一个例子说明:请求:<a href="testRequestHeader">test request header</a>handler 方法:@RequestMapping("/testRequestHeader")public String testRequestHeader(@RequestHeader(value = "Accept", required = false) String accept) { System.out.println("accept:" + accept); return "success";}控制台输出:accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.84.@CookieValue官方文档描述:
Annotation which indicates that a method parameter should be bound to an HTTP cookie. Supported for annotated handler methods in Servlet and Portlet environments. |
绑定一个 http cookie 到方法的入参,其中 value 属性表明要入参的 cookie 的 key。默认值和使用方式和 @RequestParam 类似。例子:请求:<a href="testCookieValue">test cookie value</a>handler 方法:@RequestMapping("/testCookieValue")public String testCookieValue(@CookieValue(value = "JSESSIONID", required = false) String sessionId) { System.out.println("sessionId:"+sessionId); return "success";}控制台输出:sessionId:9D16BDF7063E1BFD9A0C052F1B109A0D5.绑定请求参数到方法入参处的 bean 对象。先看两个例子:(1)绑定请求参数到 bean 请求:包括 get 和 post 请求方式提交的情况。<a href="testBean?personName=jack&age=23">test bean</a><form action="testBean" method="post"> <label> personName:<input type="text" name="personName"/> </label> <label> age:<input type="text" name="age"/> </label> <input type="submit" value="submit"/></form>handler 方法:@RequestMapping("/testBean")public String testBean(Person person) { System.out.println(person);//Person{personName="jack", age="23"} return "success";}发现不论是通过 get 方式,还是post 方式,都可以将对应的请求参数注入到对应的 bean 中。(2)绑定请求参数到级联的 beanbean 的结构:/** * @author solverpeng * @create 2016-08-04-9:43 */public class Employee {private String empName;private Address address;public String getEmpName() {return empName;}public void setEmpName(String empName) {this.empName = empName;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}@Overridepublic String toString() {return "Employee{" +"empName="" + empName + """ +", address=" + address +"}";}}Employee /** * @author solverpeng * @create 2016-08-04-9:43 */public class Address {private String addressName;public String getAddressName() {return addressName;}public void setAddressName(String addressName) {this.addressName = addressName;}@Overridepublic String toString() {return "Address{" +"addressName="" + addressName + """ +"}";}}Address 请求:同样包含 get 请求 和 post 请求<a href="testBeanCascade?empName=jack&address.addressName=beijing">test bean cascade</a><form action="testBeanCascade" method="post"><label>empName:<input type="text" name="empName"/></label><label>Address:<input type="text" name="address.addressName"/></label><input type="submit" value="submit"/></form>handler 方法:@RequestMapping("/testBeanCascade")public String testBeanCascade(Employee employee) { System.out.println(employee);//Employee{empName="jack", address=Address{addressName="beijing"}} return "success";}是如何绑定的呢?翻源码过程如下:org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter#invokeHandlerMethodExtendedModelMap implicitModel = new BindingAwareModelMap();
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);第一步:发现在 result 中已经包含了注入的 bean。所以注入是在methodInvoker.invokeHandlerMethod() 方法中做的。
第二步:org.springframework.web.bind.annotation.support.HandlerMethodInvoker#invokeHandlerMethod
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);第三步:org.springframework.web.bind.annotation.support.HandlerMethodInvoker#resolveHandlerArguments
doBind(binder, webRequest, validate, validationHints, !assignBindingResult);// 这里进行的绑定第四步:org.springframework.web.bind.ServletRequestDataBinder#bind
doBind(mpvs);第五步:org.springframework.validation.DataBinder#doBind
this.applyPropertyValues(mpvs);最终发现,是在 DataBinder 这个类的 doBind() 方法中进行的绑定。在翻源码的过程中,发现 resolveHandlerArguments() 方法值得大家看一看,不论水平高低,其实真正解决 SpringMVC 参数问题就是在这个方法中解决的。
总结一下:Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值。支持级联属性。二、SpringMVC 解决 Servlet 资源获取问题1. SpringMVC 使用 Servlet 资源作为方法的入参来解决 Servlet 资源获取问题。2.可以作为入参的 Servlet 资源有:HttpServletRequest、HttpServletResponse、HttpSession、Locale、InputStream、OutputStream、Reader、Writer3.例子:使用 HttpServletRequest 作为入参请求:<a href="testServletAPI">test servlet api</a>handler 方法:@RequestMapping("/testServletAPI")public String testServletAPI(HttpServletRequest request) { String id = request.getSession().getId(); System.out.println("sessionId:" + id); return "success";}控制台输出:sessionId:E369037AF3DC276BA78539F0AF5C044B其他的 Servlet 资源这里就不在赘述。
三、总结SpringMVC 使用 @PathVariable 来获取 @RequestMapping 中占位符的值,为 REST 风格的程序的编写提供了支持。使用
@RequestParam 能接收绝大部分请求参数,同时提供了类型转换这种扩展。使用 @RequestHeader 来映射请求头信息。使用 @CookieValue 来映射 http cookie 信息。同时还支持模型的注入。也可以获取到原生的 servlet 资源。即在目标的方法处,我们可以获取到任何我们想要的资源,SpringMVC 对这个过程进行了简化,使开发更加便捷,灵活。Spring学习之第一个Spring MVC程序(IDEA开发环境) http://www.linuxidc.com/Linux/2016-06/132658.htmSpringMVC总结篇 http://www.linuxidc.com/Linux/2016-06/132659.htmSpring+SpringMVC企业快速开发架构搭建 http://www.linuxidc.com/Linux/2015-09/122942.htmSpringMVC的乱码处理 http://www.linuxidc.com/Linux/2015-07/120542.htmSpring MVC+Spring3+Hibernate4开发环境搭建 http://www.linuxidc.com/Linux/2013-07/87119.htm Spring MVC整合Freemarker基于注解方式 http://www.linuxidc.com/Linux/2013-02/79660.htm 基于注解的Spring MVC简单介绍 http://www.linuxidc.com/Linux/2012-02/54896.htmSpringMVC详细示例实战教程 http://www.linuxidc.com/Linux/2015-06/118461.htmSpring MVC 框架搭建及详解 http://www.linuxidc.com/Linux/2012-01/52740.htmSpringMVC 异常处理 http://www.linuxidc.com/Linux/2015-06/119049.htm
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-08/133973.htm