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

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

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

    隨筆-7  評論-9  文章-0  trackbacks-0

    Smartly load your properties

    Strive for disk location-independent code nirvana

    By Vladimir Roubtsov, JavaWorld.com, 08/08/2003

    http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html

    巧妙地加載屬性

    為獨立于磁盤位置的代碼天堂而努力

    QWhat is the best strategy for loading property and configuration files in Java?

    問:在Java中加載屬性和配置文件最好的策略是什么?

    AWhen you think about how to load an external resource in Java, several options immediately come to mind: files, classpath resources, and URLs. Although all of them eventually get the job done, experience shows that classpath resources and URLs are by far the most flexible and user-friendly options.

    答:當你考慮如何在Java中加載一個外部資源時,多種選擇立即浮現在腦海:文件,類路徑資源和URL。雖然最終它們都能完成工作,但是經驗表明類路徑資源和URL顯然是最靈活最好用的選擇。

    In general, a configuration file can have an arbitrarily complex structure (e.g., an XML schema definition file). But for simplicity, I assume below that we're dealing with a flat list of name-value pairs (the familiar .properties format). There's no reason, however, why you can't apply the ideas shown below in other situations, as long as the resource in question is constructed from an InputStream.

    通常,一個配置文件可以有任意復雜的結構(例如,XML schema定義文件)。但是對于簡單的結構,下面我假設我們正在處理一個簡單的名-值對列表(常見的.properties格式)。然而,只要討論的資源是從一個InputStream構造時,你沒有理由不在以下列出的其它情況下應用那個想法(選擇類路徑資源和URL)。

    Evil java.io.File

    不幸的java.io.File

    Using good old files (via FileInputStream, FileReader, and RandomAccessFile) is simple enough and certainly the obvious route to consider for anyone without a Java background. But it is the worst option in terms of ease of Java application deployment. Using absolute filenames in your code is not the way to write portable and disk position-independent code. Using relative filenames seems like a better alternative, but remember that they are resolved relative to the JVM's current directory. This directory setting depends on the details of the JVM's launch process, which can be obfuscated by startup shell scripts, etc. Determining the setting places an unfair amount of configuration burden on the eventual user (and in some cases, an unjustified amount of trust in the user's abilities). And in other contexts (such an Enterprise JavaBeans (EJB)/Web application server), neither you nor the user has much control over the JVM's current directory in the first place.

    使用舊文件(通過FileInputStream, FileReader, RandomAccessFile)相當簡單,當然考慮到沒有Java背景的人這也是明顯的方式。但是就Java應用部署的簡易性而言文件是最壞的選擇。在你的代碼中使用絕對文件名不是編寫可移植、獨立于磁盤位置的代碼的方式。使用相對文件名好像是一個較好的替代方法,但是記住它們是相對于JVM的當前路徑被解析的。目錄設置依賴于JVM加載進程的細節,像啟動shell腳本或其他加載JVM的方式使得目錄設置變得混亂。將決定如何設置的配置負擔加給最終用戶是不公平的(在某些情況下,對用戶能力的信任是不合理的)。而且在其它環境(像企業JavaBeans(EJB)/Web應用服務器)中,在一開始你和用戶對JVM的當前路徑都沒有太多的控制。

    An ideal Java module is something you add to the classpath, and it's ready to go. Think EJB jars, Web applications packaged in .war files, and other similarly convenient deployment strategies. java.io.File is the least platform-independent area of Java. Unless you absolutely must use them, just say no to files.

    理想的Java模型是你添加到classpath的東西,準備上手吧。考慮一下EJB jar,打包到.war文件的Web應用程序和其他類似方便的部署策略。除非你絕對要使用文件,否則還是對文件說不。

    Classpath resources

    Having dispensed with the above diatribe, let's talk about a better option: loading resources through classloaders. This is much better because classloaders essentially act as a layer of abstraction between a resource name and its actual location on disk (or elsewhere).

    我們不再對File進行抨擊,讓我們討論一種更好的選擇:通過類加載器加載資源。因為類加載器在資源名稱和在磁盤上(或其他地方)的實際位置之間主要扮演了一個抽象層角色,這是相當好的。

    Let's say you need to load a classpath resource that corresponds to a some/pkg/resource.properties file. I use classpath resource to mean something that's packaged in one of the application jars or added to the classpath before the application launches. You can add to the classpath via the -classpath JVM option each time the application starts or by placing the file in the <jre home>\classes directory once and for all. The key point is that deploying a classpath resource is similar to deploying a compiled Java class, and therein lies the convenience.

    比如說你需要加載一個類路徑資源,它對應some/pkg/resource.properties文件。我使用類路徑資源意味著這個資源要打包到應用程序的某個jar中或者在應用程序啟動之前加到類路徑中。你可以在應用程序每次啟動時通過-classpath這個JVM參數添加類路徑,或者干脆把那個文件放到<jrehome>\classes目錄中。關鍵點是部署一個類路徑資源類似于部署一個已編譯的Java,方便的地方就在于此。

    You can get at some/pkg/resource.properties programmatically from your Java code in several ways. First, try:

    ClassLoader.getResourceAsStream ("some/pkg/resource.properties");

      Class.getResourceAsStream ("/some/pkg/resource.properties");

      ResourceBundle.getBundle ("some.pkg.resource");

    你可以通過多種方式在你的Java代碼中以編程方式訪問到some/pkg/resource.properties。首先,試一試:

    ClassLoader.getResourceAsStream ("some/pkg/resource.properties");

      Class.getResourceAsStream ("/some/pkg/resource.properties");

      ResourceBundle.getBundle ("some.pkg.resource");

    Additionally, if the code is in a class within a some.pkg Java package, then the following works as well:

    Class.getResourceAsStream ("resource.properties");

    此外,如果代碼在some.pkg這個Java包中的類,以下方式也可以:

    Class.getResourceAsStream ("resource.properties");

    Note the subtle differences in parameter formatting for these methods. All getResourceAsStream() methods use slashes to separate package name segments, and the resource name includes the file extension. Compare that with resource bundles where the resource name looks more like a Java identifier, with dots separating package name segments (the .properties extension is implied here). Of course, that is because a resource bundle does not have to be backed by a .properties file: it can be a class, for a example.

    注意這些方法在參數格式上的細微差別。所有getResourceAsStream()方法都是使用斜杠(/)來分隔包名段,而且資源文件名包括文件擴展名。比較一下,使用資源包(resource bundle)時資源名看上去更像Java標識符,它是以點(.)分隔包名段(這里的.properties擴展名被隱含了)。當然,一個資源包(resource bundle)不必非要是.properties文件:例如,它可以是一個類。

    To slightly complicate the picture, java.lang.Class's getResourceAsStream() instance method can perform package-relative resource searches (which can be handy as well, see "Got Resources?"). To distinguish between relative and absolute resource names, Class.getResourceAsStream() uses leading slashes for absolute names. In general, there's no need to use this method if you are not planning to use package-relative resource naming in code.

    對于稍復雜的情況,java.lang.Class getResourceAsStream()實例方法可以執行相對于包的資源搜索(這是相當方便的,參見 Got Resources?)。為了區別相對和絕對資源名稱Class.getResourceAsStream()對絕對名稱使用前導斜杠(/)。一般來說,如果你沒有計劃在代碼中使用相對于包的資源命名沒有必要使用這種方法。

    It is easy to get mixed up in these small behavioral differences for ClassLoader.getResourceAsStream(), Class.getResourceAsStream(), and ResourceBundle.getBundle(). The following table summarizes the salient points to help you remember:

    很容易混淆ClassLoader.getResourceAsStream(), Class.getResourceAsStream(), ResourceBundle.getBundle()在行為上的細小區別。下表總結了一些顯著點來幫助你記憶:


     


     

    Behavioral differences

    Method

    (方法)

    Parameter format

    (參數格式)

    Lookup failure behavior

    (查詢失敗行為)

    Usage example

    (用法示例)

    ClassLoader.
    getResourceAsStream()

    "/"-separated names; no leading "/" (all names are absolute)

    "/"-分隔名稱; 沒有前導"/" (所有名稱都是絕對名稱)

    Silent (returns null)

    this.getClass().getClassLoader()
    .getResourceAsStream
    ("some/pkg/resource.properties")

    Class.
    getResourceAsStream()

    "/"-separated names; leading "/" indicates absolute names; all other names are relative to the class's package

    "/"-分隔名稱; 前導 "/" 表示絕對名稱;所有其他名稱是相對于類所在包的。

    Silent (returns null)

    this.getClass()
    .getResourceAsStream
    ("resource.properties")

    ResourceBundle.
    getBundle()

    "."-separated names; all names are absolute; .properties suffix is implied

    "."-分隔名稱;所有名稱都是絕對名稱; .properties后綴被隱含。

    Throws unchecked
    java.util.MissingResourceException

    ResourceBundle.getBundle
    ("some.pkg.resource")

     


     

    From data streams to java.util.Properties

    You might have noticed that some previously mentioned methods are half measures only: they return InputStreams and nothing resembling a list of name-value pairs. Fortunately, loading data into such a list (which can be an instance of java.util.Properties) is easy enough. Because you will find yourself doing this over and over again, it makes sense to create a couple of helper methods for this purpose.

    你可能已經注意到前面提到的一些方法僅是折衷辦法:它們返回的是InputStream,沒有類似一個名-值對的列表的東西。幸運地是,將數據加載到這樣一個列表(可以是java.util.Properties的一個實例)是相當容易的。因為你將發現你在反復做這個加載工作,所以為這個目的創建一組幫助方法是有意義的。

    The small behavioral difference among Java's built-in methods for classpath resource loading can also be a nuisance, especially if some resource names were hardcoded but you now want to switch to another load method. It makes sense to abstract away little things like whether slashes or dots are used as name separators, etc. Without further ado, here's my PropertyLoader API that you might find useful (available with this article's download):

    類路徑資源加載的Java內建方法之間的細小行為差別可能是一個令人討厭的事情,尤其是有些資源名稱是硬編碼的但是你現在想切換到另一個加載方法。抽象出一些東西像不管是斜杠(/)還是點(.)作為分隔符等等就變得有意義了。不再羅嗦,這是我的PropertyLoader API你可能發現它是有用的。

    package com.jeffma.util;

     

    import java.io.InputStream;

    import java.util.Enumeration;

    import java.util.Locale;

    import java.util.Properties;

    import java.util.ResourceBundle;

     

    public abstract class PropertyLoader {

        /**

         * Looks up a resource named 'name' in the classpath. The resource must map

         * to a file with .properties extention. The name is assumed to be absolute

         * and can use either "/" or "." for package segment separation with an

         * optional leading "/" and optional ".properties" suffix. Thus, the

         * following names refer to the same resource:

         *

         * <pre>

         * some.pkg.Resource

         * some.pkg.Resource.properties

         * some/pkg/Resource

         * some/pkg/Resource.properties

         * /some/pkg/Resource

         * /some/pkg/Resource.properties

         * </pre>

         *

         * @param name

         *            classpath resource name [may not be null]

         * @param loader

         *            classloader through which to load the resource [null is

         *            equivalent to the application loader]

         *

         * @return resource converted to java.util.Properties [may be null if the

         *         resource was not found and THROW_ON_LOAD_FAILURE is false]

         * @throws IllegalArgumentException

         *             if the resource was not found and THROW_ON_LOAD_FAILURE is

         *             true

         */

        public static Properties loadProperties(String name, ClassLoader loader) {

           if (name == null)

               throw new IllegalArgumentException("null input: name");

     

           if (name.startsWith("/"))

               name = name.substring(1);

     

           if (name.endsWith(SUFFIX))

               name = name.substring(0, name.length() - SUFFIX.length());

     

           Properties result = null;

     

           InputStream in = null;

           try {

               if (loader == null)

                  loader = ClassLoader.getSystemClassLoader();

     

               if (LOAD_AS_RESOURCE_BUNDLE) {

                  name = name.replace('/', '.');

                  // Throws MissingResourceException on lookup failures:

                  final ResourceBundle rb = ResourceBundle.getBundle(name, Locale

                         .getDefault(), loader);

     

                  result = new Properties();

                  for (Enumeration keys = rb.getKeys(); keys.hasMoreElements();) {

                      final String key = (String) keys.nextElement();

                      final String value = rb.getString(key);

     

                      result.put(key, value);

                  }

               } else {

                  name = name.replace('.', '/');

     

                  if (!name.endsWith(SUFFIX))

                      name = name.concat(SUFFIX);

     

                  // Returns null on lookup failures:

                  in = loader.getResourceAsStream(name);

                  if (in != null) {

                      result = new Properties();

                      result.load(in); // Can throw IOException

                  }

               }

           } catch (Exception e) {

               result = null;

           } finally {

               if (in != null)

                  try {

                      in.close();

                  } catch (Throwable ignore) {

                  }

           }

     

           if (THROW_ON_LOAD_FAILURE && (result == null)) {

               throw new IllegalArgumentException("could not load ["

                      + name

                      + "]"

                      + " as "

                      + (LOAD_AS_RESOURCE_BUNDLE ? "a resource bundle"

                             : "a classloader resource"));

           }

     

           return result;

        }

     

        /**

         * A convenience overload of {@link #loadProperties(String, ClassLoader)}

         * that uses the current thread's context classloader.

         */

        public static Properties loadProperties(final String name) {

           return loadProperties(name, Thread.currentThread()

                  .getContextClassLoader());

        }

     

        private static final boolean THROW_ON_LOAD_FAILURE = true;

        private static final boolean LOAD_AS_RESOURCE_BUNDLE = false;

        private static final String SUFFIX = ".properties";

    } // End of class

    The Javadoc comment for the loadProperties() method shows that the method's input requirements are quite relaxed: it accepts a resource name formatted according to any of the native method's schemes (except for package-relative names possible with Class.getResourceAsStream()) and normalizes it internally to do the right thing.

    loadProperties()方法的Javadoc注釋表明方法的輸入需求是很隨意的:它接受一個根據任何原生方法模式格式化的資源名稱(除了使用Class.getResourceAsStream()相對于包的名稱),內部將資源名稱標準化來做正確的事情。

    The shorter loadProperties() convenience method decides which classloader to use for loading the resource. The solution shown is reasonable but not perfect; you might consider using techniques described in "Find a Way Out of the ClassLoader Maze" instead.

    更簡捷的loadProperties()方法決定了哪個classloader用于加載資源。已列出的解決方案是有道理的但不是完美的;你可考慮使用"Find a Way Out of the ClassLoader Maze"中描述的技術替代它。

    Note that two conditional compilation constants control loadProperties() behavior, and you can tune them to suit your tastes:

    • THROW_ON_LOAD_FAILURE selects whether loadProperties() throws an exception or merely returns null when it can't find the resource
    • LOAD_AS_RESOURCE_BUNDLE selects whether the resource is searched as a resource bundle or as a generic classpath resource

    說明一下兩個條件編譯常量控制loadProperties()的行為,你可以調整它們以適應你的風格:

    • THROW_ON_LOAD_FAILUREloadProperties()不能找到資源時,選擇 拋出異常還是僅僅返回runll
    • LOAD_AS_RESOURCE_BUNDLE 選擇資源是作為一個資源包被搜索還是作為一個普通類路徑資源被搜索。

    Setting LOAD_AS_RESOURCE_BUNDLE to true isn't advantageous unless you want to benefit from localization support built into java.util.ResourceBundle. Also, Java internally caches resource bundles, so you can avoid repeated disk file reads for the same resource name.

    除非你想從java.util.ResourceBundle中的本地化支持獲得好處,將LOAD_AS_RESOURCE_BUNDLE設置為true沒有什么優勢。還有,Java內部緩存了資源包,所以你可以避免對于同一個資源名的磁盤文件重復讀。

    More things to come

    I intentionally omitted an interesting classpath resource loading method, ClassLoader.getResources(). Despite its infrequent use, ClassLoader.getResources() allows for some very intriguing options in designing highly customizable and easily configurable applications.

    我有意忽略了一個有意思的類路徑資源加載方法,ClassLoader.getResources()。盡管它用的不多,但是在設計高度可自定義的和易配置的應用程序中ClassLoader.getResources()考慮到一些很有趣的選項。

    I didn't discuss ClassLoader.getResources() in this article because it's worthy of a dedicated article. As it happens, this method goes hand in hand with the remaining way to acquire resources: java.net.URLs. You can use these as even more general-purpose resource descriptors than classpath resource name strings. Look for more details in the next Java Q&A installment.

    在這篇文章中我不討論ClassLoader.getResources(),因為這值得用一篇專門的文章來討論它。碰巧,這個方法要與獲取資源的另一種方式(java.net.URLs)一起使用。你可以使用它們作為比類路徑資源名稱串更通用目的資源描述符。在下一期Java Q&A中找到更多內容。

    About the author

    關于作者

    Vladimir Roubtsov has programmed in a variety of languages for more than 13 years, including Java since 1995. Currently, he develops enterprise software as a senior engineer for Trilogy in Austin, Texas.

    posted on 2010-06-29 14:21 jeffma 閱讀(599) 評論(2)  編輯  收藏

    評論:
    # re: 巧妙地加載屬性(翻譯) 2010-06-29 14:22 | jeffma
    第一次發布,歡迎指正。  回復  更多評論
      
    # re: 巧妙地加載屬性(翻譯) 2012-11-21 22:35 | 莫老酒
    翻譯的很不錯啊,我引用一下。  回復  更多評論
      

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲美女在线国产| 亚洲成人福利网站| 久久久久久国产精品免费无码| 久久亚洲伊人中字综合精品| 亚洲黄色免费网站| 四虎一区二区成人免费影院网址 | 成人女人A级毛片免费软件| 亚洲爆乳成av人在线视菜奈实| 国产偷国产偷亚洲高清日韩| 很黄很黄的网站免费的| 中国xxxxx高清免费看视频| 亚洲夂夂婷婷色拍WW47| 精品亚洲一区二区三区在线播放 | 亚洲精品国产va在线观看蜜芽| 久别的草原电视剧免费观看| 亚洲国产综合AV在线观看| 亚洲色偷偷偷鲁综合| 免费电视剧在线观看| 国内精品免费久久影院| 亚洲成年网站在线观看| 国产亚洲一区二区精品| 国产网站免费观看| 91禁漫免费进入| 四虎一区二区成人免费影院网址| 亚洲视频一区在线观看| 亚洲AV无码乱码在线观看牲色| a视频在线免费观看| 黄色三级三级免费看| 97久久精品亚洲中文字幕无码| 亚洲一区免费观看| 亚洲国产乱码最新视频| 亚洲中文字幕无码日韩| 成年性午夜免费视频网站不卡| 又硬又粗又长又爽免费看| 久久精品国产亚洲av高清漫画 | 最近中文字幕mv免费高清视频7| 羞羞漫画在线成人漫画阅读免费| 成人国产mv免费视频| 国产日韩精品无码区免费专区国产 | 毛片大全免费观看| 中文字幕不卡免费高清视频|