<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)系 :: 聚合  :: 管理

    目前我們的電影服務只提供了對電影信息的訪問服務,現(xiàn)在我們要再增加兩項級服務,分別用來訪問導演和演員信息。加上原先的電信信息服務,我們把 URI 統(tǒng)一放到 /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 提供了動態(tài)資源綁定的功能,讓我們能夠?qū)@種情況做一些整理。

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

    @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 方法根據(jù)服務的名稱返回相應的服務實例,注意該方法只有一個 @Path 注解,因為它并不清楚請求的具體內(nèi)容;返回對象的類型為 Object,表明動態(tài)資源定位不要求服務類實現(xiàn)相同的接口,只需要它們的方法帶有相應的 JAX-RS 注解,就能夠被 JAX-RS 自動發(fā)現(xiàn)和處理(專業(yè)術語稱為 introspect,內(nèi)省),以 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 方法返回結(jié)果。比起最開始那三個簡單的類,雖然多了一層調(diào)用,但換來了更加清晰的結(jié)構(gòu)。

    動態(tài)資源定位是一個非常靈活強大的功能,用好的話,完全可以把 URI 層次整理成一個類似于文件目錄結(jié)構(gòu)的抽象文件系統(tǒng)。

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

    籠子大了什么鳥都有。同樣的道理,不論多么細心地設計 URI 結(jié)構(gòu),在系統(tǒng)復雜到一定程度后,仍然難以避免路徑?jīng)_突。為此,JAX-RS 使用一些規(guī)則來定義路徑匹配的優(yōu)先級。

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

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

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

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

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

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

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

    話說,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 沒有經(jīng)過代碼審閱、沒有經(jīng)過測試、沒有經(jīng)過……然后,@author Xueming Shen,真是丟咱華夏民族的臉……

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

    比較內(nèi)容 官方 ZFS 山寨 ZFS
    實現(xiàn)方式 另起爐灶,用純 Java 重新實現(xiàn)了對 ZIP 文件格式的處理代碼。 基于 ZipFileZipInputStream 這兩個已經(jīng)穩(wěn)定多年的類,但涉及了大量本地代碼調(diào)用,也許會影響性能。
    讀操作 支持,且通過解壓到臨時文件支持隨機訪問。 支持,但不支持隨機訪問。
    寫操作 通過解壓到臨時文件進行支持,但無法檢測到其他進程對同一個 ZIP 文件的寫操作,不適用于并發(fā)環(huán)境。 不支持。ZIP 文件事實上是一個整體,對內(nèi)部條目的任何修改都可能導致重構(gòu)整個文件,因此所謂的寫操作必須通過臨時文件來處理,效率低下,意義不大,而且難以處理嵌套 ZIP 文件。這也符合我的原則:不解壓。
    嵌套 ZIP 文件 不支持。 支持,當然讀取嵌套 ZIP 文件會慢一些。
    反斜線分隔符 不支持,直接瓜掉。 支持,且和標準的斜線分隔符區(qū)別對待。例如,/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);
            // 默認目錄,用于創(chuàng)建和解析相對路徑。默認為“/”。
            env.put("defaultDirectory", "/dir/");
    
            // 從文件創(chuàng)建一個 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")) {
                    // 創(chuàng)建一個嵌套的 ZFS。
                    try (FileSystem nestedZfs = zfs.provider().newFileSystem(path, env)) {
                        // 此處省略若干行。
                    }
                }
            }
        

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

    posted @ 2011-12-01 13:12 蜀山兆孨龘 閱讀(2429) | 評論 (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");
        

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

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

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

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

    該類構(gòu)造的時候除了必須要指定線程數(shù)量,還可以傳入一個 Runnable 對象,它的 run 方法將在到達公共屏障點后執(zhí)行一次。子線程完成計算后,分別調(diào)用 CyclicBarrier#await 方法進入阻塞狀態(tài),直到其他所有子線程都調(diào)用了 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. 兩者在構(gòu)造的時候都必須指定線程數(shù)量,而且該數(shù)量在構(gòu)造后不可修改。
    2. 前者可以傳入一個 Runnable 對象,在任務完成后自動調(diào)用,執(zhí)行者為某個子線程;后者可在 await 方法后手動執(zhí)行一段代碼實現(xiàn)相同的功能,但執(zhí)行者為主線程。
    3. 前者在每個子線程上都進行阻塞,然后一起放行;后者僅在主線程上阻塞一次。
    4. 前者可以重復使用;后者的倒計數(shù)器歸零后就作廢了。
    5. 兩者的內(nèi)部實現(xiàn)完全不同。

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

    僅列出標題
    共8頁: 上一頁 1 2 3 4 5 6 7 8 下一頁 
    主站蜘蛛池模板: 亚洲字幕在线观看| 国产亚洲精品xxx| 免费一级毛片正在播放| 免费v片在线观看无遮挡| 国产亚洲AV手机在线观看| 日本亚洲欧洲免费天堂午夜看片女人员 | 好大好硬好爽免费视频| 免费二级毛片免费完整视频| 综合亚洲伊人午夜网| 亚洲视频精品在线| 亚洲日本一线产区和二线产区对比| 日韩精品亚洲专区在线影视| 亚洲阿v天堂在线2017免费| 免费黄网站在线看| 久久久久国产精品免费免费搜索| 国产色婷婷精品免费视频| 区久久AAA片69亚洲| 亚洲天堂一区二区三区| 337P日本欧洲亚洲大胆艺术图| 中文字幕在线免费播放| 美女视频黄是免费的网址| 波多野结衣一区二区免费视频 | 国产免费观看视频| 亚洲AV无码专区国产乱码电影| 中文字幕无码精品亚洲资源网久久| 一级毛片视频免费观看| 亚洲精品视频在线观看免费| 国产传媒在线观看视频免费观看| 亚洲成色在线综合网站| 最新国产精品亚洲| 91免费福利视频| 成人免费男女视频网站慢动作| 亚洲啪啪综合AV一区| 亚洲最大的成人网| 亚洲免费观看视频| 国产乱人免费视频| 亚洲黄色网站视频| 免费一级做a爰片久久毛片潮| 91黑丝国产线观看免费 | 国产成人一区二区三区免费视频| 亚洲va在线va天堂va888www|