2025-02-10

JAX-RS 使用註解進行配置,所以用它開發REST 風格的服務非常簡單。樓主在本文用一個小例子來說明JAX-RS 的基本用法。

 

假設樓主要開發一個小電影服務,客戶端可以通過請求URI 對電影進行CRUD 操作。為簡明起見,這兒不使用數據庫,隻在內存中模擬。先用一個非常簡單的Movie類,在後續的文章中根據情況逐步擴充:

 

 

1

2

3

4

5

publicclassMovie {

    privateintid;

    privateString title;

    // 此處省略若幹行

}

嗯,就是一個很普通的JavaBean,實際項目中可以根據需要加上@Entity等註解。接下來看看如何編寫JAX-RS 服務。

 

一個JAX-RS 服務就是一個使用瞭JAX-RS 註解來將HTTP 請求綁定到方法的Java 類,一共支持兩種類型:單請求對象或單例對象。單請求對象意味著每來一個請求,就創建一個服務對象,在請求結束時銷毀。單例對象則意味著隻有一個服務對象處理所有的請求,從而可以在多個請求間維持服務狀態。JAX-RS 服務可通過繼承javax.ws.rs.core.Application來定義,其中的getClasses方法返回單請求對象的類型,getSingletons方法返回單例對象的類型。這兩個方法是可選的。在Java EE 6 環境中,如果這兩個方法都返回null或者空集合,那麼應用程序中的所有JAX-RS 都將被部署。這時可以用CDI 的@javax.inject.Singleton或者EJB 的@javax.ejb.Singleton註解來指定單例對象。

如果電影服務的上下文根路徑為https://localhost/ms,而樓主希望將服務部署到https://localhost/ms/rest 下面,隻需要寫一個類:

 

 

1

2

3

@ApplicationPath("rest")

publicclassRestApplication extendsApplication {

}

@ApplicationPath註解指定所有服務的相對基址,如果為空字符串,則直接使用上下文根路徑。另一種配置方式是在web.xml 文件中進行聲明,那是為瞭使JAX-RS 能在Servlet 容器(例如Tomcat)中運行,此處略過。這項配置必不可少,否則無法部署服務。

 

很好很強大,現在開始編寫電影服務類MovieService,先看看聲明和初始化:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

@Singleton

@Path("movie")

publicclassMovieService {

    privateAtomicInteger ai;

    privateConcurrentMap<Integer, Movie> movieMap;

 

    @PostConstruct

    privatevoidinit() {

        ai = newAtomicInteger();

        movieMap = newConcurrentHashMap<>();

        intid = ai.getAndIncrement();

        movieMap.put(id, newMovie().setId(id).setTitle("Avatar"));

    }

因為樓主隻需要一個“內存數據庫”,所以用單例對象即可,此處使用CDI 的@javax.inject.Singleton來聲明單例。@Path聲明瞭一個服務,它指示MovieService負責處理發送到https://localhost/ms/rest/movie 的請求。路徑的拼接方式非常直觀。init方法帶有@PostConstruct註解,因此將在MovieService構造完成後立即調用,它向movieMap中存入瞭一個ID 為0 的Movie對象。為簡化代碼,Movie的設置方法都返回this,有點偽造構建者模式的味道。

 

接下來看看如何處理HTTP 請求。

GET

GET 請求用於獲取一個資源。在本例中用來獲取一部電影的信息:

 

 

1

2

3

4

5

6

7

8

9

10

11

@GET

@Path("{id}")

@Produces(MediaType.APPLICATION_JSON)

publicMovie find(@PathParam("id") intid) {

    Movie movie = movieMap.get(id);

    if(movie != null) {

        returnmovie;

    } else{

        thrownewWebApplicationException(Response.Status.NOT_FOUND);

    }

}

該方法標註瞭@GET,表示用來處理向https://localhost/ms/rest/movie/{id} 發送的GET 請求。@Path再次用來綁定路徑,註意其參數{id},它帶有花括號,對應URI 的最後一段,也正好和方法參數id的@PathParam的值相對應。這種參數還有很多高級用法,以後再介紹。@Produces註解指定輸出格式為JSON。JAX-RS 內置瞭很多格式,詳見MediaType的文檔。如果找到瞭相應ID 的對象,則直接返回,JAX-RS 會自動加上響應碼200 OK;否則拋出異常,錯誤碼為404 Not Found。

例如,通過瀏覽器訪問https://localhost/ms/rest/movie/0,得到的結果為{"@id":"0","@title":"Avatar"}。

POST

POST 請求用於創建一個資源。在本例中用來創建一部電影:

 

 

1

2

3

4

5

6

7

@POST

@Consumes(MediaType.APPLICATION_JSON)

publicResponse create(Movie movie) {

    intid = ai.getAndIncrement();

    movieMap.put(id, movie.setId(id));

    returnResponse.created(URI.create(String.valueOf(id))).build();

}

由於沒有@Path註解,所以POST 請求的目標就直接是https://localhost/ms/rest/movie。Consumes和@Produces相反,表示接受的數據類型,此處JAX-RS 會自動把JSON 數據轉換為Movie對象。返回的響應碼為201 Created,並且帶有所創建資源的URI。

例如,向https://localhost/ms/rest/movie 發送POST 請求,正文為{"@title": "007"},則可以從FireBug 的網絡監控中看到返回的響應碼,以及頭部中Location 的值為https://localhost:8080/rest/service/movie/1。多次發送該POST 請求,將會創建多個資源,以保證POST 不是冪等的。

PUT

PUT 請求用於創建或更新一個資源。與POST 不同,PUT 請求要指定某個特定資源的地址。在本例中用來更新一部電影的信息:

 

 

1

2

3

4

5

6

7

8

9

10

11

@PUT

@Path("{id}")

@Consumes(MediaType.APPLICATION_JSON)

publicResponse update(@PathParam("id") intid, Movie movie) {

    movie.setId(id);

    if(movieMap.replace(id, movie) != null) {

        returnResponse.ok().build();

    } else{

        thrownewWebApplicationException(Response.Status.NOT_FOUND);

    }

}

更新成功就返回200 OK,否則返回404 Not Found。這兒先把movie對象的ID 強制改為URI 所指定的,以免出現不一致。也可以根據需求,將不一致作為異常處理,給客戶端返回一個錯誤碼。

順便囉嗦一句,反正代碼在自己手中,樓主也可以把PUT 搞成非冪等的,例如將PUT 當成POST 來處理,就像以前把GET 和POST 一視同仁那樣。不過咱既然在搞JAX-RS,就還是要沾染一點REST 風格,嚴格遵守HTTP 才是。

DELETE

DELETE 請求用於刪除一個資源。在本例中用來刪除一部電影:

 

 

1

2

3

4

5

6

7

8

9

@DELETE

@Path("{id}")

publicResponse delete(@PathParam("id") intid) {

    if(movieMap.remove(id) != null) {

        returnResponse.ok().build();

    } else{

        thrownewWebApplicationException(Response.Status.NOT_FOUND);

    }

}

 

沒什麼特別的,該說的前面都說瞭。

HEAD 和OPTIONS 請求就忽略吧,用得不太多,也同樣挺簡單的。

 

JAX-RS 服務的部署和部署常規Web 程序一樣,打包成war 文件就可以瞭。最後贊一下NetBeans 可以為REST 風格的服務自動生成測試頁面,很好用,雖然在Firefox 下頁面顯示不正常,但IE 是可以的。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *