簡介
JAX-RS (JSR-311) 是爲 Java EE 環境下的 RESTful 服務能力提供的一種規範。它能提供對傳統的基於 SOAP 的 Web 服務的一種可行替代。JAX-RS,全稱爲Java API for RESTful Web Services.的核心概念是resource,即面向資源。
@Path,標註資源類或者方法的相對路徑
@GET,@PUT,@POST,@DELETE,標註方法是HTTP請求的類型。
@Produces,標註返回的MIME媒體類型
@Consumes,標註可接受請求的MIME媒體類型
@PathParam,@QueryParam,@HeaderParam,@CookieParam,@MatrixParam,@FormParam,分別標註方法的參數來自於HTTP請求的不同位置,例如@PathParam來自於URL的路徑,@QueryParam來自於URL的查詢參數,@HeaderParam來自於HTTP請求的頭信息,@CookieParam來自於HTTP請求的Cookie。
基於JAX-RS實現的框架有Jersey,RESTEasy等。這兩個框架創建的應用可以很方便地部署到Servlet 容器中,比如Tomcat,JBoss等。值得一提的是RESTEasy是由JBoss公司開發的,所以將用RESTEasy框架實現的應用部署到JBoss服務器上,可以實現很多額外的功能。
Java 資源
JAX-RS 建立了一種特殊的語言來描述資源,正如由其編程模型所表示的。有五種主要條目:根資源、子資源、資源方法、子資源方法以及子資源定位器。
根資源
根資源是由 @Path
註釋的 Java 類。@Path
註釋提供了一個 value
屬性,用來表明此資源所在的路徑。value
屬性可以是文本字符、變量或變量外加一個定製的正則表達式。清單 1 給出了一個例子。
清單 1. JAX-RS 根資源
|
子資源
子資源是作爲 subresource locator 調用的結果返回的 Java 類。它們類似於根資源,只不過它們不是由 @Path
註釋的,因它們的路徑是由子資源定位器給出的。子資源通常包含由 HTTP 請求方法指示符(designator)註釋的方法以便服務此請求。如果它們不包含如此註釋的方法,那麼它們將會通過指派給合適的子資源定位器來進一步解析此資源處理請求。
清單 2. JAX-RS 子資源
|
如上所示的清單 2 展示了由 ContactsResource.getContactDepartment
方法返回的子資源。在這個例子中,如果一個 HTTP GET 請求被髮送給 /contact/{contactName}/department
路徑,那麼 Department
子資源內的 getDepartmentName
資源方法就會處理此請求。
資源方法
資源方法是根資源或子資源內綁定到 HTTP 方法的 Java 方法。綁定是通過諸如 @GET
這樣的註釋完成的。
清單 3. JAX-RS 資源方法
|
在清單 3 的例子中,發送到 /contacts
路徑的 HTTP GET 請求將會由 getContacts()
資源方法處理。
子資源方法
子資源方法非常類似於資源方法;惟一的區別是子資源方法也是由 @Path
註釋的,此註釋進一步限定了該方法的選擇。
清單 4. JAX-RS 子資源方法
|
在清單 4 中,發送到 /contacts/ids
路徑的 HTTP GET 請求將會由 getContactIds()
子資源方法處理。
子資源定位器
子資源定位器是能進一步解析用來處理給定請求的資源的一些方法。它們非常類似於子資源方法,因它們具備一個 @Path
註釋,但不具備 HTTP 請求方法指示符,比如 @GET
註釋。
清單 5. JAX-RS 子資源定位器
|
在上述例子中,對 /contact/{contactName}/department
路徑的任何 HTTP 請求都將由 getContactDepartment
子資源定位器處理。 {contactName}
部分表明 contact
路徑部分之後可以是任何合法的 URL 值。
註釋
本節將會探討一些重要的註釋及其使用。對於由 JAX-RS 規範提供的註釋的完整列表,可以參考本文的 參考資料 部分給出的 JSR-311 鏈接。
@Path
@Path
註釋被用來描述根資源、子資源方法或子資源的位置。value
值可以包含文本字符、變量或具有定製正則表達式的變量。清單 6 的例子展示了 @Path
註釋的主要應用。
清單 6. @Path 的使用
|
ContactsResource
類上的註釋表明對 /contacts
路徑的所有請求都將由 ContactsResource
根資源處理。getByEmailAddress
上的 @Path
註釋則表明任何發送到 /contacts/{emailAddress}
的請求(其中 emailAddress
代表的是正則表達式 .+@.+\\.[a-z]+
)都將由 getByEmailAddress
處理。
getByLastName
方法上的 @Path
註釋指定了發送到 /contacts/{lastName}
路徑的所有請求(其中 lastName
代表的是一個與getByEmailAddress
內的正則表達式不匹配的有效的 URL 部分)都將由 getByLastName
方法處理。
@GET、@POST、@PUT、@DELETE、@HEAD
@GET、@POST、@PUT、@DELETE 以及 @HEAD 均是 HTTP 請求方法指示符註釋。您可以使用它們來綁定根資源或子資源內的 Java 方法與 HTTP 請求方法。HTTP GET 請求被映射到由 @GET 註釋的方法;HTTP POST 請求被映射到由 @POST 註釋的方法,以此類推。用戶可能還需要通過使用 @HttpMethod
註釋定義其自己的定製 HTTP 請求方法指示符。
清單 7. 定製的 HTTP 請求方法指示符註釋
|
上述的聲明定義了 @CustomGET
註釋。此註釋將具有與 @GET 註釋相同的語義值並可用在其位置上。
@Conumes 和 @Produces
@Consumes
註釋代表的是一個資源可以接受的 MIME 類型。@Produces
註釋代表的是一個資源可以返回的 MIME 類型。這些註釋均可在資源、資源方法、子資源方法、子資源定位器或子資源內找到。
清單 8. @Consumes/@Produces
|
對於上述的 getByEmailAddress
和 addContactInfo
方法,它們均能處理 text/xml
和 application/json
。被接受或返回的資源表示將依賴於客戶機設置的 HTTP 請求頭。@Consumes
註釋針對 Content-Type
請求頭進行匹配,以決定方法是否能接受給定請求的內容。
在清單 9 中,application/json
的 Content-Type
頭再加上對路徑 /contacts
的 POST,表明我們的 ContactsResource
類內的 addContactInfo
方法將會被調用以處理請求。
清單 9. Content-Type 頭部的使用
|
相反地,@Produces
註釋被針對 Accept
請求頭進行匹配以決定客戶機是否能夠處理由給定方法返回的表示。
清單 10. Accept 頭部的使用
|
在清單 10 中,對 /contacts/[email protected] 的 GET
請求表明了 getByEmailAddress
方法將會被調用並且返回的格式將會是application/json
,而非 text/xml。
Providers
JAX-RS 提供程序是一些應用程序組件,允許在三個關鍵領域進行運行時行爲的定製:數據綁定、異常映射以及上下文解析(比如,向運行時提供 JAXBContext 實例)。每個 JAX-RS 提供程序類必須由 @Provider
註釋。如下的例子討論了兩個數據綁定提供程序MessageBodyWriter
和 MessageBodyReader
。
MessageBodyWriter
MessageBodyWriters 被 JAX-RS 運行時用來序列化所返回資源的表示。遵從 JSR-311 的運行時提供了對常見類型(java.lang.String、java.io.InputStream、 JAXB 對象等)的本機支持,但用戶也可以向 JAX-RS 運行時提供他或她自己的 MessageBodyWriter。比如,您可以提供一個定製 MessageBodyWriter
來處理定製 ContactInfo
Java 類型,如下所示。
清單 11. 定製 MessageBodyWriter
|
ContactInfoWriter
則在所返回的資源表示被序列化之前由 JAX-RS 運行時調用。如果 isWriteable
返回 true 且 @Produces
是此資源方法的 @Produces
值最爲接近的匹配,就會調用 writeTo
方法。在這裏,ContactInfoWriter
負責向底層的OutputStream
序列化 ContactInfo
實例的內容。
MessageBodyReader
MessageBodyReaders 則與 MessageBodyWriters 相反。對於反序列化,JAX-RS 運行時支持與序列化相同的類型。用戶也可以提供他或她自己的 MessageBodyReader 實現。MessageBodyReader 的最主要的功能是讀取請求 InputStream
並將傳入的字節反序列化到一個此資源方法期望的 Java 對象。ContactInfo
類型的 MessageBodyReader
可以類似於清單 12。
package com.ibm.jaxrs.sample.organization;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
@Provider
@Consumes("text/xml")
public class ContactInfoReader implements MessageBodyReader<ContactInfo> {
public boolean isReadable(java.lang.Class<ContactInfo> type,
java.lang.reflect.Type genericType, java.lang.annotation.Annotation[]
annotations, MediaType mediaType) {
return true;
}
public ContactInfo readFrom(java.lang.Class<ContactInfo> type,
java.lang.reflect.Type genericType, java.lang.annotation.Annotation[]
annotations, MediaType mediaType, MultivaluedMap<
java.lang.String,java.lang.String> httpHeaders, java.io.InputStream
entityStream) {
return ContactInfo.parse(entityStream);
}
}
與 MessageBodyWriter isWriteable
類似,ContactInfoReader
的 isReadable
方法將被調用以便決定 MessageBodyReader 能否處理此輸入。如果 isReadable
返回 true 且 @Consumes
值與此資源方法的 @Consumes
值最爲匹配,就會選擇ContactInfoReader
。當 readFrom
方法被調用時,結果會是基於請求 InputStream
的內容創建 ContactInfo
實例。
配置
至此,我們探討了 JAX-RS 資源類和一些提供程序類(MessageBodyReaders 和 MessageBodyWriters)。那麼,該如何在 JAX-RS 運行時內配置這些類呢?這可以通過擴展 javax.ws.rs.core.Application 類實現。此類提供了一組類或一組單例(singleton)對象實例,在一個 JAX-RS 應用程序內包括所有的 根級別的資源和提供程序(由 @Provider
註釋的類)。若爲這個示例聯繫信息應用程序擴展這個 Application 類,它應該類似於清單 13。
清單 13. ContactInfoApplication
|
getClasses
方法爲 JAX-RS 運行時提供了一組可用於元數據的類。請注意,getSingletons
方法什麼都不返回。通常而言,將 JAX-RS 提供程序視爲單例是沒有問題的,但將一個 JAX-RS 資源視爲單例則要格外謹慎。常被 JAX-RS 資源類使用的基於註釋的注入可能在一個單例實例的情況內並不受支持。因此,除非仔細計劃,否則應該避免使用 JAX-RS 資源的單例實例。
假設,您正在一個 servlet 容器內部署一個 JAX-RS 應用程序,有兩種方法可以向 JAX-RS 運行時註冊您的 javax.ws.rs.core.Application 子類。這是由 WAR 文件的 web.xml 處理的,如下所示。
清單 14. 不能感知 JAX-RS 的 servlet 容器
|
在一個被認爲是不能感知 JAX-RS 的 servlet 容器內,應該作爲 servlet 定義內的 init-param 提供 Application 子類名。init-param 的名字必須是 javax.ws.rs.Application。servlet 類則很可能是 JAX-RS 運行時系統 servlet。您可以列出每個可能的 URL 模式,或者使用 /*
通配符註冊,如下所示。
清單 15. 能感知 JAX-RS 的 servlet 容器
|
在一個被認爲是能感知 JAX-RS 的 servlet 容器內,必須作爲 servlet 定義內的 servlet 類元素的值提供 Application 子類名。您仍然可以選擇是列出每個可能的 URL 模式還是使用 /*
通配符註冊。
以 Apache Wink 作爲運行時的 JAX-RS
下一步是找到一個能夠支持 JAX-RS 內的可用功能的運行時。Apache Wink 項目就提供了一個能滿足這種要求的運行時,具有上面所述的所有特性(參見 參考資料)。起初,Wink 是由開源社區的多個廠商和成員發起的一個協作項目。該項目的目的是提供最爲靈活和輕量級的運行時。
除了標準 JAX-RS 特性之外,Wink 還提供了對 JSON、Atom 和 RSS 序列化格式的增強支持。JAX-RS 本身並不提供客戶機 API,但 Wink 包括了其對客戶機 API 的自身模型,並且是完全以資源爲中心的。
爲了簡化基於 Wink 的服務的開發,可以下載 Wink 1.0 庫並將它們作爲默認 JAX-RS 庫包括到 Rational Application Developer (RAD) 7.5.5 開發環境(參見 參考資料)中。在這個更新版本中,RAD 添加了一個 JAX-RS facet,可供您進行配置以支持驗證器和註釋幫助。這個新的 facet 還能通過自動生成所需的 servlet 項和映射來簡化 servlet 的配置。
結束語
與傳統的 servlet 模型相比,JAX-RS 提供了一種可行的、更爲簡便、移植性更好的方式來在 Java 內實現 RESTful 服務。使用註釋讓您能夠輕鬆提供 Java 資源的路徑位置並將 Java 方法綁定到 HTTP 請求方法。一種可移植的數據綁定架構提供了一些本機的 Java 類型支持並允許進行序列化/反序列化處理的完全定製。javax.ws.rs.core. Application 子類的擴展以及 web.xml 內的相應清單表明了用最少的部署描述符配置就能進行輕鬆部署。
本文只涉及了 JAX-RS 所能提供功能的一部分。就提供應用程序上下文(比如 JAXBContext 實例)並將運行時異常映射給 HTTP 請求而言,其他兩個 JAX-RS 提供程序類型 ContextResolvers
和 ExceptionMappingProviders
還能提供對應用程序組件的進一步控制。註釋的定義是爲了控制方法參數和類成員的注入,它們在運行時的整個過程嚮應用程序提供了有價值的上下文信息。總的來說,JAX-RS 必將是一種面向基於 Java 的 RESTful 服務開發的簡便、可移植的、全面的 API。
使用 JAX-RS 簡化 REST 應用開發 :https://www.ibm.com/developerworks/cn/java/j-lo-jaxrs/#icomments