<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    神奇好望角 The Magical Cape of Good Hope

    庸人不必自擾,智者何需千慮?
    posts - 26, comments - 50, trackbacks - 0, articles - 11
      BlogJava :: 首頁 ::  :: 聯系 :: 聚合  :: 管理

    JAX-RS 從傻逼到牛叉 2:開發一個簡單的服務

    Posted on 2011-09-20 17:22 蜀山兆孨龘 閱讀(9744) 評論(3)  編輯  收藏 所屬分類: Java EESOA

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


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

            public class Movie {
                private int id;
                private String 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 注解來指定單例對象。

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

            @ApplicationPath("rest")
            public class RestApplication extends Application {
            }
        

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


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

            @Singleton
            @Path("movie")
            public class MovieService {
                private AtomicInteger ai;
                private ConcurrentMap<Integer, Movie> movieMap;
    
                @PostConstruct
                private void init() {
                    ai = new AtomicInteger();
                    movieMap = new ConcurrentHashMap<>();
                    int id = ai.getAndIncrement();
                    movieMap.put(id, new Movie().setId(id).setTitle("Avatar"));
                }
        

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


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

    GET

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

            @GET
            @Path("{id}")
            @Produces(MediaType.APPLICATION_JSON)
            public Movie find(@PathParam("id") int id) {
                Movie movie = movieMap.get(id);
                if (movie != null) {
                    return movie;
                } else {
                    throw new WebApplicationException(Response.Status.NOT_FOUND);
                }
            }
        

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

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

    POST

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

            @POST
            @Consumes(MediaType.APPLICATION_JSON)
            public Response create(Movie movie) {
                int id = ai.getAndIncrement();
                movieMap.put(id, movie.setId(id));
                return Response.created(URI.create(String.valueOf(id))).build();
            }
        

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

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

    PUT

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

            @PUT
            @Path("{id}")
            @Consumes(MediaType.APPLICATION_JSON)
            public Response update(@PathParam("id") int id, Movie movie) {
                movie.setId(id);
                if (movieMap.replace(id, movie) != null) {
                    return Response.ok().build();
                } else {
                    throw new WebApplicationException(Response.Status.NOT_FOUND);
                }
            }
        

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

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

    DELETE

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

            @DELETE
            @Path("{id}")
            public Response delete(@PathParam("id") int id) {
                if (movieMap.remove(id) != null) {
                    return Response.ok().build();
                } else {
                    throw new WebApplicationException(Response.Status.NOT_FOUND);
                }
            }
        

    沒什么特別的,該說的前面都說了。

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


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


    評論

    # re: JAX-RS 從傻逼到牛叉 2:開發一個簡單的服務[未登錄]  回復  更多評論   

    2013-06-13 18:29 by ddd
    ffffff

    # re: JAX-RS 從傻逼到牛叉 2:開發一個簡單的服務  回復  更多評論   

    2013-11-12 09:41 by 飛過天涯海角
    lz,我昨天跑了一下你的代碼,在jax-rs中@Singleton是沒用的,每次訪問movie的時候,都會新建一個movieService對象,movieMap也會被重新初始化,沒法作為內存數據庫來使用。

    # re: JAX-RS 從傻逼到牛叉 2:開發一個簡單的服務  回復  更多評論   

    2014-01-07 11:49 by zihang_Wu
    想問一下樓主,我的webservice還有一層接口,注解該加在哪里比較好啊
    主站蜘蛛池模板: 国产一区二区三区免费| 一本色道久久88亚洲综合 | 亚洲视频免费一区| 一级毛片试看60分钟免费播放| 亚洲乱码卡一卡二卡三| 亚洲综合图色40p| 免费国产精品视频| 欧美三级在线电影免费| 91嫩草免费国产永久入口| 国产精品福利片免费看| 最新亚洲人成无码网www电影| 国产精品亚洲专区在线观看| 久久青青草原亚洲av无码app| 亚洲精品自在在线观看| 亚洲中久无码不卡永久在线观看| 日本免费网站观看| 成年性羞羞视频免费观看无限| **一级一级毛片免费观看| 人人玩人人添人人澡免费| 久久www免费人成看国产片| 老外毛片免费视频播放| 亚洲heyzo专区无码综合| 亚洲国产精品ⅴa在线观看| 亚洲偷偷自拍高清| 亚洲天堂电影在线观看| 亚洲人妖女同在线播放| 亚洲人成影院77777| 亚洲小说区图片区| 亚洲an日韩专区在线| 激情综合亚洲色婷婷五月| 亚洲精品电影在线| 亚洲成A∨人片在线观看无码| 亚洲人6666成人观看| 久久精品亚洲AV久久久无码| 国产午夜亚洲精品国产| 亚洲日韩精品无码专区| 国产成人高清亚洲一区久久| 免费大片av手机看片| 永久免费无码网站在线观看个| eeuss免费天堂影院| 91精品成人免费国产|