使用 Apache Wink、Eclipse 和 Maven 開發(fā) RESTful Web 服務(wù)
Apache Wink 是一個促進創(chuàng)建和使用 REST Web 服務(wù)的 Apache 孵化器項目。通過 REST Web 服務(wù),客戶機和服務(wù)之間的交互局限于一組預(yù)定義的操作,客戶機和服務(wù)器之間的交互的復(fù)雜性限制為客戶機和服務(wù)之間交換的資源表示。這種方法支持構(gòu)建可互操作、可伸縮、可靠的、基于 REST 的分布式超媒體系統(tǒng)。
常用縮略詞
- API: 應(yīng)用程序編程接口
- HTTP: 超文本傳輸協(xié)議
- IDE: 集成開發(fā)環(huán)境
- JSON: JavaScript 對象符號
- REST: 具象狀態(tài)傳輸
- URI: 統(tǒng)一資源標識符
- XML: 可擴展標記語言
本文介紹如何使用 Apache Wink、Eclipse IDE 以及 Maven 項目管理工具開發(fā)、部署和運行 RESTful Web 服務(wù)。
設(shè)計 Web 服務(wù)的 REST 方法將客戶機和服務(wù)之間的交互限制到一組創(chuàng)建、讀取、更新和刪除(CRUD)操作。這些操作直接映射到 HTTP 方法 — 具體而言,映射到 POST
、GET
、 PUT
和 DELETE
。盡管 RESTful 樣式?jīng)]有綁定到 HTTP 協(xié)議,本文假設(shè) HTTP 用于客戶機和服務(wù)之間的通信。
REST Web 服務(wù)在資源上執(zhí)行 CRUD 操作。客戶機使用資源狀態(tài)的 REST 服務(wù)表示進行交換。這些表示使用的數(shù)據(jù)格式在 HTTP 請求或響應(yīng)的頭部中指定 — XML 和 JSON 是廣泛使用的格式。數(shù)據(jù)格式可能在不同操作之間發(fā)生變化;例如,創(chuàng)建資源的數(shù)據(jù)格式與用于讀取資源的數(shù)據(jù)格式不同。REST 服務(wù)保持資源的狀態(tài),但 — 與 servlets 不同的是 — 不保持客戶機會話信息。
REST 方法支持構(gòu)建可互操作、可伸縮和可靠的基于 REST 的分布式系統(tǒng)。例如,GET
、POST
和 DELETE
方法是等冪的,即多次執(zhí)行它們與執(zhí)行一次的結(jié)果相同。由于 GET
操作不會更改資源的狀態(tài),因此 GET
請求的結(jié)果可以緩存起來以加快 “請求-響應(yīng)” 循環(huán)。
JAX-RS 為基于 HTTP 協(xié)議的 RESTful Java Web 服務(wù)定義了一個 API。JAX-RS 實現(xiàn)包括 Apache Wink、Sun Jersey 和 JBoss RESTEasy。本文將使用 Apache Wink。
JAX-RS 利用 Java 注釋的威力,使用注釋來執(zhí)行諸如以下的操作:
- 將 HTTP 方法和 URIs 綁定到 Java 類的方法
- 將來自 URI 或 HTTP 頭部的元素作為方法參數(shù)注入
- 在 HTTP 消息體和 Java 類型之間來回轉(zhuǎn)換
- 將 URI 模式綁定到 Java 類和方法 —
@Path
注釋 - 將 HTTP 操作綁定到 Java 方法 —
@GET
、@POST
、@PUT
和@DELETE
注釋
JAX-RS 還提供了一個框架來構(gòu)建新功能。例如,對于自定義數(shù)據(jù)格式,程序員可以開發(fā)消息閱讀器并將 Java 對象編組到 HTTP 消息并從 HTTP 消息解組它們。
在本文中,您將使用 Eclipse 和 Maven 下載 Apache Wink,運行 Apache Wink 中包含的 HelloWorld 示例,然后將您自己的 REST Web 服務(wù)創(chuàng)建為一個 Eclipse 項目。
在這個小節(jié)中,您將使用 Eclipse 以及 Maven Integration for Eclipse(稱為 m2eclipse)和 Subclipse 插件來安裝 Apache Wink。(M2eclipse 提供從 Eclipse 對 Maven 的訪問;Subclipse 提供對 Subversion 資源庫的訪問。)您還可以將 Eclipse 用作一個平臺,從這個平臺構(gòu)建并運行 Web 服務(wù)。
在獲取 Apache Wink 之前,要先下載并安裝以下軟件包(參見 參考資料 獲取下載 URLs):
- Java Software Development Kit (JDK) version 6。設(shè)置 JAVA_HOME 環(huán)境變量并添加到路徑 %JAVA_HOME%\bin(在 Windows® 中)或 $JAVA_HOME/bin(在 Linux® 中)。
- Apache Tomcat version 6.0。設(shè)置 CATALINA_HOME 環(huán)境變量以指向安裝目錄。
- Eclipse IDE for Java™ Platform, Enterprise Edition (Java EE) developers。本文撰寫之時的當前版本為 Eclipse Galileo。
要使用 Eclipse 管理具有 Maven 感知的項目,要安裝 Eclipse 插件 Subclipse 和 m2eclipse。要安裝 Subclipse 插件,執(zhí)行以下步驟:
- 啟動 Eclipse。
- 單擊菜單欄中的 Help,然后選擇 Install new software。
- 在 Available Software 窗口中,單擊 Add。
- 在 Add Site 窗口中,輸入:
Name: Subclipse Location: http://subclipse.tigris.org/update_1.6.x/
然后單擊 OK。
- 在 Available Software 窗口中,選擇 Subclipse 下的 Subclipse (Required) 和 SVNKit Client Adapter (Not required) 復(fù)選框(如 圖 1 所示),然后單擊 Next。
圖 1. 安裝 Subclipse 插件 - 在 Install Details 窗口中,單擊 Next。
- 在 Review Licenses 窗口中,檢查許可,接受許可協(xié)議條款,然后單擊 Finish。
- 單擊 Yes 重新啟動 Eclipse。
m2eclipse 插件的安裝步驟與安裝 Subclipse 插件類似,但有以下幾點例外:
- 在 Add Site 窗口中輸入:
Name: Maven Integration for Eclipse Location: http://m2eclipse.sonatype.org/update/
然后單擊 OK。
- 在 Available Software 窗口中,選擇 Maven Integration for Eclipse (Required) 和 Maven SCM handler for Subclipse (Optional) 復(fù)選框(如 圖 2 所示),然后單擊 Next。
圖 2. 安裝 m2eclipse 插件
現(xiàn)在,您可以使用 Eclipse 從資源庫檢查 Apache Wink 示例,將必要的 Java 歸檔(JAR)文件(包括 Apache Wink JAR 文件)下載到一個本地資源庫,構(gòu)建并運行 Apache Wink HelloWorld 示例。為此,執(zhí)行以下步驟:
- 在 Eclipse 中,選擇 File > Import 啟動 Import Wizard。
- 在 Select 向?qū)ы撁娴?nbsp;Select and import source 文本框中輸入
maven
。 - 在 Maven 下,選擇 Materialize Maven Projects 并單擊 Next。
- 在 Select Maven artifacts 向?qū)ы撁嫔蠁螕?nbsp;Add。
- 在 Add Dependency 頁面上的 Enter groupId, artifactId 文本框中輸入
org.apache.wink.example
。
注意: Artifact(工件) 是一個用于 Maven 的術(shù)語,指的是設(shè)置了版本并存儲在資源庫中的軟件包的層級結(jié)構(gòu)。 - 在 Search Results 區(qū)域中,選擇 org.apache.wink.example apps(如 圖 3 所示)并單擊 OK。
圖 3. org.apache.wink.example 組中的應(yīng)用程序工件 - 在 Select Maven artifacts 向?qū)ы撁嫔希瑔螕?nbsp;Next,然后單擊 Finish。
- 在 Maven Projects 向?qū)ы撁嫔希贿x擇 /pom.xml 復(fù)選框,然后單擊 Finish。
Maven 處理一個工件的所有依賴項的方式是從遠程資源庫下載它們并構(gòu)建一個本地資源庫。Maven 的優(yōu)勢之一是能夠處理臨時依賴項;因此,在 Maven Project Object Model (POM) 文件中,只需聲明工件的傳遞依賴項(transitive dependencies),Maven 將為您處理高階依賴項(higher-order dependencies)。
步驟 8 完成后,將創(chuàng)建一個 Eclipse 項目,它包含 Apache Wink 示例的 apps
模塊中的代碼。在 Eclipse 的 Project Explorer 中瀏覽項目文件。
我們來檢查一下 apps
模塊中的 HelloWorld
Java 類。在 Project Explorer 視圖中,單擊 apps > HelloWorld > src > main,然后打開文件 HelloWorld.java,該文件的結(jié)構(gòu)如 清單 1 所示。
清單 1. HelloWorld.java 文件
package org.apache.wink.example.helloworld; ... @Path("/world") public class HelloWorld { public static final String ID = "helloworld:1"; @GET @Produces(MediaType.APPLICATION_ATOM_XML) public SyndEntry getGreeting() { SyndEntry synd = new SyndEntry(new SyndText("Hello World!"), ID, new Date()); return synd; } } |
HelloWorld 是一個 JAX-RS 資源(或服務(wù)),正如其類定義前面的 @Path("/world")
注釋所示。字符串 "/world"
是該資源的相對根 URI。JAX-RS 將匹配相對 URI "/world"
的 HTTP 請求路由到 HelloWorld
類的方法。
HelloWorld
類的惟一方法 — getGreeting
— 擁有一個 @GET
注釋,表示它將服務(wù)于 HTTP GET
請求。
@Produces(MediaType.APPLICATION_ATOM_XML)
注釋表明 HTTP 響應(yīng)的媒體類型 — 即,HTTP 響應(yīng)頭部中的 Content-Type
字段的值。
與相對 URI "/world"
關(guān)聯(lián)的絕對 URI 由部署描述符 web.xml
中的設(shè)置決定,這個部署描述符駐留在 HelloWorld/src/main/webapp/WEB-INF 中,如 圖 4 所示。
圖 4. HelloWorld 應(yīng)用程序的部署描述符

注意,URI 模式 /rest/*
被綁定到 restSdkService
servlet,這個 servlet 的 context path 是 HelloWorld
,由 HelloWorld/pom.xml 文件中的 <finalName>
元素定義。這樣,HelloWorld
資源的絕對 URI 是:
http://host:port/HelloWorld/rest/world |
圖 4 中的 servlet 名稱 restSdkService
引用 org.apache.wink.server.internal.servlet.RestServlet
Apache Wink 類,如圖中的<servlet>
元素所示。RestServlet
類使用初始參數(shù) applicationConfigLocation
傳遞 HelloWorld
類的名稱,這個初始參數(shù)反過來指向位于 HelloWorld/src/main/webapp/WEB-INF 文件夾中的 web.xml 旁邊的 application 文件。這個 application 文件只有一行,即HelloWorld
資源的限定名:
org.apache.wink.example.helloworld.HelloWorld |
RestServlet
servlet 可以在 “無 JAX-RS 感知” 的 servlet 容器中運行,從而支持將 JAX-RS 服務(wù)輕松部署到 “無 JAX-RS 感知” 的容器中。
我們現(xiàn)在開始構(gòu)建 HelloWorld 服務(wù),然后將其部署到 Tomcat 6.0 servlet 容器中并運行。
在 Eclipse 中,可以使用 m2eclipse 插件來構(gòu)建、部署和運行 HelloWorld。首先,編譯源代碼并為 HelloWorld 服務(wù)構(gòu)建 Web 歸檔 (WAR) 文件。要構(gòu)建這個服務(wù),指示 m2eclipse 執(zhí)行安裝生命周期階段。(執(zhí)行一個 Maven 生命周期階段將觸發(fā)項目生命周期中的此前階段的執(zhí)行。)要執(zhí)行安裝階段,在 Project Explorer 視圖中右鍵單擊 HelloWorld,然后單擊 Run As > Maven install,如 圖 5所示。
圖 5. 執(zhí)行 Maven 安裝階段

Maven (從中央資源庫 http://repo1.maven.org/maven2 或從鏡像資源庫)下載所有依賴項,并在 Windows® 中的 %HOMEPATH%\.m2\repository 或 Linux® 中的 $HOME/.m2/repository 下構(gòu)建本地資源庫。Maven 執(zhí)行的動作記錄在 Eclipse 窗口的 Console 視圖中。
如果安裝階段成功結(jié)束,那么 WAR 文件 HelloWorld.war 就構(gòu)建在目標目錄下并部署到本地資源庫中,Console 視圖將顯示消息 “Build successful”。
您將使用 Maven 的 Tomcat 插件。(與 Eclipse 一樣,Maven 通過插件提供大量功能。)您必須將 Tomcat 插件的位置告知 Maven,方法是指定插件的 groupId
和 artifactId
,如 圖 6 所示(pom.xml 中的 45-46 行)。
圖 6. Tomcat Maven 插件

您還需要告知 Maven 用于訪問 Tomcat manager 應(yīng)用程序的用戶名和密碼。Maven 使用 manager 應(yīng)用程序來指導(dǎo) Tomcat 部署或取消部署一個 Web 應(yīng)用程序。按照以下步驟告知 Maven 身份驗證信息:
- 聲明
tomcat-maven-plugin
工件的一個 Tomcat 服務(wù)器配置 — 稱為tomcat-localhost
,如 圖 6 所示(pom.xml 中的 47-49 行) - 在 Maven 設(shè)置文件 settings.xml(位于 Windows 的 %HOMEPATH%\.m2\ 或 Linux 的 $HOME/.m2 下)中定義這個配置,如清單 2 所示。
清單 2. Maven settings.xml 文件<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <servers> <server> <id>tomcat-localhost</id> <username>admin</username> <password>admin</password> </server> </servers> </settings>
現(xiàn)在可以將這個服務(wù)部署到 Tomcat 了,方法是執(zhí)行 Tomcat 插件的 redeploy 目標。(一個 Maven 目標(goal) 被自身執(zhí)行,這與生命周期階段不同,后者的執(zhí)行由生命周期中的此前階段自動推進。)要執(zhí)行 redeploy 目標,執(zhí)行以下步驟:
- 在 Tomcat 配置文件 conf/tomcat-users.xml(位于 CATALINA_HOME 下)中插入身份驗證信息(如 圖 7 所示),但要使用一個密碼,而不是 admin。
圖 7. Tomcat 身份驗證配置 - 通過運行以下命令啟動 Tomcat:
In Windows: %CATALINA_HOME%\bin\catalina.bat start In Linux: $CATALINA_HOME/bin/catalina.sh start
- 執(zhí)行
maven tomcat:redeploy
,這條命令使用 Tomcat manager 應(yīng)用程序?qū)?HelloWorld.war 部署到 Tomcat。為此,執(zhí)行以下步驟:- 在 Project Explorer 視圖中,右鍵單擊 HelloWorld,然后單擊 Run As > Run configurations。
- 在 Create, manage, and run configurations 窗口中,選擇 Maven Build > New_configuration,然后單擊 Main 選項卡(參見 圖 8)。
圖 8. 運行 tomcat:redeploy 目標 - 單擊 Browse workspace,然后選擇 apps > HelloWorld,然后單擊 OK。
- 在 Goals 文本框中,輸入
tomcat:redeploy
,然后單擊 Run。如果 redeploy 目標成功執(zhí)行,Console 視圖將顯示消息 “Build successful”。
現(xiàn)在,您可以使用一個 HTTP 客戶機(比如 Curl)調(diào)用 HelloWorld 服務(wù),如 清單 3 所示。
清單 3. 在 Curl 中調(diào)用 HelloWorld 服務(wù)
$ curl -X GET http://localhost:8080/HelloWorld/rest/world <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <entry xmlns="http://www.w3.org/2005/Atom" xmlns:ns2="http://a9.com/-/spec/opensearch/1.1/" xmlns:ns3="http://www.w3.org/1999/xhtml"> <id>helloworld:1</id> <updated>2010-01-06T13:26:43.924+01:00</updated> <title type="text">Hello World!</title> </entry> |
在這個小節(jié)中,您將從頭開始創(chuàng)建一個 REST Web 服務(wù)來管理一個圖書資源集合。這個 REST 服務(wù)的源代碼可以從下面的 下載 表中獲取。
開發(fā)這樣一個服務(wù)的關(guān)鍵步驟是:
- 定義資源的 URIs,用于操作資源的方法,以及每個方法的數(shù)據(jù)格式。
- 定義表示圖書資源的 Java 對象,提供 Java 代碼或注釋來編組和解組 Java 對象。
- 定義將 URIs 和 HTTP 方法綁定到 Java 方法的 Java 服務(wù)。
- 提供
javax.ws.rs.core.Application
抽象類的一個具體子類。
下面我們就執(zhí)行這些步驟。
這個服務(wù)支持 GET
, POST
、PUT
和 DELETE
HTTP 方法,并按如下方式將 HTTP 請求映射到 Java 方法:
- 將
POST /books
請求映射到帶有createBook(@Context UriInfo, Book)
簽名的 Java 方法。(@Context
注釋將在 JAX-RS Web 服務(wù) 小節(jié)中介紹。)用于創(chuàng)建一個圖書資源的數(shù)據(jù)格式為:POST /books HTTP/1.1 Content-Type: application/xml <book> <title> ... </title> <isbn> .... </isbn> </book>
- 將
GET /books
請求映射到 Java 方法getBook()
。用于獲取圖書資源的數(shù)據(jù)格式為:GET /books HTTP/1.1 Content-Type: application/xml
- 將
GET /books/{id}
請求映射到帶有getBook(@PathParam("id") int)
簽名的 Java 方法。(@PathParam
注釋將在 JAX-RS Web 服務(wù) 小節(jié)中介紹。)用于獲取帶有 URI 模式/books/{id}
的圖書資源的數(shù)據(jù)格式為:GET /books/{id} HTTP/1.1 Content-Type: application/xml
- 將
PUT /books/{id}
請求映射到帶有updateBook(@PathParam("id") int, Book)
簽名的 Java 方法。用于更新帶有 URI 模式/books/{id}
的圖書資源的數(shù)據(jù)格式為:PUT /books/{id} HTTP/1.1 Content-Type: application/xml <book> <title> ... </title> <isbn> .... </isbn> </book>
- 將
DELETE /books/{id}
請求映射到帶有deleteBook(@PathParam("id") int)
簽名的 Java 方法。用于刪除帶有 URI 模式/books/{id}
的圖書資源的數(shù)據(jù)格式為:DELETE /books/{id} HTTP/1.1 Content-Type: application/xml
用于實現(xiàn)這個圖書集合的 Java 對象模型有 3 個類:
清單 4. Book 類
package com.ibm.devworks.ws.rest.books; import javax.xml.bind.annotation.*; @XmlRootElement(name="book") @XmlAccessorType(XmlAccessType.FIELD) public class Book { @XmlAttribute(name="id") private int id; @XmlElement(name="title") private String title; @XmlElement(name="isbn") private String ISBN; @XmlElement(name = "link") private Link link; public Book() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getISBN() { return ISBN; } public void setISBN(String ISBN) { this.ISBN = ISBN; } public Link getLink() { return link; } public void setLink(String uri) { this.link = new Link(uri); } } |
在 Book
類中,您使用 Java Architecture for XML Binding (JAXB) 注釋來執(zhí)行 Java 對象的編組和解組。Apache Wink 自動編組和解組帶有 JAXB 注釋的 Java 對象,包括創(chuàng)建執(zhí)行編組和解組工作的 Marshaller
和 Unmarshaller
實例。
您將使用 BookList
類來表示圖書對象的集合,BookList
類將這些對象以內(nèi)部存儲方式存儲在 ArrayList
類型的一個字段中,ArrayList
帶有 JAXB 注釋,以便轉(zhuǎn)換為 XML 格式。
清單 5. BookList 類
package com.ibm.devworks.ws.rest.books; import java.util.ArrayList; import java.util.Map; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlElementRef; @XmlRootElement(name="books") public class BookList { @XmlElementRef ArrayList<Book> books; public BookList() { } public BookList( Map<Integer, Book> bookMap ) { books = new ArrayList<Book>( bookMap.values() ); } } |
您將使用 Link
類將鏈接元素插入到圖書對象的 XML 表示中(參見 清單 6)。
清單 6. Link 類
package com.ibm.devworks.ws.rest.books; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @XmlRootElement(name="link") @XmlAccessorType(XmlAccessType.FIELD) public class Link { @XmlAttribute String href = null; @XmlAttribute String rel = "self"; public Link() { } public Link( String uri) { this.href = uri; } public Link( String href, String rel) { this.href = href; this.rel = rel; } } |
現(xiàn)在,您已經(jīng)定義了將 URIs 和 HTTP 方法綁定到 Java 方法的 Java 服務(wù)。您可以將這個服務(wù)定義為一個 Java 接口或一個類;對于前者,還需要定義實現(xiàn)接口的類。在接口中,您本地化 JAX-RS 注釋并添加一個類來實現(xiàn)接口。
清單 7 展示了 BookService
Java 接口。@javax.ws.rs.Path
注釋將這個接口指定為一個 JAX-RS 服務(wù)。注釋的 /books
值定義這個服務(wù)的相對根 URI。注釋 @javax.ws.rs.POST
、@javax.ws.rs.GET
、@javax.ws.rs.PUT
和 @javax.ws.rs.DELETE
將 HTTPPOST
、GET
、PUT
和 DELETE
操作綁定到緊隨其后的 Java 方法。
清單 7. BookService 接口
package com.ibm.devworks.ws.rest.books; import javax.ws.rs.*; import javax.ws.rs.core.*; @Path("/books") public interface BookService { @POST @Consumes(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML) public Response createBook(@Context UriInfo uriInfo, Book book); @GET @Produces(MediaType.APPLICATION_XML) public BookList getBook(); @Path("{id}") @GET @Produces(MediaType.APPLICATION_XML) public Book getBook(@PathParam("id") int id); @Path("{id}") @PUT @Consumes(MediaType.APPLICATION_XML) public void updateBook(@PathParam("id") int id, Book book_updated); @Path("{id}") @DELETE public void deleteBook(@PathParam("id") int id); } |
對于其 URI 與接口的 @PATH("/books")
注釋指定的值不同的方法,您將一個特定的 @PATH
注釋附加到那些方法。@PATH
注釋是可以累積的,即,一個路徑表達式后面的另一個路徑表達式將附加到前面的表達式。因此,綁定到 deleteBook
方法的路徑模式為/books/{id}
。
JAX-RS 提供了一種機制,用于從一個 HTTP 請求提取信息,將信息賦予一個 Java 對象,然后將該對象注入一個 Java 方法參數(shù)。這種機制稱為注入(injection)。注入使用 Java 注釋指定。在 BookService
接口中,您將插入兩個注入注釋:
@Context
注釋從 HTTP 請求提取 URI 信息,將其轉(zhuǎn)換為UriInfo
對象,然后將這個對象作為方法參數(shù)uriInfo
注入。@PathParam("id")
注釋匹配前面的@Path({"id"})
注釋。這個注釋從 URI 模式/books/{id}
提取{id}
,并將其注入getBook
方法的id
參數(shù)的值。
注意,BookService
的方法將 Java 對象(比如 Book
)接收為參數(shù)并返回 Java 對象;這是可能的,因為這些對象帶有 JAXB 注釋,因此 Apache Wink 能夠?qū)λ鼈冞M行自動編組和解組。
清單 8 展示了實現(xiàn) BookService
的類的結(jié)構(gòu)。BookServiceImpl
是一個 singleton 類,使用一個內(nèi)存映射圖(in-memory map)來維護圖書對象集合。singleton 實例使用多線程服務(wù)并發(fā)請求,因此它必須是線程安全的(thread-safe)。
清單 8. BookService 實現(xiàn)類
package com.ibm.devworks.ws.rest.books; import javax.ws.rs.*; import java.net.URI; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @Path("/books") public class BookServiceImpl implements BookService { private static BookServiceImpl instance = null; private BookServiceImpl() { } public synchronized static BookServiceImpl getInstance() { if(instance == null) { instance = new BookServiceImpl(); } return instance; } private Map <Integer, Book> bookMap = new ConcurrentHashMap <Integer, Book>(); private AtomicInteger seqNumber = new AtomicInteger(); public Response createBook(@Context UriInfo uriInfo, Book book) throws WebApplicationException { ... } public BookList getBook() { ... } public Book getBook(@PathParam("id") int id) throws WebApplicationException { ... } public void updateBook(@PathParam("id") int id, Book book_updated) throws WebApplicationException { ... } public void deleteBook(@PathParam("id") int id) throws WebApplicationException { ... } } |
圖書集合存儲在一個映射圖中。要提供對這個映射圖的線程安全訪問,您將使用一個 ConcurrentHashMap
。要生成惟一的圖書 IDs,您將使用一個 AtomicInteger
,它能確保增量操作是原子級的(atomic),從而確保線程安全。
JAX-RS Web 應(yīng)用程序包含一組資源(REST Web 服務(wù)),這些資源作為 JAX-RS 抽象類 javax.ws.rs.core.Application
的子類出現(xiàn)。清單 9 中顯示的 BookWebApp
類擴展了 Application
類。
清單 9. javax.ws.rs.core.Application 的具體子類
package com.ibm.devworks.ws.rest.books; import java.util.HashSet; import java.util.Set; import javax.ws.rs.core.Application; public class BookWebApp extends Application { private Set<Object> svc_singletons = new HashSet<Object>(); private Set<Class<?>> svc_classes = new HashSet<Class<?>>(); public BookWebApp() { svc_singletons.add(BookServiceImpl.getInstance()); } @Override public Set<Object> getSingletons() { return svc_singletons; } @Override public Set<Class<?>> getClasses() { return svc_classes; } } |
BookWebApp
的構(gòu)造函數(shù)獲取這個圖書服務(wù)實現(xiàn)對象的 singleton 實例。getSingletons
方法返回一個組,其惟一元素就是這個 singleton。JAX-RS 在服務(wù)一個請求后并不刪除這個 singleton。
您將使用 Eclipse 來創(chuàng)建一個動態(tài) Web 項目,這個項目包含上一小節(jié)介紹過的 Java 類和其他資源,比如 Web 應(yīng)用程序部署描述符和必要的 JAR 文件。
注意:創(chuàng)建一個 Eclipse 動態(tài) Web 項目的另一種方法是從一個 archetype(比如 maven-archetype-webapp
或 webapp-jee5
)創(chuàng)建一個 Maven 項目(archetype 是一個 Maven 項目模板)。但是,在 Maven 當前版本中,生成 Eclipse 使用的項目元數(shù)據(jù)時會出現(xiàn)一些問題:要支持 IDE 中的自動代碼構(gòu)建,必須手動修改一些 Eclipse 項目文件。因此,本文使用 Eclipse 動態(tài) Web 項目。
Eclipse 動態(tài) Web 項目支持開發(fā)在一個 servlet 容器中運行的 Web 應(yīng)用程序,因此它是用于開發(fā) Apache Wink 服務(wù)的一個不錯的項目模板。要為這個圖書 REST 服務(wù)創(chuàng)建一個動態(tài) Web 項目,執(zhí)行以下步驟:
- 在 Eclipse 中,單擊 File > New > Dynamic Web Project。
- 在 Dynamic Web Project Wizard 中,在 Project name 文本框中輸入
Books
。 - 單擊 Target runtime 方框旁邊的 New 創(chuàng)建一個 Tomcat 運行時。
- 在 New Server Runtime Environment 向?qū)ы撁嫔希瑔螕?nbsp;Apache > Apache Tomcat v6.0,然后單擊 Next。
- 在 Tomcat Server 向?qū)ы撁嫔希瑔螕?nbsp;Browse,然后導(dǎo)航到 先決條件 小節(jié)中定義的 CATALINA_HOME 目錄。
- 單擊 OK,然后單擊 Finish。
- 在 Dynamic Web Project 頁面上,單擊 Finish。
將上一小節(jié)中介紹的 Java 類添加到剛才創(chuàng)建的 Books 項目中。為此,執(zhí)行以下步驟:
- 在 Project Explorer 視圖中,打開 Books 文件夾。右鍵單擊 Java Resources:src,然后單擊 New > Package。
- 在 Java Package 窗口中的 Name 文本框中,輸入包名稱 —
com.ibm.devworks.ws.rest.books
— 并單擊 Finish。 - 將 Java 類添加到
com.ibm.devworks.ws.rest.books
包:- 在 Project Explorer 視圖中,右鍵單擊上述包,然后單擊 New > Class。
- 在 Name 文本框中,輸入類名稱(比如為
Book.java
輸入Book
),然后單擊 Finish。 - 使用 Eclipse Java 編輯器來創(chuàng)建類。
然后,向項目添加構(gòu)建和運行一個 Apache Wink 服務(wù)所需的 JAR 文件。當您創(chuàng)建這個應(yīng)用程序項目時,Maven 將 JAR 文件下載到本地資源庫,本地資源庫駐留在 %HOMEPATH%\.m2\repository(Windows)或 $HOME/.m2/repository(Linux)中。
要將 JAR 文件添加到這個項目,執(zhí)行以下步驟:
- 在 Project Explorer 視圖中,導(dǎo)航到 Books/Web Content/WEB-INF。
- 右鍵單擊 lib,然后單擊 Import > General > File System,然后單擊 Next。
- 在 File System 窗口中,從 Maven 資源庫(已在導(dǎo)入應(yīng)用程序 Apache Wink 模塊時由 m2eclipse 填充)導(dǎo)入 JAR 文件 jsr311-api-1.0.jar,方法是導(dǎo)航到包含這個 JAR 文件的目錄,然后單擊 OK。在 File System 窗口中,選中這個 JAR 文件,然后單擊 Finish。
- 重復(fù)步驟 2 到步驟 3,分別導(dǎo)入 JAR 文件 wink-server-1.0-incubating.jar、 wink-common-1.0-incubating.jar、slf4j-api-1.5.8.jar 和 slf4j-simple-1.5.8.jar。
所有庫都導(dǎo)入后,lib 目錄將包含如 圖 9 所示的 JAR 文件。
圖 9. 一個 Apache Wink 服務(wù)所需的庫

您還可以通過從下面的 下載 部分中提供的代碼樣例中導(dǎo)入 Book 服務(wù)來創(chuàng)建這個 Eclipse 項目。為此,下載并解壓文件,然后在 Eclipse 中單擊 File > Import > Existing Project Into Workspace。在 Import Projects Wizard 中,單擊 Browse, 導(dǎo)航到 Books 文件夾,單擊 OK,然后單擊 Finish。
在部署描述符文件 web.xml(如 清單 10 所示)中,您將帶有相對根路徑 /* 的 URIs 映射到 Wink servletorg.apache.wink.server.internal.servlet.RestServlet
。RestServlet
被傳遞為 init-param
,這是 Books 服務(wù)提供的Application
子類的名稱。
清單 10. BookService 部署服務(wù)描述符 web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>Book Web Application</display-name> <servlet> <servlet-name>restSdkService</servlet-name> <servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>com.ibm.devworks.ws.rest.books.BookWebApp</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>restSdkService</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> |
部署描述符設(shè)置好后,就可以將這個 Books 服務(wù)部署到 Tomcat 了。為了進行測試,將這個 Books 服務(wù)部署到一個由 Eclipse 托管的 Tomcat 實例中并運行。為此,執(zhí)行以下步驟:
- 在 Project Explorer 視圖中,右鍵單擊 Books,然后單擊 Run As > Run on Server。
- 在 Run on Server 窗口中,單擊 Finish,這將把這個 Books 服務(wù)部署到創(chuàng)建這個 Books 項目時配置的 Tomcat 運行時中并啟動 Tomcat。
測試完成后,您將這個 Books 服務(wù)打包到一個 WAR 文件中,以備部署到生產(chǎn)環(huán)境中。為此,在 Project Explorer 視圖中,右鍵單擊Books,然后單擊 Export > WAR file。在 WAR Export Wizard 中,單擊 Browse,選擇創(chuàng)建 WAR 文件的文件夾,然后單擊 Save 和Finish。
可以使用一個支持 HTTP 操作 GET
、POST
、PUT
和 DELETE
的 HTTP 客戶機來調(diào)用這個 Books 服務(wù)。 清單 11 展示了如何使用 Curl 來調(diào)用這個服務(wù)。首先,您將得到所有圖書的表示(最初沒有圖書);然后,您使用 jaxrs.xml 和 rest.xml 兩個表示來創(chuàng)建兩本圖書;最后,您得到所有圖書的表示和 id=2
的圖書的表示。
清單 11. 調(diào)用 Books 服務(wù)
$ curl -X GET http://localhost:8080/Books/books <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <books/> $ more Books\xml\jaxrs.xml <?xml version="1.0" encoding="UTF-8"?> <book> <titleRESTful Java with JAX-RS</title> <isbn>978-0-596-15804-0</isbn> </book> $ more Books\xml\rest.xml <?xml version="1.0" encoding="UTF-8"?> <book> <title>RESTful Web Services</title> <isbn>978-0-596-52926-0</isbn> </book> $ curl -H "Content-Type: application/xml" -T Books\xml\jaxrs.xml \ -X POST http://localhost:8080/Books/books $ curl -H "Content-Type: application/xml" -T Books\xml\rest.xml \ -X POST http://localhost:8080/Books/books $ curl -X GET http://localhost:8080/Books/books <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <books> <book id="2"> <title>RESTful Web Services</title> <isbn>978-0-596-52926-0</isbn> <link rel="self" href="http://localhost:8080/Books/books/2"/> </book> <book id="1"> <title>RESTful Java with JAX-RS</title> <isbn>978-0-596-15804-0</isbn> <link rel="self" href="http://localhost:8080/Books/books/1"/> </book> </books> $ curl -X GET http://localhost:8080/Books/books/2 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <book id="2"> <title>RESTful Web Services</title> <isbn>978-0-596-52926-0</isbn> <link rel="self" href="http://localhost:8080/Books/books/2"/> </book> |
本文展示了如何聯(lián)合使用幾種技術(shù)來支持 Apache Wink REST Web 服務(wù)的開發(fā)、構(gòu)建和部署。Java 注釋的威力簡化了開發(fā)工作。
REST Web 服務(wù)支持應(yīng)用程序交換鏈接數(shù)據(jù)。Tim Berners-Lee 先生預(yù)言,鏈接數(shù)據(jù)將改變 Web 的面貌。Bill Burke 在他撰寫的圖書 RESTful Java with JAX-RS 中指出,通過將服務(wù)交互的復(fù)雜性限制為數(shù)據(jù)表示,REST Web 服務(wù)將服務(wù)組合性和重用提高到一個新的水平。
本文僅僅涉及 Apache Wink 服務(wù)開發(fā)的皮毛,Apache Wink 的其他重要特性包括鏈接構(gòu)建器、自定義消息體閱讀器以及針對不受支持的數(shù)據(jù)格式的編寫器。要深入了解 Apache Wink 框架,一定要看一看 Apache Wink 中包含的其他示例。
描述 | 名字 | 大小 | 下載方法 |
---|---|---|---|
本文源代碼 | wink_code.zip | 645KB | HTTP |
學習
- Apache Wink:探秘 Apache Wink 項目頁面。
- 基于 REST 的 Web 服務(wù):基礎(chǔ):閱讀 Alex Rodriguez 的文章,理解 REST 的概念。
- RESTful Java with JAX-RS(O'Reilly Media,2009):閱讀這本由 Bill Burke 撰寫的圖書,該書主要講述如何開發(fā) JAX-RS 兼容服務(wù)。
- 實用數(shù)據(jù)綁定:閱讀這份由 Brett McLaughlin 撰寫的關(guān)于數(shù)據(jù)綁定的初級教程的 第 1 部分和 第 2 部分。
- 雙重檢查鎖定及單例模式:了解如何使 singletons 線程安全。
- Linked Data:閱讀 Tim Berners-Lee 先生撰寫的這篇文章,了解鏈接數(shù)據(jù)和語義 Web。
- developerWorks 技術(shù)活動和網(wǎng)絡(luò)廣播:隨時關(guān)注 developerWorks 技術(shù)活動和網(wǎng)絡(luò)廣播。
- developerWorks Web development 專區(qū):通過專門關(guān)于 Web 技術(shù)的文章和教程,擴展您在網(wǎng)站開發(fā)方面的技能。
- developerWorks Ajax 資源中心:這是有關(guān) Ajax 編程模型信息的一站式中心,包括很多文檔、教程、論壇、blog、wiki 和新聞。任何 Ajax 的新信息都能在這里找到。
- My developerWorks:個性化您的 developerWorks 體驗。
posted on 2011-10-07 18:18 Steven_bot 閱讀(1567) 評論(0) 編輯 收藏 所屬分類: eclipse