<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 :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理

    顧名思義,CountDownLatch 是一個用來倒計數(shù)的咚咚。如果某項任務可以拆分成若干個子任務同時進行,然后等待所有的子任務完成,可以考慮使用它。

    該類的用法非常簡單。首先構(gòu)造一個 CountDownLatch,唯一的參數(shù)是任務數(shù)量,一旦構(gòu)造完畢就不能修改。接著啟動所有的子任務(線程),且每個子任務在完成自己的計算后,調(diào)用 CountDownLatch#countDown 方法將倒計數(shù)減一。最后在主線程中調(diào)用 CountDownLatch#await 方法等待計數(shù)器歸零。

    例如賽跑的準備階段,八名運動員先后到達起點做好準備,然后裁判打響發(fā)令槍,準備工作就結(jié)束了,比賽開始。如果把從運動員就位到發(fā)令槍響看做賽跑準備任務,那么每個運動員的準備過程就是其子任務,可以用 CountDownLatch 模擬如下:

            final int count = 8;
            System.out.println("運動員開始就位。");
    
            // 構(gòu)造 CountDownLatch。
            final CountDownLatch cdl = new CountDownLatch(count);
            for (int i = 1; i <= count; i++) {
                final int number = i;
                new Thread() {
                    @Override
                    public void run() {
                        System.out.println(number + " 號運動員到場并開始準備...");
                        try {
                            // 讓運動員隨機準備 2~5 秒鐘。
                            TimeUnit.SECONDS.sleep(new Random().nextInt(4) + 2);
                        } catch (InterruptedException ex) {
                        }
                        System.out.println(number + " 號運動員就位。");
                        // 倒計數(shù)減一。
                        cdl.countDown();
                    }
                }.start();
            }
    
            System.out.println("等待所有運動員就位...");
            try {
                // 等待倒計數(shù)變?yōu)?0。
                cdl.await();
                System.out.println("比賽開始。");
            } catch (InterruptedException ex) {
            }
        

    運行輸出(可能)為:

    運動員開始就位。
    1 號運動員到場并開始準備...
    2 號運動員到場并開始準備...
    4 號運動員到場并開始準備...
    等待所有運動員就位...
    8 號運動員到場并開始準備...
    6 號運動員到場并開始準備...
    3 號運動員到場并開始準備...
    7 號運動員到場并開始準備...
    5 號運動員到場并開始準備...
    6 號運動員就位。
    1 號運動員就位。
    5 號運動員就位。
    4 號運動員就位。
    7 號運動員就位。
    8 號運動員就位。
    2 號運動員就位。
    3 號運動員就位。
    比賽開始。

    從上面的例子還可以看出 CountDownLatch 的局限性和 CompletionService 類似,在于無法處理子任務數(shù)量不確定的情況,例如統(tǒng)計某個文件夾中的文件數(shù)量。另外,如果某個子任務在調(diào)用 countDown 之前就掛掉了,倒計數(shù)就永遠不會歸零。對于這種情況,要么用 finally 之類的手段保證 countDown 一定會被調(diào)用,要么用帶參數(shù)的 await 方法指定超時時間。

    posted @ 2011-10-14 14:22 蜀山兆孨龘 閱讀(1713) | 評論 (1)編輯 收藏

    JAX-RS 的核心功能是處理向 URI 發(fā)送的請求,所以它提供了一些匹配模式以便簡化對 URI 的解析。樓主在本系列的上一篇文章中已經(jīng)使用了最簡單的路徑參數(shù),本文將介紹一些稍微高級點的咚咚。

    模板參數(shù)

    前面已經(jīng)見過用 @Path("{id}")@PathParam("id") 來匹配路徑參數(shù) id。這種匹配方式可以被嵌入到 @Path 注解中的任何地方,從而匹配多個參數(shù),例如下面的代碼用來查找 ID 在某一范圍內(nèi)的電影:

            @GET
            @Path("{min}~{max}")
            @Produces(MediaType.APPLICATION_JSON)
            public List<Movie> findMovies(@PathParam("min") int min, @PathParam("max") int max) {
        

    于是,GET /ms/rest/movie/5~16 就將返回 ID 為 5 到 16 的電影。此處的 minmax 已被自動轉(zhuǎn)換為 int 類型。JAX-RS 支持多種類型的自動轉(zhuǎn)換,詳見 @PathParam 的文檔。

    根據(jù) HTTP 規(guī)范,參數(shù)可能會編碼。默認情況下,JAX-RS 會自動解碼。如果希望得到未解碼的參數(shù),只需在參數(shù)上再加個 @Encoded 注解。該注解適用于大多數(shù) JAX-RS 注入類型,但并不常用。

    模板參數(shù)雖然靈活,也可能會帶來歧義。例如想用 {firstName}-{lastName} 匹配一個人的姓名,但恰好某人的名(lastName)含有“-”字符,像 O-live K 這種,匹配后就會變成姓 live-K,名 O。這種場景很難避免,一種簡單的解決方法就是對參數(shù)值進行兩次編碼,然后在服務端代碼解碼一次,因為 JAX-RS 默認會進行一次解碼,或者加上 @Encoded 注解,自己進行兩次解碼。

    另外,在一個復雜系統(tǒng)中,多個 @Path 可能會造成路徑混淆,例如 {a}-{b}{a}-z 都能匹配路徑 a-z。雖然 JAX-RS 定義了一些規(guī)則來指定匹配的優(yōu)先級,但這些規(guī)則本身就比較復雜,并且也不能完全消除混淆。樓主認為,設計一個 REST 系統(tǒng)的核心就是對 URI 的設計,應當小心處理 URI 的結(jié)構(gòu),合理分類,盡量保證匹配的唯一性,而不要過度使用晦澀的優(yōu)先級規(guī)則。樓主將在下一篇文章介紹優(yōu)先級規(guī)則。

    正則表達式

    模板參數(shù)可以用一個正則表達式進行驗證,寫法是在模板參數(shù)的標識符后面加一個冒號,然后跟上正則表達式字符串。例如在根據(jù) ID 查詢電影信息的代碼中,模板參數(shù) {id} 只能是整數(shù),于是代碼可以改進為:

            @GET
            @Path("{id : \\d+}")
            @Produces(MediaType.APPLICATION_JSON)
            public List<Movie> findMovies(@PathParam("min") int min, @PathParam("max") int max) {
        

    冒號左右的空格將被忽略。用正則表達式驗證數(shù)據(jù)很有局限性,可惜 JAX-RS 目前并不能直接集成 Bean 驗證框架,因此復雜的驗證只能靠自己寫代碼。

    查詢參數(shù)

    查詢參數(shù)很常見,就是在 URI 的末尾跟上一個問號和一系列由“&”分隔的鍵值對,例如查詢 ID 為 5 到 16 的電影也可以設計為 /ms/rest/movie?min=5&max=16。JAX-RS 提供了 QueryParam 來注入查詢參數(shù):

            @GET
            @Produces(MediaType.APPLICATION_JSON)
            public List<Movie> findMovies(@DefaultValue("0") @QueryParam("min") int min,
                    @DefaultValue("0") @QueryParam("max") int max) {
        

    查詢參數(shù)是可選的。如果 URI 沒有設定某個查詢參數(shù),JAX-RS 就會根據(jù)情況為其生成 0、空字符串之類的默認值。如果要手動設定默認值,需要像上面的代碼一樣用 @DefaultValue 注解來指定。另外還可以加上 Encoded 注解來得到編碼的原始參數(shù)。

    有的查詢參數(shù)是一對多的鍵值對,例如 /xyz?a=def&a=pqr,這種情況只需將注入的參數(shù)類型改為 List 即可。

    矩陣參數(shù)

    矩陣參數(shù)應該屬于 URI 規(guī)范中的非主流類型,但它實際上比查詢參數(shù)更靈活,因為它可以嵌入到 URI 路徑中的任何一段末尾(用分號隔開),用來標識該段的某些屬性。例如 GET /ms/rest/movie;year=2011/title;initial=A 表示在 2011 年出品的電影中查找首字母為 A 的標題。year 是電影的屬性,而 initial 是標題的屬性,這比把它們都作為查詢參數(shù)放在末尾更直觀可讀。匹配 URI 的時候,矩陣參數(shù)將被忽略,因此前面的 URI 匹配為 /ms/rest/movie/title。矩陣參數(shù)可以用 @MatrixParam 來注入:

            @GET
            @Path("title")
            @Produces(MediaType.APPLICATION_JSON)
            public List<String> findTitles(@MatrixParam("year") int year,
                    @MatrixParam("initial") String initial) {
        

    如果 URI 的多個段中含有相同名稱的矩陣參數(shù),例如 /abc;name=XXX/xyz;name=OOO,這種直接注入就失效了,只能用下面要講的編程式訪問來取得。

    編程式訪問

    如果簡單的注入不能達到目的,就需要通過注入 PathSegmentUriInfo 對象來直接編程訪問 URI 的信息。

    一個 PathSegment 對象代表 URI 中的一個路徑段,可以從它得到矩陣參數(shù)。它可以通過 @PathParam 來注入,這要求該路徑段必須整個被定義為一個模板參數(shù)。例如下面的代碼也可以用來處理 GET /ms/rest/movie/{id}

            @GET
            @Path("{id}")
            @Produces(MediaType.APPLICATION_JSON)
            public Movie findMovie(@PathParam("id") PathSegment ps) {
        

    @PathParam 也可以注入多個段,如果想把 /a/b/c/d 匹配到 /a/{segments}/d,直接注入一個字符串顯然不行,因為 b/c 是兩個路徑段。唯一的選擇是把注入的類型改為 List<PathSegment>。樓主嚴重不推薦用一個模板參數(shù)匹配多個路徑段,因為這很容易干擾其他匹配的設計,最后搞成一團亂麻。URI 路徑段應當盡量設計得簡單明晰,再輔以矩陣參數(shù)或查詢參數(shù)就能應付大多數(shù)場景。不論對服務端還是客戶端開發(fā)人員來說,簡潔的 URI 既便于管理,又便于使用。網(wǎng)上有不少關于 URI 設計指南的文章,此處不再贅述。

    如果想完全手動解析路徑,則可以用 @Context 注入一個 UriInfo 對象,通過此對象可以得到 URI 的全部信息,詳見 API 文檔。例如:

            @GET
            @Path("{id}/{segments}")
            @Produces(MediaType.PLAIN_TEXT)
            public String getInfo(@PathParam("id") int id, @Context UriInfo uriInfo) {
        

    UriInfo 主要用在某些特殊場合下起輔助作用,設計良好的 URI 用普通的注入就能完成大部分匹配。


    工欲善其事必先利其器,為此 JAX-RS 提供了這些利器來解析 URI。至于如何用這些器來做出一個好系統(tǒng),則還是依賴于 URI 本身的設計。

    posted @ 2011-10-09 12:43 蜀山兆孨龘 閱讀(4258) | 評論 (1)編輯 收藏

    CompletionService 接口的實例可以充當生產(chǎn)者和消費者的中間處理引擎,從而達到將提交任務和處理結(jié)果的代碼進行解耦的目的。生產(chǎn)者調(diào)用 submit 方法提交任務,而消費者調(diào)用 poll(非阻塞)或 take(阻塞)方法獲取下一個結(jié)果:這一特征看起來和阻塞隊列(BlockingQueue)類似,兩者的區(qū)別在于 CompletionService 要負責任務的處理,而阻塞隊列則不會。

    在 JDK 中,該接口只有一個實現(xiàn)類 ExecutorCompletionService,該類使用創(chuàng)建時提供的 Executor 對象(通常是線程池)來執(zhí)行任務,然后將結(jié)果放入一個阻塞隊列中:果然本就是一家親啊!ExecutorCompletionService 將線程池和阻塞隊列糅合在一起,僅僅通過三個方法,就實現(xiàn)了任務的異步處理,可謂并發(fā)編程初學者的神兵利器!

    接下來看一個例子。樓主有一大堆 *.java 文件,需要計算它們的代碼總行數(shù)。利用 ExecutorCompletionService 可以寫出很簡單的多線程處理代碼:

            public int countLines(List<Path> javaFiles) throws Exception {
                // 根據(jù)處理器數(shù)量創(chuàng)建線程池。雖然多線程并不保證能夠提升性能,但適量地
                // 開線程一般可以從系統(tǒng)騙取更多資源。
                ExecutorService es = Executors.newFixedThreadPool(
                        Runtime.getRuntime().availableProcessors() * 2);
                // 使用 ExecutorCompletionService 內(nèi)建的阻塞隊列。
                CompletionService cs = new ExecutorCompletionService(es);
    
                // 按文件向 CompletionService 提交任務。
                for (final Path javaFile : javaFiles) {
                    cs.submit(new Callable<Integer>() {
                        @Override
                        public Integer call() throws Exception {
                            // 略去計算單個文件行數(shù)的代碼。
                            return countLines(javaFile);
                        }
                    });
                }
    
                try {
                    int loc = 0;
                    int size = javaFiles.size();
                    for (int i = 0; i < size; i++) {
                        // take 方法等待下一個結(jié)果并返回 Future 對象。不直接返回計算結(jié)果是為了
                        // 捕獲計算時可能拋出的異常。
                        // poll 不等待,有結(jié)果就返回一個 Future 對象,否則返回 null。
                        loc += cs.take().get();
                    }
                    return loc;
                } finally {
                    // 關閉線程池。也可以將線程池提升為字段以便重用。
                    // 如果任務線程(Callable#call)能響應中斷,用 shutdownNow 更好。
                    es.shutdown();
                }
            }
        

    最后,CompletionService 也不是到處都能用,它不適合處理任務數(shù)量有限但個數(shù)不可知的場景。例如,要統(tǒng)計某個文件夾中的文件個數(shù),在遍歷子文件夾的時候也會“遞歸地”提交新的任務,但最后到底提交了多少,以及在什么時候提交完了所有任務,都是未知數(shù),無論 CompletionService 還是線程池都無法進行判斷。這種情況只能直接用線程池來處理。

    posted @ 2011-09-29 13:37 蜀山兆孨龘 閱讀(2072) | 評論 (0)編輯 收藏

    實體間的多對多的關聯(lián)需要一張關聯(lián)表。如果直接使用 ManyToMany 來映射,JPA 就會隱式地幫我們自動管理關聯(lián)表,代碼寫出來和其他類型的關聯(lián)差別不大。例如,某州炒房團需要一個炒房跟蹤系統(tǒng),那么該系統(tǒng)中的炒房客和房子就是多對多的關系:

            public class Speculator implements Serializable {
                @Id
                private Integer id;
                @ManyToMany
                @JoinTable(joinColumns = @JoinColumn(name = "speculator_id"),
                        inverseJoinColumns = @JoinColumn(name = "house_id"))
                private List<House> houses;
                // 此處省略若干行
            }
    
            public class House implements Serializable {
                @Id
                private Integer id;
                @ManyToMany(mappedBy = "houses")
                private List<Speculator> speculators;
                // 此處省略若干行
            }
        

    如果炒房客 s 要賣掉房子 h(嚴格點說是賣掉房子的產(chǎn)權(quán)部分),那么系統(tǒng)執(zhí)行的代碼差不多就是 s.getHouses().remove(h)。看似簡單,然而底層的操作卻性能低下:JPA 會先從數(shù)據(jù)庫中取出炒房客的所有房產(chǎn)(s.getHouses()),然后再刪除指定的那套房子;從數(shù)據(jù)庫層面上看,這將先從關聯(lián)表(speculator_house)中找到該炒房客的所有房子的外鍵,然后從 house 表載入這些 House 對象,最后才從 speculator_house 刪除關聯(lián)。在 ORM 出現(xiàn)前,這種操作只需要改關聯(lián)表,根本不用關心其他房子。這種簡單的多對多映射寫法將關聯(lián)表隱藏起來,雖然簡化了代碼,卻也可能帶來性能隱患。

    很自然地可以想到,如果把關聯(lián)表也映射成實體類,就能解決這個問題。speculator_house 包含兩個外鍵,可用作聯(lián)合主鍵。如果把它映射為 SpeculatorHouse 類,則該類與 SpeculatorHouse 都是多對一的關系。關聯(lián)表實體類的代碼如下(EmbeddedId 的映射技巧見《JPA 應用技巧 2:主鍵外鍵合體映射》):

            @Embeddable
            public class SpeculatorHouseId implements Serializable {
                private Integer speculatorId;
                private Integer houseId;
                // 此處省略若干行
            }
    
            @Entity
            @Table(name = "speculator_house")
            public class SpeculatorHouse implements Serializable {
                @EmbeddedId
                private SpeculatorHouseId id;
                @MapsId("speculatorId")
                @ManyToOne
                private Speculator speculator;
                @MapsId("houseId")
                @ManyToOne
                private House house;
                // 此處省略若干行
            }
        

    SpeculatorHouse 也要增加相應的關聯(lián)信息:

            public class Speculator implements Serializable {
                @Id
                private Integer id;
                @ManyToMany
                @JoinTable(joinColumns = @JoinColumn(name = "speculator_id"),
                        inverseJoinColumns = @JoinColumn(name = "house_id"))
                private List<House> houses;
                @OneToMany(mappedBy = "speculator")
                private List<SpeculatorHouse> speculatorHouses;
                // 此處省略若干行
            }
    
            public class House implements Serializable {
                @Id
                private Integer id;
                @ManyToMany(mappedBy = "houses")
                private List<Speculator> speculators;
                @OneToMany(mappedBy = "house")
                private List<SpeculatorHouse> speculatorHouses;
                // 此處省略若干行
            }
        

    這樣既保留了多對多關系,又映射了關聯(lián)表,然后就可以根據(jù)實際情況選擇隱式或顯示的關聯(lián)表管理。例如,要得到一個炒房客的全部房子,就使用隱式管理:s.getHouses();而要刪除炒房客和某套房子的關聯(lián),則用顯示管理:delete from SpeculatorHouse sh where sh.speculator = :s and sh.house = :h

    posted @ 2011-09-27 11:04 蜀山兆孨龘 閱讀(3259) | 評論 (2)編輯 收藏

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


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

            public class Movie {
                private int id;
                private String title;
                // 此處省略若干行
            }
        

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


    一個 JAX-RS 服務就是一個使用了 JAX-RS 注解來將 HTTP 請求綁定到方法的 Java 類,一共支持兩種類型:單請求對象或單例對象。單請求對象意味著每來一個請求,就創(chuàng)建一個服務對象,在請求結(jié)束時銷毀。單例對象則意味著只有一個服務對象處理所有的請求,從而可以在多個請求間維持服務狀態(tài)。JAX-RS 服務可通過繼承 javax.ws.rs.core.Application 來定義,其中的 getClasses 方法返回單請求對象的類型,getSingletons 方法返回單例對象的類型。這兩個方法是可選的。在 Java EE 6 環(huán)境中,如果這兩個方法都返回 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)中運行,此處略過。這項配置必不可少,否則無法部署服務。


    很好很強大,現(xiàn)在開始編寫電影服務類 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"));
                }
        

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


    接下來看看如何處理 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} 發(fā)送的 GET 請求。@Path 再次用來綁定路徑,注意其參數(shù) {id},它帶有花括號,對應 URI 的最后一段,也正好和方法參數(shù) id@PathParam 的值相對應。這種參數(shù)還有很多高級用法,以后再介紹。@Produces 注解指定輸出格式為 JSON。JAX-RS 內(nèi)置了很多格式,詳見 MediaType 的文檔。如果找到了相應 ID 的對象,則將其返回,JAX-RS 會自動加上響應碼 200 OK;否則拋出異常,錯誤碼為 404 Not Found。

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

    POST

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

            @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 相反,表示接受的數(shù)據(jù)類型,此處 JAX-RS 會自動把 JSON 數(shù)據(jù)轉(zhuǎn)換為 Movie 對象。返回的響應碼為 201 Created,并且?guī)в兴鶆?chuàng)建資源的 URI。

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

    PUT

    PUT 請求用于創(chuàng)建或更新一個資源。與 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 所指定的,以免出現(xiàn)不一致。也可以根據(jù)需求,將不一致作為異常處理,給客戶端返回一個錯誤碼。

    順便啰嗦一句,反正代碼在自己手中,樓主也可以把 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 服務的部署和部署常規(guī) Web 程序一樣,打包成 war 文件就可以了。最后贊一下 NetBeans 可以為 REST 風格的服務自動生成測試頁面,很好用,雖然在 Firefox 下頁面顯示不正常(對此我已經(jīng)提了一個 bug),但 IE 是可以的。

    posted @ 2011-09-20 17:22 蜀山兆孨龘 閱讀(9746) | 評論 (3)編輯 收藏

    僅列出標題
    共8頁: 上一頁 1 2 3 4 5 6 7 8 下一頁 
    主站蜘蛛池模板: 一区二区三区四区免费视频| 久久国产免费直播| 男人的好免费观看在线视频| 婷婷精品国产亚洲AV麻豆不片 | 国产精品亚洲专区在线观看| 毛片在线播放免费观看| 亚洲成A人片在线观看WWW| 久久久久女教师免费一区| 亚洲色欲久久久综合网东京热| 一级毛片一级毛片免费毛片| 亚洲国产成人久久一区久久| 国产精品青草视频免费播放| 亚洲午夜久久久久妓女影院| 在线观看免费播放av片| 久久久久亚洲AV无码永不| **毛片免费观看久久精品| 亚洲 日韩 色 图网站| 天天天欲色欲色WWW免费| mm1313亚洲国产精品无码试看| 日批日出水久久亚洲精品tv| 国产精品一区二区三区免费| 亚洲av无码一区二区三区乱子伦| 91青青青国产在观免费影视| 亚洲国产熟亚洲女视频| 免费大黄网站在线观| 中文毛片无遮挡高清免费| 91亚洲国产成人久久精品网站| 无码人妻一区二区三区免费| 激情小说亚洲图片| 亚洲国产精品无码久久一区二区| 1000部夫妻午夜免费 | 精品亚洲成A人无码成A在线观看 | 一级成人生活片免费看| 久久精品国产96精品亚洲| 日本亚洲免费无线码| 国产99久久亚洲综合精品| 亚洲AV日韩AV鸥美在线观看| 天天拍拍天天爽免费视频| a视频在线观看免费| 亚洲色成人WWW永久在线观看| 亚洲男人的天堂在线va拉文|