1 RESTful簡要介紹
RESTful風格的WebService之所以當下如此流行,是由于其相對于SOAP風格的WebService更簡潔、更輕量級,REST風格的WebService傳輸的是JSON或極其簡潔的XML,因而其效率和性能都比較理想。
RESTful風格的WebService主張重用HTTP協議,面向資源編程(ROA)。扼要的說,RESTful風格WebService中,每一個URL即表示一個資源,比如http://www.example.com/employees/1 表示id為1的員工。
1. 如果對此URL調用HTTP GET方法,則返回員工的XML形式;
2. 如果對此URL調用HTTP POST/PUT方法,則可以新增或者修改此員工;
3. 如果對此URL調用HTTP DELETE方法,則可以刪除此員工;
因此,我們在設計一個RESTful風格的接口時,一定要擁有面向資源設計的考量!
此外,在實現接口的過程中,要遵循RESTful風格的幾個特性,他們分別是:
1. 無狀態性:HTTP本身即是無狀態協議,因此RESTful天然的具備無狀態性,具備優良的水平擴展能力!
2. 冪等性:GET/PUT/DELETE方法具備此特性,冪等性可概述為無論重復調用多少次,其結果都一致!POST例外,不過設計的時候也可以按照此特性設計!
3. 唯一性:即URL地址要唯一的表示一個資源!
關于事務、安全等更多的高級特性這里不闡述,有興趣的可參考《Restful Web Service中文版》一書。
下面以開發一個普通接口和開發REST風格WebService接口為對比,指導大家快速入門。
2 開發一個普通的接口
2.1 定義DTO
public class Info implements Serializable { private String id; private String name; private String description; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } |
2.2 定義接口
public interface SaleService extends Serializable { public List<Info> getInfos(); public Info getInfo(String id); public void saveOrUpdateInfo(Info info); public void deleteInfo(String id); } |
2.3 實現類
接口的實現,就此省略。
3 開發RESTful風格WebService服務端
3.1 定義資源(定義DTO)
@XmlRootElement(name = "Info") public class Info implements Serializable { private String id; private String name; private String description; @XmlElement(name = "ID") public String getId() { return id; } public void setId(String id) { this.id = id; } @XmlElement(name = "NAME") public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElement(name = "DESCRIPTION") public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } |
解析:@XmlRootElement(name = "Info")之類的注釋,是JAXB規范,用于XML與Java對象之間的互相轉換。不熟悉JAXB規范的可以自行搜索相關資料。
此Info對象對應的XML格式如下:
<Info> <DESCRIPTION>des1</DESCRIPTION> <ID>1</ID> <NAME>name1</NAME> </Info> |
3.2 開發接口
@Produces({ MediaType.APPLICATION_XML }) public interface SaleService extends Serializable { @GET @Path("/infos") public List<Info> getInfos(); @GET @Path("/infos/{id}") public Info getInfo(@PathParam("id") String id); @POST @Path("/infos") @Consumes({ MediaType.APPLICATION_XML, MediaType.TEXT_XML, MediaType.APPLICATION_JSON }) public void saveOrUpdateInfo(Info info); @DELETE @Path("/infos/{id}") public void deleteInfo(@PathParam("id") String id); } |
解析:接口中的注釋均為JSR311中的規范。
@Produces表示這個接口響應格式為XML
表示響應http://www.example.com/.../infos的GET請求,返回Info列表。
表示響應http://www.example.com/.../infos/? 的GET請求,URL中的“?”作為參數代入方法中,最終返回對應的Info
表示響應http://www.example.com/.../infos 的POST請求,可以接受APPLICATION_XML,TEXT_XML,JSON格式,最終保存Info對象。
表示響應http://www.example.com/.../infos/? 的DELETE請求,URL中的“?”作為參數代入方法中,最終刪除對應的Info
3.3 實現類
接口的實現,同上。
3.4 配置CXF(整合Spring、maven)
3.4.1 接口的Spring配置文件
定義接口的實現類
<bean id="saleService" class="com.csair.acp.service.impl.SaleServiceImpl" /> |
3.4.2 CXF的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: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/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <jaxrs:server id="saleRestService" address="/v1"> <jaxrs:serviceBeans> <ref bean="saleService" /> </jaxrs:serviceBeans> </jaxrs:server> </beans> |
解析:<ref bean="saleService" />對應接口的Spring定義,address="/v1"定義地址的前綴。比如: http://www.example.com/xxx/v1/infos/?
強烈建議加前綴!以此來提供不同版本的WebService訪問!
3.4.3 修改web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/*.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</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>/*</url-pattern> </servlet-mapping> |
3.4.4 Maven配置
在maven中使用cfx發布webservice需要添加如下依賴:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-bundle</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-bundle-jaxrs</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-core</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>2.2.4</version>
</dependency>
到此,配置完畢,可以啟動部署到web容器中啟動,在FireFox中訪問http://localhost:8080/v1/infos ,看看是否有XML格式的輸出。
4 開發RESTful風格WebService客戶端(Java)
4.1 定義客戶端接口
public interface SaleClient extends Serializable { public List<Info> getInfos(); public Info getInfo(String id); public void saveOrUpdateInfo(Info info); public void deleteInfo(String id); } |
4.2 定義實現類
客戶端如果能引用服務端的jar包,那么開發將非常簡單。
import com.csair.acp.resources.Info; //引用自服務端 import com.csair.acp.service.SaleService; //引用自服務端 public class SaleClientImpl implements SaleClient { private static String BASE_ADDRESS = "http://localhost:8080/v1"; SaleService service; public SaleClientImpl() { initProxy(); } private SaleService initProxy() { service = JAXRSClientFactory.create(BASE_ADDRESS, SaleService.class); WebClient.client(service).accept(MediaType.APPLICATION_XML);// 一定需要 return service; } @Override public List<Info> getInfos() { try { return service.getInfos(); } catch (WebApplicationException ex) { ex.printStackTrace(); return null; } } @Override public Info getInfo(String id) { try { return service.getInfo(id); } catch (WebApplicationException ex) { ex.printStackTrace(); return null; } } @Override public void saveOrUpdateInfo(Info info) { try { service.saveOrUpdateInfo(info); } catch (WebApplicationException ex) { ex.printStackTrace(); } } @Override public void deleteInfo(String id) { try { service.deleteInfo(id); } catch (WebApplicationException ex) { ex.printStackTrace(); } } } |
客戶端的開發十分簡便,如果用其他語言,那么需要自行使用對應的HTTP類庫進行編碼。
如果使用spring injecting proxies 方式進行配置,則使用如下配置:
<jaxrs:client id ="saleClient " address="http://localhost:8080/v1" serviceClass="com.csair.acp.service.SaleService">
</jaxrs:client>
具體配置參照:http://cxf.apache.org/docs/jax-rs-client-api.html
5 范例
本文對應的例子為maven構建:
rest-server為服務端,運行jetty:run即可
rest-client為客戶端,運行rest-client/src/test/java/com/csair/acp/client/impl/SaleClientImplTest.java單元測試類即可