Web Service 目前在風格上有兩大類,一個是基於 SOAP 協議,一個是徹底遵循 HTTP 協議規範的RESTful 風格。 SOAP 方式的 web service 已經很成熟了,應用也很廣,已經成爲 Web Service 的工業標準。不過 RESTful Web Service 如今勢頭愈來愈猛,特別是其靈活性以及與 Ajax 的完美結合,讓人愛不釋手,頗有必要了解一下。 RESTful 就是一種架構風格,是對 HTTP 協議的徹底遵循。像是人們經歷了無止境的對 HTTP 引伸、擴展之路後的一種迴歸,讓 web service 開發者從新作回 HTTP 協議的信徒。 RESTful 倡導用 HTTP 協議中的 verb 與實際數據操做的增刪改查相對應,如 HTTP 中的 PUT 、 GET 、 POST 、DELETE 分別對應 web 系統中數據的改、查、增、刪的操做。固然 RESTful 支持的 http verb 還不只限於上述 4 個,還有像其餘的 HEAD,OPTION ……等,不過上述 4 個已經夠咱們平常使用了。java
目前 Web Service 的框架不少,不過我只用過 CXF ,因此我仍是以 apache-cxf2.2.2 爲例介紹一下 RESTful Web Service 的開發。web
比較常見的 RESTful Web Service 的發佈有 JAX-RS 風格、 Provider 方式、 Servlet 方式、 HTTPBinding ,也許還有其餘方式,不過我只知道這些了。總的感受最偷懶的方式是採用 Servlet 方式發佈 Web Service ,說是 Web Service 發佈,其實就是轉換下對 Servlet 的認識, Servlet 自己就支持對 HTTP 請求中各類動做的支持,因此 Servlet 原生就是一種 RESTful Web Service 。不過這種方式我以爲有些不夠時尚。 Povider 的方式我瞭解了一下,須要實現 Provider 接口,感受很受拘束。正在我對 RESTful 喪失興趣的時候,發現了 JAX-RS 風格, JAX-RS 就像是一個清純 MM ,服務端配置完成,並且配置很是的清秀。並且也很是的平易近人,客戶端調用方式很靈活。接下來我就把她介紹給你們。spring
1. 環境搭建apache
在本示例中採用了 Web Project 的方式, Web 工程創建的過程就再也不贅述了,只列舉一下環境須要的 jar包。以下:json
下面是我本地工程中的 jar ,僅供參考。api
注:若是要在 RESTful 服務中直接返回 json 數據格式的話, jsr311-api-1.0.jar 必不可少。瀏覽器
2. 服務開發服務器
咱們以簡單的一個客戶信息服務爲例進行介紹。服務的業務實現你們不用太在乎,關注服務開發的過程就行了。架構
2.1 開門見山app
首先咱們先構造一個客戶的 VO 。
package com.harvey.cxf.demo.rest.rs.vo; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "Customer") @XmlAccessorType(XmlAccessType.FIELD) public class Customer { private long id; private String name; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
接下來以 CustomerService 做爲客戶服務的實現類
先以最多見的查詢某一客戶信息爲例,以下:
@Path("/") public class CustomerService { …… //普通路徑參數方式GET請求 @GET @Path("/customers/{id}") public Customer getCustomer(@PathParam("id") String id) { …… } }
假設咱們的服務最終發佈地址爲 http://127.0.0.1:8080/customerservice 的話,若是要查詢某一客戶的信息的話,咱們只需發送 GET 請求 http://127.0.0.1:8080/customerservice/customers/123 ,或者直接在瀏覽器中輸入該 url就可直接訪問該服務。其中 url 中傳入的 123 會自動適配到服務中的 {id} 參數。這樣一個簡單的處理 GET 請求的RESTful Web service 就開發完了。
2.2 參數處理
在上面的例子中咱們看到, client 在服務請求時傳遞參數是經過的請求路徑的方式傳入的。如以前的http://127.0.0.1:8080/customerservice/customers/123 , 客戶 ID123 做爲了路徑一部分。對應的服務器端的參數配置採用了 @PathParam ( "id" ) String id 。
除了支持路徑傳遞的方式以外,咱們還能夠在請求時的查詢參數中指定。好比仍是查詢某一客戶的信息,server 端代碼能夠以下:
…… @GET @Path("/customers/getCustomerById") public Customer getCustomerByQueryString(@QueryParam("id") String id){ …… }
此時該服務訪問 url 則爲:
http://127.0.0.1:8080/customerservice/customers/getCustomerById?id=123
說着這,也許會有聽衆要問了(也許沒人問):以前提到的都是簡單參數,這個容易,那麼對於一些複雜bean 參數該怎麼處理呢?
這個問題問得好,接下來我就舉例說明下。說例子以前再重複一遍啊,看例子時千萬不要糾結於代碼的業務邏輯,只看技術實現方式。
…… @GET @Path("/customers/getCust") public Customer getCustomer(@QueryParam("")Customer cust) { …… }
這個例子中須要傳入以前聲明的客戶信息類做爲參數。參數處理方式爲 QueryParam ,因此這時候咱們訪問url 格式相似如下:
http://127.0.0.1:8080/customerservice/customers/getCust?id=123&name=xiaoming
若是參數 bean 還包括其餘屬性,用 & 符號依次追加就行了。還一般的 web 訪問沒什麼區別。
若是你以爲上述用查詢字串的方式不夠個性的話,能夠採用另一種: @MatrixParam
Server 端代碼以下:
…… @GET @Path("/customers/getCustByMat") public Customer getCustomerByMat(@MatrixParam("")Customer cust) { …… }
這時候咱們再訪問時就能夠用下面的形式了:
http://127.0.0.1:8080/customerservice/customers/getCustByMat;id=123;name=xiaoming
若是 cusmomer 還有其餘屬性,直接在後面追加就能夠了,參數之間用 ; 分隔 .
以上咱們用到的參數雖然採用的處理方式的註解各不相同,可是都是有註解的。接下來出場的這個就屬於特殊人物了。有請 body 參數出場……
…… @POST @Path("/addCustomer") public Customer addCustomer(String body) { …… }
這個 body 參數是 JAX-RS 中比較特殊的,它前面沒有任何註解,它表明的是請求的 body 內容或者請求的inputstream ,自動解析映射爲字符串參數。由於以前咱們的例子都是 GET 請求,消息中是沒有 body 的,因此細心的聽衆可能會發現咱們此次的服務的 verb 配置爲了 @POST 。
2.3 請求、應答數據格式
以前的示例中咱們的方法都返回了 Customer ,可是咱們的系統是一個 RESTful web service 啊,客戶端接收到的確定是一個 HTTP 的 response 消息,那麼返回的 Customer 是怎樣的一個消息內容呢?
默認狀況下 reponse 消息的 Customer 會以 xml 的格式返回。相似於:
<Customer><id>123</id><name>skdjl</name></Customer>
客戶端調用 server 端服務,獲得上述的 xml 字串結果,而後進行後續處理。
既然這說的默認狀況,言外之意就是說還有不少其餘狀況了,不過須要配置一下。就不得不說下@Produces註解了。@Produces就是表示server端返回的數據格式類型,具體包括application/xml, application/json, application/text……,其實就是咱們常見的web端的content-type中的類型。若是咱們設置爲application/json,天然返回的數據格式爲json形式。另外一個註解:@Consumes,就是與@Produces相對應的。@Consumes是設定的客戶端發送的請求數據的格式,對應支持的類型與@Produces相同。具體配置以下:
…… @POST @Path("/addCustomerUseBean") @Produces("application/json") @Consumes("application/xml") public Customer addCustomerUseBean(Customer cust) { …… }
這個例子的意思是客戶端傳入 Customer 做爲參數,傳遞格式爲 xml 風格, server 端應答的數據格式爲json 風格。咱們固然也能夠在類註解中加入 @Produces 和 @Consumes 做爲服務總體的 request 和 response風格,具體服務方法若是不設定的話採用類的 @Produces 和 @Consumes 的設定。
3 服務配置
服務開發完成以後,咱們要作的就是把服務發佈出去。發佈方式也很靈活,能夠採用編碼的方式,也能夠和spring 結合用配置的方式。在下面的例子中咱們採用後者。配置的內容再也不作過多的解釋,你們看一下就能明白。
3.1 web.xml 的配置
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>DEMO</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/applicationContext*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
3.2 spring 配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:cxf="http://cxf.apache.org/core" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml"/> <jaxrs:server id="customerService" address="/customerservice"> <jaxrs:serviceBeans> <ref bean="customerServiceBean"/> </jaxrs:serviceBeans> </jaxrs:server> <bean id="customerServiceBean" class="com.harvey.cxf.demo.rest.rs.server.CustomerService"/> </beans>
而後啓動咱們的 web 程序,服務就發佈完成了。能夠在瀏覽器中敲入:
http://127.0.0.1:8080/customerservice/customers/1 測試服務是否正常。
從上面的例子也許你能發現,咱們雖然是作的RESTful的例子,可是具體提供的服務verb中不少都是帶有動做的成分好比getCustomer,addCustomer等等,這些詞彙的存在預示着咱們的服務雖然採用了RESTful的技術,可是並無遵循ROA(面向資源的架構)的理念,真正的ROA信徒看到上面的例子可能會很是不爽。由於畢竟RESTful和ROA纔是兩小無猜。不過正如前面提到的,你們值關注技術實現就行了。
4 客戶端調用
咱們的服務發佈完成了,接下來咱們能夠作一個測試 client 程序進行 測試。對於 RESTful 的調用方式不少,能夠用 java 的 Connect 方式,也能夠用 apche 的 httpclint ,另外還能夠用 cxf 提供的 WebClient 進行,等等。後面的 client 代碼中分別採用幾種調用方式,在這裏咱們沒必要拘泥,能夠任意選用本身熟悉的方式。