1、Spring對REST的支持
Spring3(這里討論Spring3.2+)對Spring MVC的一些增強功能為REST提供了良好的支持。Spring對開發REST資源提供以下支持:
- 操作方式:控制器可以處理所有的HTTP方法,包含4個主要的REST方法:GET、PUT、DELETE以及POST。Spring的表單綁定JSP標簽庫的<form:form>標簽以及新的HiddenHttpMethodFilter,使得通過HTML表單提交的PUT和DELETE請求成為可能
- 資源標識:新的@PathVariable注解使得控制器能夠處理參數化的URL
- 資源表述方法1:通過使用Spring的視圖解析器,資源可以以各種形式進行表述,包括XML、JSON、Atom和RSS()。使用新的ContentNegotiatingViewResolver來選擇最合適客戶端的表述
- 資源表述方法2:通過使用@ResponseBody注解和各種HttpMethodConverter實現來達到。@RequestBody注解記憶HttpMethodConverter實現可以將傳入的HTTP數據轉化為傳入控制器方法的Java對象
- REST客戶端:RestTemplate簡化了客戶端對REST資源的使用
2、參數化URL
Spring3.0中引入了新的@PathVariable注解,幫助Controller使用面向資源的方式處理請求。如:
1 @Controller 2 @RequestMapping(value="/spitters") 3 public class SpittleController{ 4 private SpitterService spitterService; 5 ... 6 @RequestMapping(value="/{id}", method= RequestMethod.PUT) 7 @ResponseStatus(HttpStatus.NO_CONTENT) 8 public void putSpittle(@PathVariable("id")) long id, 9 @Valid Spittle spittle) 10 { 11 spitterService.saveSpittle(spittle); 12 } 13 }
3、協商資源表述
“協商資源表述”即確定一個HTTP請求所需要返回的資源格式,HTML、XML還是JSON等。按優先級,Spring MVC依次通過以下三個條件確定客戶端需要什么類型的內容表述:
- URL路徑上添加擴展名,如 http://myserver/myapp/accounts/list.html 或者 http://myserver/myapp/accounts/list.xls。
- URL參數,如 http://myserver/myapp/accounts/list?format=xls,參數名默認為“format”,但也可以更改。
- 通過HTTP請求的Accept頭部信息確定返回資源格式,但由於客戶端的處理不同,這個方法並不太可靠。
JAF(JavaBeans Activation Framework)可提供由擴展名(或URL參數)到資源格式的自動映射機制(如json->"application/json"),這時classpath下必須有activation.jar包。
Spring MVC通過ContentNegotiationManager來進行資源協商,一般配置如下:
1 <bean id="contentNegotiationManager" 2 class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> 3 <property name="favorPathExtension" value="false" /> 4 <property name="favorParameter" value="true" /> 5 <property name="parameterName" value="mediaType" /> 6 <property name="ignoreAcceptHeader" value="true"/> 7 <property name="useJaf" value="false"/> 8 <property name="defaultContentType" value="application/json" /> 9 10 <property name="mediaTypes"> 11 <map> 12 <entry key="json" value="application/json" /> 13 <entry key="xml" value="application/xml" /> 14 </map> 15 </property> 16 </bean>
以上配置表示:不使用路徑擴展名方式,也不使用Accept頭信息方式,僅使用URL參數方式確定資源類型,URL參數名使用“mediaType”代替默認的“format”,不使用JAF而是自行定義URL參數到資源格式的映射,這里只定義了JSON和XML。
4、表述資源(一)
Spring MVC通過兩種方式將Java表述形式的資源轉化為發送給客戶端的表述形式:
- 基於視圖渲染進行協商(@ContentNegotiatingViewResolver)
- HTTP消息轉換器(HttpMessageConverters)
首先是第一種方式,使用HttpMessageConverters表述資源。當ContentNegotiationManager配置如下,且類路徑下包含JAXB和Jackson包時,Spring MVC將自動匹配使用哪種HttpMessageCoverter。
ContentNegotiationManager配置:
1 <!-- enable the "produces" annotation of "RequestMapping" --> 2 <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" /> 3 4 <!-- Simple strategy: only path extension is taken into account --> 5 <bean id="cnManager" 6 class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> 7 <property name="favorPathExtension" value="true"/> 8 <property name="ignoreAcceptHeader" value="true" /> 9 <property name="defaultContentType" value="text/html" /> 10 <property name="useJaf" value="false"/> 11 12 <property name="mediaTypes"> 13 <map> 14 <entry key="html" value="text/html" /> 15 <entry key="json" value="application/json" /> 16 <entry key="xml" value="application/xml" /> 17 </map> 18 </property> 19 </bean>
Controller:
1 @Controller 2 class AccountController { 3 // RESTful method, use MappingJacksonHttpMessageConverter and Jaxb2RootElementHttpMessageConverter automatically 4 //accounts.json -> application/json 5 //accounts.xml -> text/xml 6 @RequestMapping(value="/accounts", produces={"application/xml", "application/json"}) 7 @ResponseStatus(HttpStatus.OK) 8 public @ResponseBody List<Account> listWithMarshalling(Principal principal) { 9 return accountManager.getAccounts(principal); 10 } 11 12 // View-based method 13 //accounts.html -> text/html 14 //accounts.others -> text/html 15 @RequestMapping("/accounts") 16 public String listWithView(Model model, Principal principal) { 17 // Call RESTful method to avoid repeating account lookup logic 18 model.addAttribute( listWithMarshalling(principal) ); 19 20 // Return the view to use for rendering the response 21 return ¨accounts/list¨; 22 } 23 }
使用 JAXB 和 Jackson時,需要在account類上添加annotation:

1 /** 2 * Represents an account for a member of a financial institution. An account has 3 * zero or more {@link Transaction}s and belongs to a {@link Customer}. An aggregate entity. 4 */ 5 @Entity 6 @Table(name = "T_ACCOUNT") 7 @XmlRootElement 8 public class Account { 9 10 // data-members omitted ... 11 12 public Account(Customer owner, String number, String type) { 13 this.owner = owner; 14 this.number = number; 15 this.type = type; 16 } 17 18 /** 19 * Returns the number used to uniquely identify this account. 20 */ 21 @XmlAttribute 22 public String getNumber() { 23 return number; 24 } 25 26 /** 27 * Get the account type. 28 * 29 * @return One of "CREDIT", "SAVINGS", "CHECK". 30 */ 31 @XmlAttribute 32 public String getType() { 33 return type; 34 } 35 36 /** 37 * Get the credit-card, if any, associated with this account. 38 * 39 * @return The credit-card number or null if there isn't one. 40 */ 41 @XmlAttribute 42 public String getCreditCardNumber() { 43 return StringUtils.hasText(creditCardNumber) ? creditCardNumber : null; 44 } 45 46 /** 47 * Get the balance of this account in local currency. 48 * 49 * @return Current account balance. 50 */ 51 @XmlAttribute 52 public MonetaryAmount getBalance() { 53 return balance; 54 } 55 56 57 /** 58 * Returns a single account transaction. Callers should not attempt to hold 59 * on or modify the returned object. This method should only be used 60 * transitively; for example, called to facilitate reporting or testing. 61 * 62 * @param name 63 * the name of the transaction account e.g "Fred Smith" 64 * @return the beneficiary object 65 */ 66 @XmlElement // Make these a nested <transactions> element 67 public Set<Transaction> getTransactions() { 68 return transactions; 69 } 70 71 // Setters and other methods ... 72 73 }
5、表述資源(二)
第二種方式,使用視圖渲染器(View Resolution)。使用ContentNegotiatingViewResolver和ContentNegotiationManager進行配置:
1 <!-- View resolver that delegates to other view resolvers based on the content type --> 2 <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> 3 <!-- All configuration is now done by the manager - since Spring V3.2 --> 4 <property name="contentNegotiationManager" ref="cnManager"/> 5 </bean> 6 7 <!-- Setup a simple strategy --> 8 <bean id="cnManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> 9 <property name="ignoreAcceptHeader" value="true"/> 10 <property name="defaultContentType" value="text/html" /> 11 </bean>
使用以上配置Spring將根據約定自動查找ViewResolvers,並渲染資源。也可顯式配置ViewResolvers:
1 <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> 2 <property name="contentNegotiationManager" ref="cnManager"/> 3 4 <!-- Define the view resolvers explicitly --> 5 <property name="viewResolvers"> 6 <list> 7 <bean class="org.springframework.web.servlet.view.XmlViewResolver"> 8 <property name="location" value="spreadsheet-views.xml"/> 9 </bean> 10 11 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 12 <property name="prefix" value="WEB-INF/views"/> 13 <property name="suffix" value=".jsp"/> 14 </bean> 15 </list> 16 </property> 17 </bean>
這樣Controller就可以去掉@ResponseBody的Method,只寫一個Method了。
6、參考