<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 :: 首頁 ::  :: 聯系 :: 聚合  :: 管理

    目前我們的電影服務只提供了對電影信息的訪問服務,現在我們要再增加兩項級服務,分別用來訪問導演和演員信息。加上原先的電信信息服務,我們把 URI 統一放到 /ms/rest/service/ 的子路徑下。最先想到的方法就是為這三個 URI 分別寫 JAX-RS 服務:

    @Singleton
    @Path("service/movie")
    public class MovieService {
        // 此處省略若干行
    }
    
    @Singleton
    @Path("service/director")
    public class DirectorService {
        // 此處省略若干行
    }
    
    @Singleton
    @Path("service/director")
    public class ActorService {
        // 此處省略若干行
    }
        

    這種寫法的缺點就是讓三個本來有點關系(父級 URI 相同)的服務被放到了毫不相干的三個類里面,不一個個類地查看注解難以看出這點關系。為此,JAX-RS 提供了動態資源綁定的功能,讓我們能夠對這種情況做一些整理。

    首先,我們引入一個服務定位器來處理集中管理這三個子級服務:

    @Singleton
    @Path("service")
    public class ServiceLocator {
        @Inject
        private MovieService movieService;
        @Inject
        private DirectorService directorService;
        @Inject
        private ActorService actorService;
        private Map<String, Object> serviceMap;
    
        @PostConstruct
        private initServiceMap() {
            serviceMap = new HashMap<>();
            serviceMap.put("movie", movieService);
            serviceMap.put("director", directorService);
            serviceMap.put("actor", actorService);
        }
    
        @Path("{name}")
        public Object locateService(@PathParam("name") String name) {
            Object service = serviceMap.get(name);
            if (service == null) {
                throw new WebApplicationException(Status.SERVICE_UNAVAILABLE);
            }
            return service;
        }
    }
        

    該類中的 locateService 方法根據服務的名稱返回相應的服務實例,注意該方法只有一個 @Path 注解,因為它并不清楚請求的具體內容;返回對象的類型為 Object,表明動態資源定位不要求服務類實現相同的接口,只需要它們的方法帶有相應的 JAX-RS 注解,就能夠被 JAX-RS 自動發現和處理(專業術語稱為 introspect,內省),以 MovieService 為例:

    @Singleton
    public class MovieService {
        @GET
        @Path("{id}")
        @Produces(MediaType.APPLICATION_JSON)
        public Movie find(@PathParam("id") int id) {
            Movie movie = movieDao.get(id);
            if (movie != null) {
                return movie;
            } else {
                throw new WebApplicationException(Status.NOT_FOUND);
            }
        }
    
        // 此處省略若干行
    }
    

    這樣,每個請求實際上都由兩個類先后處理。例如,處理請求 GET /ms/rest/service/movie/1 的時候,先由 ServiceLocator 返回相配的服務實例 movieService,然后再由該實例的 find 方法返回結果。比起最開始那三個簡單的類,雖然多了一層調用,但換來了更加清晰的結構。

    動態資源定位是一個非常靈活強大的功能,用好的話,完全可以把 URI 層次整理成一個類似于文件目錄結構的抽象文件系統。

    posted @ 2011-12-21 16:00 蜀山兆孨龘 閱讀(2940) | 評論 (0)編輯 收藏

    籠子大了什么鳥都有。同樣的道理,不論多么細心地設計 URI 結構,在系統復雜到一定程度后,仍然難以避免路徑沖突。為此,JAX-RS 使用一些規則來定義路徑匹配的優先級。

    如果某個請求路徑可以對上多個 URI 匹配模式,那么 JAX-RS 就把可能匹配上的 URI 模式先拼接完整,按照下列規則依次進行比較,直到找出最適合的匹配模式:

    1. 首先,字面字符數量更多的 URI 模式優先。“字面字符”就是寫死的路徑段,不包含路徑分隔符 / 和模板參數。例如 /ms/rest/movie/{id : \\d+} 包含 11 個字面字符。
    2. 其次,模板參數個數最多的 URI 模式優先。例如 /ms/rest/movie/{id : \\d+} 帶一個模板參數。
    3. 最后,含正則表達式的模板參數個數最多的 URI 模式優先。例如 /ms/rest/movie/{id : \\d+} 帶一個含正則表達式的模板參數。

    現在看一個例子。回顧一下,/ms/rest/movie/{id : \\d+} 已經用來根據 ID 獲取電影信息。為了制造麻煩,現在引入 /ms/rest/movie/{title} 來根據電影標題獲取電影信息。先請你猜一猜 /ms/rest/movie/300 代表啥?ID 為 300 的神秘電影,還是我們可愛的勇士?只能跟著規則一條一條地看:

    1. 首先,兩個 URI 匹配模式的字面字符都是 11,下一步。
    2. 其次,兩個 URI 匹配模式都帶一個模板參數,下一步。
    3. 最后,只有 /ms/rest/movie/{id : \\d+} 帶了一個含正則表達式的模板參數,勝利!所以返回 ID 為 300 的片片。

    傳說這三條規則能夠覆蓋 90% 以上的情景。不過我們馬上就能造出一個打破規則的東西:/ms/rest/movie/{title : [ \\w]+}。經過測試,/ms/rest/movie/300 會匹配上 /ms/rest/movie/{id : \\d+}。如何解釋?JAX-RS 規范文檔 3.7.2 定義了完整的匹配規則,對于這兩個簡單的 URI 匹配模式,似乎一直進行到底都無法比較出優先級。莫非有另外的潛規則?或者是 JAX-RS 的實現(參考實現為 Jersey)自行規定?但無論如何,搞出這種怪物本身就是一個設計錯誤,所以也不必去深究原因。

    posted @ 2011-12-07 15:10 蜀山兆孨龘 閱讀(2328) | 評論 (0)編輯 收藏

    以前我曾用兩個類(ZipItemZipSystem)實現了一個簡單的 ZIP 文件系統(以下簡稱 ZFS)。其實這兩個小類挺好用的,而且支持嵌套的 ZIP 文件,但是,但是……JDK 7 丟下來一枚叫做 NIO2 的笑氣炸彈,引入了一套標準的文件系統 API,我承認我中彈了,手癢了,又根據這套 API 重新實現了 ZIP 文件系統,終于在今天初步完工,哈。

    話說,JDK 7 其實捆綁銷售了一個 ZFS,demo 目錄下還有源代碼。可……它達不到我的奢求,而且 BUG 不少。隨便逮兩個:

            // com.sun.nio.zipfs.ZipFileSystemProvider 類中的方法
            @Override
            public Path getPath(URI uri) {
    
                String spec = uri.getSchemeSpecificPart();
                int sep = spec.indexOf("!/");
                if (sep == -1)
                    throw new IllegalArgumentException("URI: "
                        + uri
                        + " does not contain path info ex. jar:file:/c:/foo.zip!/BAR");
                // 難怪該方法始終拋 IllegalArgumentException 異常,原來你小子把文件的 URI
                // 當成 ZFS 的 URI 在用……
                return getFileSystem(uri).getPath(spec.substring(sep + 1));
            }
    
            // com.sun.nio.zipfs.ZipFileSystem 類中的方法
            @Override
            public PathMatcher getPathMatcher(String syntaxAndInput) {
                int pos = syntaxAndInput.indexOf(':');
                // 丫的,pos == syntaxAndInput.length()?!誰寫的?抓出來鞭尸。
                if (pos <= 0 || pos == syntaxAndInput.length()) {
                    throw new IllegalArgumentException();
        

    很明顯,官方 ZFS 沒有經過代碼審閱、沒有經過測試、沒有經過……然后,@author Xueming Shen,真是丟咱華夏民族的臉……

    下面列個表格詳細比較官方 ZFS 和山寨 ZFS:

    比較內容 官方 ZFS 山寨 ZFS
    實現方式 另起爐灶,用純 Java 重新實現了對 ZIP 文件格式的處理代碼。 基于 ZipFileZipInputStream 這兩個已經穩定多年的類,但涉及了大量本地代碼調用,也許會影響性能。
    讀操作 支持,且通過解壓到臨時文件支持隨機訪問。 支持,但不支持隨機訪問。
    寫操作 通過解壓到臨時文件進行支持,但無法檢測到其他進程對同一個 ZIP 文件的寫操作,不適用于并發環境。 不支持。ZIP 文件事實上是一個整體,對內部條目的任何修改都可能導致重構整個文件,因此所謂的寫操作必須通過臨時文件來處理,效率低下,意義不大,而且難以處理嵌套 ZIP 文件。這也符合我的原則:不解壓。
    嵌套 ZIP 文件 不支持。 支持,當然讀取嵌套 ZIP 文件會慢一些。
    反斜線分隔符 不支持,直接瓜掉。 支持,且和標準的斜線分隔符區別對待。例如,/abc//abc\ 算不同的文件,實際上這兩個能夠并存于 ZIP 文件中。
    空目錄名 不支持,直接瓜掉。 支持。例如 /a/b/a//b 是兩個可以并存且不同的文件。

    山寨 ZFS 的用法示例:

            Map<String, Object> env = new HashMap<>();
            // 用于解碼 ZIP 條目名。默認為 Charset.defaultCharset()。
            env.put("charset", StandardCharsets.UTF_8);
            // 指示是否自動探測嵌套的 ZIP 文件。默認為 false。
            env.put("autoDetect", true);
            // 默認目錄,用于創建和解析相對路徑。默認為“/”。
            env.put("defaultDirectory", "/dir/");
    
            // 從文件創建一個 ZFS。
            try (FileSystem zfs = FileSystems.newFileSystem(
                    URI.create("zip:" + Paths.get("docs.zip").toUri()), env)) {
                Path path = zfs.getPath("app.jar");
                if ((Boolean) Files.getAttribute(path, "isZip")) {
                    // 創建一個嵌套的 ZFS。
                    try (FileSystem nestedZfs = zfs.provider().newFileSystem(path, env)) {
                        // 此處省略若干行。
                    }
                }
            }
        

    最后雙手奉上源代碼:請猛擊此處!

    posted @ 2011-12-01 13:12 蜀山兆孨龘 閱讀(2435) | 評論 (1)編輯 收藏

    先看出錯的代碼:

            public class Holder<T> {
                private T value;
    
                public Holder() {
                }
    
                public Holder(T value) {
                    this.value = value;
                }
    
                public void setValue(T value) {
                    this.value = value;
                }
    
                // 此處省略若干行。
            }
    
            Holder<Object> holder = new Holder<>("xxx");
        

    看起來還好,但編譯的時候卻報錯:

    Uncompilable source code - incompatible types
      required: zhyi.test.Holder<java.lang.Object>
      found:    zhyi.test.Holder<java.lang.String>

    老老實實把類型寫出來就沒問題:

            Holder<Object> holder = new Holder<Object>("xxx");
        

    如果非要用鉆石運算符的話,可以采取下列兩種方式之一:

            // 使用默認構造器,再調用 setValue 方法。
            Holder<Object> holder = new Holder<>();
            holder.setValue("xxx");
    
            // 使用泛型通配符,但之后就不能調用 setValue 了,否則編譯出錯。
            Holder<? extends Object> holder = new Holder<>("xxx");
        

    posted @ 2011-11-11 11:06 蜀山兆孨龘 閱讀(1571) | 評論 (0)編輯 收藏

    CyclicBarrier 的功能類似于前面說到的 CountDownLatch,用于讓多個線程(子任務)互相等待,直到共同到達公共屏障點(common barrier point),在這個點上,所有的子任務都已完成,從而主任務完成。

    該類構造的時候除了必須要指定線程數量,還可以傳入一個 Runnable 對象,它的 run 方法將在到達公共屏障點后執行一次。子線程完成計算后,分別調用 CyclicBarrier#await 方法進入阻塞狀態,直到其他所有子線程都調用了 await

    下面仍然以運動員準備賽跑為例來說明 CyclicBarrier 的用法:

                final int count = 8;
                System.out.println("運動員開始就位。");
    
                final CyclicBarrier cb = new CyclicBarrier(count, new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("比賽開始...");
                    }
                });
    
                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 + " 號運動員就位。");
                            try {
                                cb.await();
                            } catch (InterruptedException | BrokenBarrierException ex) {
                            }
                        }
                    }.start();
                }
                System.out.println("等待所有運動員就位...");
        

    運行輸出(可能)為:

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

    最后看看 CyclicBarrierCountDownLatch 的主要異同:

    1. 兩者在構造的時候都必須指定線程數量,而且該數量在構造后不可修改。
    2. 前者可以傳入一個 Runnable 對象,在任務完成后自動調用,執行者為某個子線程;后者可在 await 方法后手動執行一段代碼實現相同的功能,但執行者為主線程。
    3. 前者在每個子線程上都進行阻塞,然后一起放行;后者僅在主線程上阻塞一次。
    4. 前者可以重復使用;后者的倒計數器歸零后就作廢了。
    5. 兩者的內部實現完全不同。

    posted @ 2011-10-17 11:21 蜀山兆孨龘 閱讀(1800) | 評論 (0)編輯 收藏

    僅列出標題
    共8頁: 上一頁 1 2 3 4 5 6 7 8 下一頁 
    主站蜘蛛池模板: 亚欧色视频在线观看免费| 九九九精品视频免费| 91制片厂制作传媒免费版樱花| 国产成人亚洲精品影院| 无忧传媒视频免费观看入口| 在线观看亚洲免费| 亚洲av无码专区国产不乱码| 国产jizzjizz视频全部免费| 鲁啊鲁在线视频免费播放| 免费va在线观看| 一个人看的免费观看日本视频www 一个人看的免费视频www在线高清动漫 | 亚洲日本人成中文字幕| 男女免费观看在线爽爽爽视频 | 亚洲精品无码久久久久秋霞 | 久久精品国产69国产精品亚洲| 亚洲国产免费综合| 亚洲精品亚洲人成在线观看| 黄色免费在线网站| 亚洲精品乱码久久久久久下载 | 亚洲午夜久久久久久久久久| 国产成人无码区免费网站| 久久亚洲国产视频| 青草草色A免费观看在线| 亚洲乱码av中文一区二区| 亚洲成?v人片天堂网无码| 中文日本免费高清| 亚洲欧洲精品久久| 日韩免费一级毛片| 最新久久免费视频| 亚洲一区二区三区四区视频 | 亚洲自偷自偷图片| 在免费jizzjizz在线播 | 精品一区二区三区免费视频| 亚洲欧洲第一a在线观看| 91香蕉视频免费| 日本视频免费观看| 亚洲春色另类小说| 亚洲一区无码精品色| aⅴ在线免费观看| 青娱乐在线视频免费观看| 亚洲天堂久久精品|