在Web的世界中,Java從最早的Servlet/JSP,發(fā)展到JSTL/JSF,而third party也有action-based的Struts及Spring MVC,或component-based的GWT, ZK等,事實(shí)上Java的Web世界已經(jīng)非常的成熟。然而這些架構(gòu)主要設(shè)計(jì)是以Web Application為主要訴求,但是Web的世界中還有另外一個(gè)常見的應(yīng)用是Web Services。
設(shè)計(jì)Web Services通常有兩條路可以走,一種是SOAP/WSDL等XML-Based的技術(shù),定義是相對(duì)嚴(yán)謹(jǐn)?shù)腤eb services;另外一種會(huì)是比較輕量級(jí)的以JSON為傳遞資料的格式,而通常也會(huì)設(shè)計(jì)成RESTful的Web Services。這兩種技術(shù)在Java Community中也分別被定義成標(biāo)準(zhǔn),分別是JAX-WS(Java API for XML Web Services)-JSR224跟JAX-RS(Java API for RESTful Web Services)-JSR311,前者在Java EE 5時(shí)被納入,後者在Java EE 6被納入。
RESTful Service由於輕量、好測(cè)試有彈性的特性,越來越被大家所喜愛。本次系列文主要是介紹JAX-RS,並且以JAX-RS的RI(Refernce Implementation) Jersey當(dāng)做環(huán)境。
REST是REpresentational State Transfer的縮寫,從這個(gè)縮寫實(shí)在很難看出這東西是什麼,然而他是一個(gè)設(shè)計(jì)理念,而這個(gè)概念也漸漸變成目前Web設(shè)計(jì)的主流。REST把所有WEB上的東西都以一個(gè)資源(Resource)去看待,並且所有資源都會(huì)有一個(gè)URI(Uniform Resource Identifier),這個(gè)概念就是們常見的網(wǎng)址,由於Web底層用的HTTP本身就是這樣的設(shè)計(jì),所以這個(gè)概念應(yīng)該大部分的人都知道。
進(jìn)一步去看HTTP,稍微瞭解的人應(yīng)該都知道HTTP有幾種Method,常見的有GET跟POST,而比較少見的有PUT跟DELETE。在當(dāng)初設(shè)計(jì)HTTP時(shí),有這樣的設(shè)計(jì)是希望GET代表取得資源,POST代表的是新增資源,而PUT跟DELETE就分別代表更新跟移除資源,這有沒有有點(diǎn)像資料庫(kù)設(shè)計(jì)講的 CRUD (Create,Read,Update,Delete)呢? 事實(shí)上就是如此,甚至還可以一對(duì)一的對(duì)應(yīng)。但由於我們Web上層用的HTML只有定義到GET跟POST,導(dǎo)致我們都忘記有PUT跟DELETE的存在,另外還有HEAD/STATUS等Method,都分別定義來對(duì)資源做的動(dòng)作。
那RESTful Web Services又是什麼呢? RESTful Web Services(或稱RESTful Web API)是以HTTP為基礎(chǔ),必且有以下三個(gè)特色
假設(shè)我們系統(tǒng)有個(gè)product類型的資源。那麼取得所有的products:
1 | GET http: //www.example.com/products |
取得某個(gè)product id的product:
1 | GET http: //www.example.com/products/12345 |
新增一個(gè)product:
1 2 3 4 | POST http: //www.example.com/products { { 'name' : 'foo' }, { 'price' : 1000 } } |
更新一個(gè)proudct:
1 2 3 4 | PUT http: //www.example.com/products/12345 { { 'inventory' : 5 } } |
有了RESTful基本概念,那要開始介紹的是Java對(duì)RESful Web Service的解決方案:JAX-RS。
JAX-RS跟所有Java EE的技術(shù)一樣,它只提供了技術(shù)標(biāo)準(zhǔn),可以允許各家廠商有自己的實(shí)作,本系列用的Jersey即是實(shí)作之一。JAX-RS是架構(gòu)在Java EE的Servlet之上,並且使用annotation來簡(jiǎn)化資源位置跟參數(shù)的描述,大大的減少開發(fā)RESTful Web Services的複雜度。
假設(shè)我們有一個(gè)簡(jiǎn)單的hello的resource,他的URI可能是:
http:///localhost:8080/hello/codedata
我們希望輸出是:
1 | Hello, codedata |
那這個(gè)簡(jiǎn)單的應(yīng)用的程式碼可能如下:
1 2 3 4 5 6 7 8 | @Path ( "/hello" ) public class HelloRS { @GET @Path ( "/{name}" ) public String sayHello( @PathParam ( "name" ) String name) { return "Hello, " + name; } } |
看到上面簡(jiǎn)潔的程式碼,應(yīng)該開始對(duì)於JAX-RS的精簡(jiǎn)感到興奮。如果有用過Servlet的朋友應(yīng)該可以想想同樣的需求寫在Servlet,會(huì)有多少的code在做request跟response的操作,還有對(duì)參數(shù)的處理跟型別轉(zhuǎn)換。而這些複雜的動(dòng)作,都可以透過JAX-RS的annotation的描述,對(duì)應(yīng)到Java的class/method/parameter。而且不像Servlet必須繼承一個(gè)Servlet的Base class,在JAX-RS中都是以POJO的形式存在。另外在JAX-RS也用類似Spring的injection的方式,把外部一些物件inject到物件當(dāng)中,減少Class跟Class之間的耦合度。
筆者的環(huán)境是:
1. 產(chǎn)生一個(gè)Maven webapp project
如果你是用mvn command:
1 2 3 4 | mvn archetype:generate \ -DgroupId=tw.com.codedata.jersey \ -DartifactId=JerseyExample \ -DarchetypeArtifactId=maven-archetype-webapp |
因?yàn)槲沂怯肊clipse的M2E plugin,所以操作為「File -> New -> Other…
選Maven Project」:
找到Artifact Id = maven-archetype-webapp:
填入Project資訊:
2. 產(chǎn)生好Maven project後,因?yàn)閙aven產(chǎn)生的webapp project不會(huì)有src/main/java
這個(gè)目錄,所以我們需要手動(dòng)把這個(gè)目錄產(chǎn)生出來。這點(diǎn)千萬不要忘記。
3. 增加Jersey的dependency
打開pom.xml:
1 2 3 4 5 | <dependency> <groupId>org.glassfish.jersey.containers</groupId> <artifactId>jersey-container-servlet</artifactId> <version> 2.2 </version> </dependency> |
如果你跟我一樣是用M2E,請(qǐng)點(diǎn)project root按右鍵,Maven -> Add Dependency
,填入dependency資訊:
4. 如果你的container可以支援Servlet 3.0,那會(huì)建議使用Servlet 3.0,所以請(qǐng)把src/main/webapp/WEB-INF/web.xml
的內(nèi)容改成是Servlet 3.0的descriptor。
原本是:
1 2 3 4 5 6 | <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" " <web-app> ... </web-app> |
改成:
5. 新增一個(gè)Jersey Application。此Application等同是註冊(cè)一個(gè)("/rest/*"
)的url mapping給Jersey,完全不需要去web.xml
作額外設(shè)定。
1 2 3 4 5 6 7 8 9 10 11 | package tw.com.codedata.jersey; import javax.ws.rs.ApplicationPath; import org.glassfish.jersey.server.ResourceConfig; @ApplicationPath ( "rest" ) public class MyApplication extends ResourceConfig{ public MyApplication(){ packages( "tw.com.codedata.jersey" ); } } |
6. 產(chǎn)生我們第一個(gè)Helloworld的restful service。
這邊我們定義了兩個(gè)resource:
一個(gè)是/rest/hello
,這個(gè)會(huì)印出"Hello world"
一個(gè)是/rest/hello/{name}
,這個(gè)會(huì)印出"Hello, {name}"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package tw.com.codedata.jersey; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; @Path ( "/hello" ) public class HelloRS { @GET public String sayHelloWorld() { return "Hello world" ; } @GET @Path ( "/{name}" ) public String sayHello( @PathParam ( "name" ) String name) { return "Hello, " + name; } } |
7. 把你的webapp跑起來,結(jié)果如下。
JerseyExample是webapp name,rest是Jersey的root,hello是hello resource的root,當(dāng)然這些都可透過設(shè)定的方式拿掉。
用Application的設(shè)定方法,只能限定在Servlet 3.0的webapp(由web.xml
決定,也就是我們step3做的事情),如果你的webapp無法使用Servlet 3.0,或是想要在web.xml做設(shè)定,請(qǐng)參考
https://jersey.java.net/documentation/2.2/user-guide.html#deployment
本篇先簡(jiǎn)單介紹RESTful service跟Jersey的建議安裝步驟。下一篇會(huì)開始介紹Jersey/JAX-RS的核心概念。
聯(lián)系客服