?
原文鏈接: http://www.ibm.com/developerworks/cn/java/j-lo-jaxrs/index.html
?
?
REST 是英文 Representational State Transfer 的縮寫,有中文翻譯為“具象狀態(tài)傳輸”。REST 這個(gè)術(shù)語是由 Roy Fielding 在他的博士論文 《 Architectural Styles and the Design of Network-based Software Architectures 》 中提出的。REST 并非標(biāo)準(zhǔn),而是一種開發(fā) Web 應(yīng)用的架構(gòu)風(fēng)格,可以將其理解為一種設(shè)計(jì)模式。REST 基于 HTTP,URI,以及 XML 這些現(xiàn)有的廣泛流行的協(xié)議和標(biāo)準(zhǔn),伴隨著 REST,HTTP 協(xié)議得到了更加正確的使用。
相較于基于 SOAP 和 WSDL 的 Web 服務(wù),REST 模式提供了更為簡(jiǎn)潔的實(shí)現(xiàn)方案。目前,越來越多的 Web 服務(wù)開始采用 REST 風(fēng)格設(shè)計(jì)和實(shí)現(xiàn),真實(shí)世界中比較著名的 REST 服務(wù)包括: Google AJAX 搜索 API 、 Amazon Simple Storage Service (Amazon S3) 等。
基于 REST 的 Web 服務(wù)遵循一些基本的設(shè)計(jì)原則:
- 系統(tǒng)中的每一個(gè)對(duì)象或是資源都可以通過一個(gè)唯一的 URI 來進(jìn)行尋址,URI 的結(jié)構(gòu)應(yīng)該簡(jiǎn)單、可預(yù)測(cè)且易于理解,比如定義目錄結(jié)構(gòu)式的 URI。
-
以遵循 RFC-2616 所定義的協(xié)議的方式顯式地使用 HTTP 方法,建立創(chuàng)建、檢索、更新和刪除(CRUD:Create, Retrieve, Update and Delete)操作與 HTTP 方法之間的一對(duì)一映射:
- 若要在服務(wù)器上創(chuàng)建資源,應(yīng)該使用 POST 方法;
- 若要檢索某個(gè)資源,應(yīng)該使用 GET 方法;
- 若要更改資源狀態(tài)或?qū)ζ溥M(jìn)行更新,應(yīng)該使用 PUT 方法;
- 若要?jiǎng)h除某個(gè)資源,應(yīng)該使用 DELETE 方法。
- URI 所訪問的每個(gè)資源都可以使用不同的形式加以表示(比如 XML 或者 JSON),具體的表現(xiàn)形式取決于訪問資源的客戶端,客戶端與服務(wù)提供者使用一種內(nèi)容協(xié)商的機(jī)制(請(qǐng)求頭與 MIME 類型)來選擇合適的數(shù)據(jù)格式,最小化彼此之間的數(shù)據(jù)耦合。
JAX-RS -- Java API for RESTful Web Services
Java EE 6 引入了對(duì) JSR-311 的支持。JSR-311(JAX-RS:Java API for RESTful Web Services)旨在定義一個(gè)統(tǒng)一的規(guī)范,使得 Java 程序員可以使用一套固定的接口來開發(fā) REST 應(yīng)用,避免了依賴于第三方框架。同時(shí),JAX-RS 使用 POJO 編程模型和基于標(biāo)注的配置,并集成了 JAXB,從而可以有效縮短 REST 應(yīng)用的開發(fā)周期。
JAX-RS 定義的 API 位于 javax.ws.rs 包中,其中一些主要的接口、標(biāo)注和抽象類如 圖 1 所示。
圖 1. javax.ws.rs 包概況
JAX-RS 的具體實(shí)現(xiàn)由第三方提供,例如 Sun 的參考實(shí)現(xiàn) Jersey、Apache 的 CXF 以及 JBoss 的 RESTEasy。
在接下來的文章中,將結(jié)合一個(gè)記賬簿應(yīng)用向讀者介紹 JAX-RS 一些關(guān)鍵的細(xì)節(jié)。
記賬簿示例應(yīng)用程序中包含了 3 種資源:賬目、用戶以及賬目種類,用戶與賬目、賬目種類與賬目之間都是一對(duì)多的關(guān)系。記賬簿實(shí)現(xiàn)的主要功能包括:
- 記錄某用戶在什么時(shí)間花費(fèi)了多少金額在哪個(gè)種類上
- 按照用戶、賬目種類、時(shí)間或者金額查詢記錄
- 對(duì)用戶以及賬目種類的管理
Web 資源作為一個(gè) Resource 類來實(shí)現(xiàn),對(duì)資源的請(qǐng)求由 Resource 方法來處理。Resource 類或 Resource 方法被打上了 Path 標(biāo)注,Path 標(biāo)注的值是一個(gè)相對(duì)的 URI 路徑,用于對(duì)資源進(jìn)行定位,路徑中可以包含任意的正則表達(dá)式以匹配資源。和大多數(shù) JAX-RS 標(biāo)注一樣,Path 標(biāo)注是可繼承的,子類或?qū)崿F(xiàn)類可以繼承超類或接口中的 Path 標(biāo)注。
Resource 類是 POJO,使用 JAX-RS 標(biāo)注來實(shí)現(xiàn)相應(yīng)的 Web 資源。Resource 類分為根 Resource 類和子 Resource 類,區(qū)別在于子 Resource 類沒有打在類上的 Path 標(biāo)注。Resource 類的實(shí)例方法打上了 Path 標(biāo)注,則為 Resource 方法或子 Resource 定位器,區(qū)別在于子 Resource 定位器上沒有任何 @GET、@POST、@PUT、@DELETE 或者自定義的 @HttpMethod。 清單 1 展示了示例應(yīng)用中使用的根 Resource 類及其 Resource 方法。
清單 1. 根 Resource 類
@Path("/") public class BookkeepingService { ...... @Path("/person/") @POST @Consumes("application/json") public Response createPerson(Person person) { ...... } @Path("/person/") @PUT @Consumes("application/json") public Response updatePerson(Person person) { ...... } @Path("/person/{id:\\d+}/") @DELETE public Response deletePerson(@PathParam("id") int id) { ...... } @Path("/person/{id:\\d+}/") @GET @Produces("application/json") public Person readPerson(@PathParam("id") int id) { ...... } @Path("/persons/") @GET @Produces("application/json") public Person[] readAllPersons() { ...... } @Path("/person/{name}/") @GET @Produces("application/json") public Person readPersonByName(@PathParam("name") String name) { ...... } ...... |
JAX-RS 中涉及 Resource 方法參數(shù)的標(biāo)注包括:@PathParam、@MatrixParam、@QueryParam、@FormParam、@HeaderParam、@CookieParam、@DefaultValue 和 @Encoded。這其中最常用的是 @PathParam,它用于將 @Path 中的模板變量映射到方法參數(shù),模板變量支持使用正則表達(dá)式,變量名與正則表達(dá)式之間用分號(hào)分隔。例如對(duì) 清單 1 中所示的 BookkeepingService 類,如果使用 Get 方法請(qǐng)求資源”/person/jeffyin”,則 readPersonByName 方法將被調(diào)用,方法參數(shù) name 被賦值為”jeffyin”;而如果使用 Get 方法請(qǐng)求資源”/person/123”,則 readPerson 方法將被調(diào)用,方法參數(shù) id 被賦值為 123。要了解如何使用其它的參數(shù)標(biāo)注 , 請(qǐng)參考 JAX-RS API 。
JAX-RS 規(guī)定 Resource 方法中只允許有一個(gè)參數(shù)沒有打上任何的參數(shù)標(biāo)注,該參數(shù)稱為實(shí)體參數(shù),用于映射請(qǐng)求體。例如 清單 1中所示的 BookkeepingService 類的 createPerson 方法和 updatePerson 方法的參數(shù) person。
Resource 方法合法的參數(shù)類型包括:
- 原生類型
- 構(gòu)造函數(shù)接收單個(gè)字符串參數(shù)或者包含接收單個(gè)字符串參數(shù)的靜態(tài)方法 valueOf 的任意類型
- List<T>,Set<T>,SortedSet<T>(T 為以上的 2 種類型)
- 用于映射請(qǐng)求體的實(shí)體參數(shù)
Resource 方法合法的返回值類型包括:
- void:狀態(tài)碼 204 和空響應(yīng)體
- Response:Response 的 status 屬性指定了狀態(tài)碼,entity 屬性映射為響應(yīng)體
- GenericEntity:GenericEntity 的 entity 屬性映射為響應(yīng)體,entity 屬性為空則狀態(tài)碼為 204,非空則狀態(tài)碼為 200
- 其它類型:返回的對(duì)象實(shí)例映射為響應(yīng)體,實(shí)例為空則狀態(tài)碼為 204,非空則狀態(tài)碼為 200
對(duì)于錯(cuò)誤處理,Resource 方法可以拋出非受控異常 WebApplicationException 或者返回包含了適當(dāng)?shù)腻e(cuò)誤碼集合的 Response 對(duì)象。
通過 Context 標(biāo)注,根 Resource 類的實(shí)例字段可以被注入如下類型的上下文資源:
- Request、UriInfo、HttpHeaders、Providers、SecurityContext
- HttpServletRequest、HttpServletResponse、ServletContext、ServletConfig
要了解如何使用第 1 種類型的上下文資源 , 請(qǐng)參考 JAX-RS API 。
JAX-RS 定義了 @POST、@GET、@PUT 和 @DELETE,分別對(duì)應(yīng) 4 種 HTTP 方法,用于對(duì)資源進(jìn)行創(chuàng)建、檢索、更新和刪除的操作。
POST 標(biāo)注用于在服務(wù)器上創(chuàng)建資源,如 清單 2 所示。
清單 2. POST 標(biāo)注
@Path("/") public class BookkeepingService { ...... @Path("/account/") @POST @Consumes("application/json") public Response createAccount(Account account) { ...... } ...... |
如果使用 POST 方法請(qǐng)求資源”/account”,則 createAccount 方法將被調(diào)用,JSON 格式的請(qǐng)求體被自動(dòng)映射為實(shí)體參數(shù) account。
GET 標(biāo)注用于在服務(wù)器上檢索資源,如 清單 3 所示。
清單 3. GET 標(biāo)注
@Path("/") public class BookkeepingService { ...... @Path("/person/{id}/accounts/") @GET @Produces("application/json") public Account[] readAccountsByPerson(@PathParam("id") int id) { ...... } ...... @Path("/accounts/{beginDate:\\d{4}-\\d{2}-\\d{2}},{endDate:\\d{4}-\\d{2}-\\d{2}}/") @GET @Produces("application/json") public Account[] readAccountsByDateBetween(@PathParam("beginDate") String beginDate, @PathParam("endDate") String endDate) throws ParseException { ...... } ...... |
如果使用 GET 方法請(qǐng)求資源”/person/123/accounts”,則 readAccountsByPerson 方法將被調(diào)用,方法參數(shù) id 被賦值為 123,Account 數(shù)組類型的返回值被自動(dòng)映射為 JSON 格式的響應(yīng)體;而如果使用 GET 方法請(qǐng)求資源”/accounts/2008-01-01,2009-01-01”,則 readAccountsByDateBetween 方法將被調(diào)用,方法參數(shù) beginDate 被賦值為”2008-01-01”,endDate 被賦值為”2009-01-01”,Account 數(shù)組類型的返回值被自動(dòng)映射為 JSON 格式的響應(yīng)體。
PUT 標(biāo)注用于更新服務(wù)器上的資源,如 清單 4 所示。
清單 4. PUT 標(biāo)注
@Path("/") public class BookkeepingService { ...... @Path("/account/") @PUT @Consumes("application/json") public Response updateAccount(Account account) { ...... } ...... |
如果使用 PUT 方法請(qǐng)求資源”/account”,則 updateAccount 方法將被調(diào)用,JSON 格式的請(qǐng)求體被自動(dòng)映射為實(shí)體參數(shù) account。
DELETE 標(biāo)注用于刪除服務(wù)器上的資源,如 清單 5 所示。
清單 5. DELETE 標(biāo)注
@Path("/") public class BookkeepingService { ...... @Path("/account/{id:\\d+}/") @DELETE public Response deleteAccount(@PathParam("id") int id) { ...... } ...... |
如果使用 DELETE 方法請(qǐng)求資源”/account/323”,則 deleteAccount 方法將被調(diào)用,方法參數(shù) id 被賦值為 323。
Web 資源可以有不同的表現(xiàn)形式,服務(wù)端與客戶端之間需要一種稱為內(nèi)容協(xié)商(Content Negotiation)的機(jī)制:作為服務(wù)端,Resource 方法的 Produces 標(biāo)注用于指定響應(yīng)體的數(shù)據(jù)格式(MIME 類型),Consumes 標(biāo)注用于指定請(qǐng)求體的數(shù)據(jù)格式;作為客戶端,Accept 請(qǐng)求頭用于選擇響應(yīng)體的數(shù)據(jù)格式,Content-Type 請(qǐng)求頭用于標(biāo)識(shí)請(qǐng)求體的數(shù)據(jù)格式。
JAX-RS 依賴于 MessageBodyReader 和 MessageBodyWriter 的實(shí)現(xiàn)來自動(dòng)完成返回值到響應(yīng)體的序列化以及請(qǐng)求體到實(shí)體參數(shù)的反序列化工作,其中,XML 格式的請(qǐng)求/響應(yīng)數(shù)據(jù)與 Java 對(duì)象的自動(dòng)綁定依賴于 JAXB 的實(shí)現(xiàn)。
用戶可以使用 Provider 標(biāo)注來注冊(cè)使用自定義的 MessageBodyProvider,如 清單 6 所示,GsonProvider 類使用了 Google Gson 作為 JSON 格式的 MessageBodyProvider 的實(shí)現(xiàn)。
清單 6. GsonProvider
@Provider @Produces("application/json") @Consumes("application/json") public class GsonProvider implements MessageBodyWriter<Object>, MessageBodyReader<Object> { private final Gson gson; public GsonProvider() { gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setDateFormat( "yyyy-MM-dd").create(); } public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return true; } public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { return gson.fromJson(new InputStreamReader(entityStream, "UTF-8"), type); } public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return true; } public long getSize(Object obj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return -1; } public void writeTo(Object obj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { entityStream.write(gson.toJson(obj, type).getBytes("UTF-8")); } } |
由于 JAX-RS 和 JPA 同樣都使用了基于 POJO 和標(biāo)注的編程模型,因而很易于結(jié)合在一起使用。示例應(yīng)用中的 Web 資源 ( 如賬目 ) 同時(shí)也是持久化到數(shù)據(jù)庫(kù)中的實(shí)體,同一個(gè) POJO 類上既有 JAXB 的標(biāo)注,也有 JPA 的標(biāo)注 ( 或者還有 Gson 的標(biāo)注 ) ,這使得應(yīng)用中類的個(gè)數(shù)得以減少。如 清單 7 所示,Account 類可以在 JAX-RS 與 JPA 之間得到復(fù)用,它不但可以被 JAX-RS 綁定為請(qǐng)求體 / 響應(yīng)體的 XML/JSON 數(shù)據(jù),也可以被 JPA 持久化到關(guān)系型數(shù)據(jù)庫(kù)中。
清單 7. Account
@Entity @Table(name = "TABLE_ACCOUNT") @XmlRootElement public class Account { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "COL_ID") @Expose private int id; @ManyToOne @JoinColumn(name = "COL_PERSON") @Expose private Person person; @Column(name = "COL_AMOUNT") @Expose private BigDecimal amount; @Column(name = "COL_DATE") @Expose private Date date; @ManyToOne @JoinColumn(name = "COL_CATEGORY") @Expose private Category category; @Column(name = "COL_COMMENT") @Expose private String comment; ...... |
REST 作為一種輕量級(jí)的 Web 服務(wù)架構(gòu)被越來越多的開發(fā)者所采用,JAX-RS 的發(fā)布則規(guī)范了 REST 應(yīng)用開發(fā)的接口。本文首先闡述了 REST 架構(gòu)的基本設(shè)計(jì)原則,然后通過一個(gè)示例應(yīng)用展示了 JAX-RS 是如何通過各種標(biāo)注來實(shí)現(xiàn)以上的設(shè)計(jì)原則的,最后還介紹了 JAX-RS 與 JPA、Gson 的結(jié)合使用。本文的示例應(yīng)用使用了 Jersey 和 OpenJPA,部署在 Tomcat 容器上,替換成其它的實(shí)現(xiàn)只需要修改 web.xml 和 persistence.xml 配置文件。
<!-- CMA ID: 426622 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-article-6.0-beta.xsl -->本文示例代碼 | Bookkeeping.zip | 24 KB | HTTP |
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
