歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

SpringMVC核心——參數獲取與Servlet資源獲取問題

一、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&lt;String, String&gt;} or
* {@link org.springframework.util.MultiValueMap MultiValueMap&lt;String, String&gt;}
* 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.8

4.@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:9D16BDF7063E1BFD9A0C052F1B109A0D

5.綁定請求參數到方法入參處的 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)綁定請求參數到級聯的 bean

bean 的結構:

/**
 * @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;
    }

    @Override
    public 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;
    }

    @Override
    public 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#invokeHandlerMethod

ExtendedModelMap 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、Writer

3.例子:

使用 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.htm

SpringMVC總結篇  http://www.linuxidc.com/Linux/2016-06/132659.htm

Spring+SpringMVC企業快速開發架構搭建  http://www.linuxidc.com/Linux/2015-09/122942.htm

SpringMVC的亂碼處理  http://www.linuxidc.com/Linux/2015-07/120542.htm

Spring 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.htm

SpringMVC詳細示例實戰教程 http://www.linuxidc.com/Linux/2015-06/118461.htm

Spring MVC 框架搭建及詳解 http://www.linuxidc.com/Linux/2012-01/52740.htm

SpringMVC 異常處理  http://www.linuxidc.com/Linux/2015-06/119049.htm

Copyright © Linux教程網 All Rights Reserved