Jersey 入門與Javabean
Jersey是JAX-RS(JSR311)開源參考實現用於構建RESTful Web service,它包含三個部分:
核心服務器(Core Server) 通過提供JSR 311中標準化的註釋和API標準化,可以用直觀的方式開發RESTful Web服務。
核心客戶端(Core Client) Jersey客戶端API能夠幫助開發者與RESTful服務輕鬆通信;
集成(Integration) Jersey還提供可以輕鬆繼承Spring、Guice、Apache Abdera的庫
設置Jersey環境
Maven
<!--jersey--> <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet-core</artifactId> <version>2.0</version> </dependency> <!--JAXB API--> <dependency> <groupId>javax.xml.ws</groupId> <artifactId>jaxws-api</artifactId> <version>2.1</version> </dependency> <!-- Json支持 --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-asl</artifactId> <version>1.9.12</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.12</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-jaxrs</artifactId> <version>1.9.12</version> </dependency>
引入Jar文件方式
從Jersey開發包中將以下庫複製的WEB-INF下的庫目錄:
服務器 jersey-server.jar 、jersey-container-servlet-core.jar、jersey-container-servlet.jar、javax.ws.rs-api-2.0.jar
客戶端: jersey-client.jar
common: jersey-common.jar
json支持:在Jersey2.0中需要使用Jackson1.9才能支持json。
Demo Hello Wolrd
第一步:編寫一個名爲HelloResource的資源,它接受Http Get請求並響應 ”Hello Jerset”
@Path("/hello") public class HelloResource { @GET @Produces(MediaType.TEXT_PLAIN) public String sayHello() { return "Hello Jersey"; } }
第二步:編寫JAX-RS application
public class APIApplication extends ResourceConfig { public APIApplication() { //加載Resource register(HelloResource.class); //註冊數據轉換器 register(JacksonJsonProvider.class); // Logging. register(LoggingFilter.class); } }
第三步:在web.xml文件中定義servelt調度程序,目的是將所有REST請求發送到Jersey容器。除了聲明Jersey Servlet外,還需定義一個初始化參數,指定JAX-RS application。
<!--用於定義 RESTful Web Service 接口--> <servlet> <servlet-name>JerseyServlet</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>cn.com.mink.resource.APIApplication</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JerseyServlet</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping>
第四步:測試程序
在命令終端中輸入以下命令,將會看到“Hello Jersey”。
curl http://host:port/services/hello
或者在瀏覽器中輸入以下URL,將會看到“Hello Jersey”
http://host:port/services/hello
使用
資源
Root Resource And Sub-Resource
資源是組成RESTful服務的關鍵部分,可以使用HTTP方法(如:GET、POST、PUT和DELETE)操作資源。在JAX-RX中,資源通過POJO實現,使用@Path 註釋組成其標識符。資源可以有子資源,父資源是資源集合,子資源是成員資源。
Resources是”/services” URI組成是集合資源,UserResource是“/services/user” URI組成的成員資源;
注意,POJO是指簡單的Java對象
@Path("/services") public class Resources { @Path("/user") public UserResource getUserResource() { ... } @Path("/book") public BookResource getBookResource() { ... } }
UserResource是“/user” URI組成的集合資源,getUser是“/user/{username}” URI組成的資源方法
大括號裏的內容是參數,這樣設置是合理的,因爲username也會出現在url中,而path就是url的意思
@Path("/user") public class UserResource { @GET @Path("{username"}) @Produces("application/json") public User getUser(@PathParam("username") String userName) { ... } }
HTTP Methods
HTTP方法映射到資源的CRUD(創建、讀取、更新和刪除)操作,基本模式如下:
HTTP GET: 讀取/列出/檢索單個資源
HTTP POST: 新建資源
HTTP PUT: 更新現有資源或資源集合
HTTP DELETE: 刪除資源或資源集合
@Produces
@Produces註釋用來指定將要返回給client端的數據標識類型(MIME)。@Produces可以作爲class註釋,也可以作爲方法註釋,方法的@Produces註釋將會覆蓋class的註釋。 覆蓋的意思是假如方法聲明瞭自己的Produce,那麼以方法的爲準,class的僅供參考
指定MIME類型
@Produces(“application/json”)
指定多個MIME類型
@Produces({“application/json”,“application/xml”})
@Consumes
@Consumes與@Produces相反,用來指定可以接受client發送過來的MIME類型,同樣可以用於class或者method,也可以指定多個MIME類型,一般用於@PUT,@POST 。
參數(Parameter Annotations)
Parameter Annotations用於獲取client發送的數據。本文只介紹常用的註解,更多詳見Jersey用戶手冊
@PathParam
使用@PathParam可以獲取URI中指定規則的參數,比如:
@GET @Path("{username"}) @Produces(MediaType.APPLICATION_JSON) public User getUser(@PathParam("username") String userName) { ... }
當瀏覽器請求http://localhost/user/jack時,userName值爲jack。
注意,這裏的username並不是說Key是username, value是jack而是說/usr/後面跟着的東西就是username,這裏username只是個變量
至於key,value的形式,下面也有
@QueryParam
@QueryParam用於獲取GET請求中的查詢參數,如:
@GET @Path("/user") @Produces("text/plain") public User getUser(@QueryParam("name") String name, @QueryParam("age") int age) { ... }
當瀏覽器請求http://host:port/user?name=rose&age=25時,name值爲rose,age值爲25。如果需要爲參數設置默認值,可以使用@DefaultValue,如:
@GET @Path("/user") @Produces("text/plain") public User getUser(@QueryParam("name") String name, @DefaultValue("26") @QueryParam("age") int age) { ... }
當瀏覽器請求http://host:port/user?name=rose時,name值爲rose,age值爲26。
@FormParam
@FormParam,顧名思義,從POST請求的表單參數中獲取數據。如:
@POST @Consumes("application/x-www-form-urlencoded") public void post(@FormParam("name") String name) { // Store the message }
表單我一直沒弄清楚到底是什麼東西,但就使用的情況來看,就是key,value之類的東西
BeanParam
當請求參數很多時,比如客戶端提交一個修改用戶的PUT請求,請求中包含很多項用戶信息。這時可以用@BeanParam。
User Bean定義如下:
@XmlRootElement(name = "user") public class User { @PathParam("userName) //我覺得這個應該是寫錯了,FormParam private String userName; @FormParam("name") private String name; @FormParam("telephone") private String telephone; @FormParam("email") private String email; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } ... }
使用Map
在一個大型的server中,因爲參數的多變,參數結構的調整都會因爲以上幾種方式而遇到問題,這時可以考慮使用@Context 註釋,並獲取UriInfo實例,如下:
@GET public String get(@Context UriInfo ui) { MultivaluedMap<String, String> queryParams = ui.getQueryParameters(); MultivaluedMap<String, String> pathParams = ui.getPathParameters(); }
我覺得,可以認爲map是上面幾種情況的超集,因爲它能夠替代以上任意一種。map就是context
同樣還可以通過@Context 註釋獲取ServletConfig、ServletContext、HttpServletRequest、HttpServletResponse和HttpHeaders等,如下:
@Path("/") public class Resource { @Context HttpServletRequest req; @Context ServletConfig servletConfig; @Context ServletContext servletContext; @GET public String get(@Context HttpHeaders hh) { MultivaluedMap<String, String> headerParams = hh.getRequestHeaders(); Map<String, Cookie> pathParams = hh.getCookies(); } }
Jersey返回Json和Xml
JAX-RS支持使用JAXB(Java API for XML Binding)將JavaBean綁定到XML或JSON,反之亦然。JavaBean必須使用@XmlRootElement標註,沒有@XmlElement註釋的字段將包含一個名稱與之相同的XML元素,如下:
這裏就說明了javabean和xml, json的關係
@XmlRootElement public class OptionResult { @XmlElement(name = "code") private String result; private String errorMsg; public String getResult() { return result; } public void setResult(String result) { this.result = result; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } }
@Path("/user") public class UserResource { @POST @Produces("application/json") public OptionResult create(@BeanParam User user) { ... } }
最後,要註冊數據轉換器,該轉換器會自動將JavaBean轉換爲json數據:
public class APIApplication extends ResourceConfig { public APIApplication() { //加載Model register(OptionResult.class); //加載與OptionResult同一個packge的Model //packages(OptionResult.class.getPackage().getName()); //加載Resource register(UserResource.class); //註冊數據轉換器 register(JacksonJsonProvider.class); // Logging. register(LoggingFilter.class); } }
說明:返回XML數據的原理相同,僅僅是數據轉換器不同,只需要在APIApplication中同時XML數據轉換器即可。
Wiki上關於REST的總結
含狀態傳輸(英文:Representational State Transfer,簡稱REST)是Roy Fielding博士在2000年他的博士論文中提出來的一種軟件架構風格.
資源是由URI來指定。
對資源的操作包括獲取、創建、修改和刪除資源,這些操作正好對應HTTP協議提供的GET、POST、PUT和DELETE方法。
通過操作資源的表現形式來操作資源。
資源的表現形式則是XML或者HTML,取決於讀者是機器還是人,是消費web服務的客戶軟件還是web瀏覽器。當然也可以是任何其他的格式。
應該注意區別應用的狀態和連接協議的狀態。HTTP連接是無狀態的(也就是不記錄每個連接的信息),而REST傳輸會包含應用的所有狀態信息,因此可以大幅降低對HTTP連接的重複請求資源消耗。
實現舉例:
每種操作都會對應一個函數
列舉所有商品
GET http://www.store.com/products
呈現某一件商品
GET http://www.store.com/product/12345
下單購買
POST http://www.store.com/order <purchase-order> <item> ... </item> </purchase-order>
REST的優點
可更高效利用緩存來提高響應速度
通訊本身的無狀態性可以讓不同的服務器的處理一系列請求中的不同請求,提高服務器的擴展性
瀏覽器即可作爲客戶端,簡化軟件需求
相對於其他疊加在HTTP協議之上的機制,REST的軟件依賴性更小
不需要額外的資源發現機制
在軟件技術演進中的長期的兼容性更好
JavaBean
JavaBeans是Java語言中可以重複使用的軟件組件,它們是一種特殊的Java類[1],將很多的對象封裝到了一個對象(bean)中。特點是可序列化,提供無參構造器,提供getter方法和setter方法訪問對象的屬性。
優點
Bean可以控制它的屬性,事件和方法是否暴露給其他程序
Bean可以接收來自其他對象的事件,也可以產生事件給其他對象
有軟件可以配置Bean
Bean的屬性可以被序列化,以供日後使用
JavaBean規範
要成爲JavaBean類,必須遵循關於命名,構造器,方法的特定規範,有了這些規範,才能使用,複用,替代和連接javabean的工具。
規範是:
有一個public 無參構造器
屬性可以通過get, set, is方法或者遵循特定命名規範的其他方法訪問。
可序列化
一個例子
public class PersonBean implements java.io.Serializable { /** * <code>name</code>屬性(注意大小寫) */ private String name = null; private boolean deceased = false; /** 無參構造器(沒有參數) */ public PersonBean() { } /** * <code>name</code>屬性的Getter方法 */ public String getName() { return name; } /** * <code>name</code>屬性的Setter方法 * @param value */ public void setName(final String value) { name = value; } /** * "deceased"屬性的Getter方法 * 布爾型屬性的Getter方法的不同形式(這裏使用了is而非get) */ public boolean isDeceased() { return deceased; } /** * <code>deceased</code>屬性的Setter方法 * @param value */ public void setDeceased(final boolean value) { deceased = value; } }
總結
1. Javabean提供了一種保證,即對於所有的成員變量,一定有get,set方法。並且還可以序列化
2. REST服務基於底層的保證來實現,因此可以簡化很多事情,比如Javabean和XML的轉換就可以寫得相當簡單。