2013-07-23 44 views
5

我有一些代碼,使用一個持有大量硬編碼常量的類。這是什麼樣子:如何從硬編碼的靜態配置文件切換到.properties文件?

class Constants{ 
    public static final String name1 = "value1"; 
    public static final String name2 = "value2"; 
    public static final Integer value3 = 3; 
    ... and so on 
} 

這些常量像Constants.name1代碼到處被使用。

我現在需要做的是使配置文件中的這些常量值可能爲*.properties文件。

我的問題是:什麼是最好的方式來做到這一點,不得不重寫儘可能少的代碼?

我想過使用一個單獨的配置類,它在實例化時從文件中讀取屬性,但隨後我將不得不將所有靜態值的調用替換爲對該類的實例的調用,並且必須更改現有的方法將這個配置實例傳遞給它們。有沒有更好的辦法?

+0

我想您的概念可以通過使用Eclipse的NLS概念來實現。雖然它是osgi jar,但它可以用於非osgi項目。 –

回答

4

這裏是一塊我已經在過去使用的代碼 - 可以適應您的例子:

public enum Configuration { 

    PROPERTY1("property1.name", "default_value_1"), 
    PROPERTY2("property2.name", "default_value_2"); 

    private final String key; 
    private String defaultValue; 

    Configuration(String key) { 
     this(key, NA); 
    } 

    Configuration(String key, String defaultValue) { 
     this.key = key; 
     this.defaultValue = defaultValue; 
    } 
    private final static Logger logger = LoggerFactory.getLogger(Configuration.class); 
    private final static String NA = "n.a."; 
    private final static String CONFIG_FILE = "properties/config.properties"; 
    private final static String NOT_A_VALID_KEY = "Not a valid property key"; 
    private final static Map<Configuration, String> configuration = new EnumMap<>(Configuration.class); 

    static { 
     readConfigurationFrom(CONFIG_FILE); 
    } 

    private static void readConfigurationFrom(String fileName) { 
     logger.info("Reading resource: {}", fileName); 
     try (InputStream resource = Configuration.class.getClassLoader().getResourceAsStream(fileName);) { 
      Properties properties = new Properties(); 
      properties.load(resource); //throws a NPE if resource not founds 
      for (String key : properties.stringPropertyNames()) { 
       configuration.put(getConfigurationKey(key), properties.getProperty(key)); 
      } 
     } catch (IllegalArgumentException | IOException | NullPointerException e) { 
      logger.error("Error while reading the properties file {}", fileName, e); 
      populateDefaultValues(); 
     } 
    } 

    private static Configuration getConfigurationKey(String key) { 
     for (Configuration c : values()) { 
      if (c.key.equals(key)) { 
       return c; 
      } 
     } 
     throw new IllegalArgumentException(NOT_A_VALID_KEY + ": " + key); 
    } 

    private static void populateDefaultValues() { 
     for (Configuration c : values()) { 
      configuration.put(c, c.defaultValue); 
     } 
    } 

    /** 
    * @return the property corresponding to the key or null if not found 
    */ 
    public String get() { 
     return configuration.get(this); 
    } 
} 
+0

+1;我喜歡使用枚舉類,儘管乍一看它延遲和/或忽略配置類型。 –

+0

@DaveNewton是的,它只是基於字符串。但是同樣的原理可以應用於私有靜態最終成員,它們可以在靜態塊中初始化。 – assylias

+0

這是唯一的非手動解決方案,+1 – skiwi

3

使用Properties.load(...)加載文件中的屬性,並從這些屬性中分配常量。

class Constants{ 
    public static final String name1; 
    public static final String name2; 
    public static final Integer value3; 

    static{ 
    Properties p = new Properties(); 
    try (FileInputStream stream = new FileInputStream(new File("path/to/file.properties"))) {   
     p.load(stream); 
    }catch(Exception e){ 
     //handle exceptions 
    } 

    name1 = p.getProperty("name1"); 
    name2 = p.getProperty("name2"); 
    value3 = Integer.valueOf(p.getProperty("value3")); 
} 

請注意,這只是一個快速和骯髒的解決方案,並做出了很多假設。如果配置爲空或不是數字,則最好處理個別例外情況,並且還必須處理可能由Integer.valueOf(...)引發的問題。

另一個注意事項:您也可以嘗試使用一些非靜態或至少非最終配置,以便能夠在運行時更改屬性。

編輯:我爲流添加了autoclose,但請注意,在Java 7之前,您必須自己處理它。

+0

@DaveNewton或者我可以告訴他該做什麼,並以這種方式離開。 (代碼示例)旨在爲他提供一個快速答案,並通知他稍後再檢查一次。 – Thomas

+0

'p.load()'處理關閉FileInputStream嗎? – skiwi

+0

@skiwi不,它沒有。我會添加一個提示。 – Thomas

2

作爲一種快速入門,您可以閱讀該類的static初始值設定項中的屬性文件,那麼您不必立即更改類字段的靜態屬性,但我建議您隨着時間的推移無論如何都要這樣做。

創建一個保存所有舊常量值的新類。從現在開始,將這個配置對象注入到你的新代碼中。

class NewConstants { 
    public final String name1; 
    public final String name2; 
    public final Integer value3; 
    ... and so on 

    public NewConstants (Properties props) 
    { 
     name1 = props.getProperty("name1"); 
     ... 
    } 
} 

現在重構你的舊Constants

class Constants (
    public static final String name1; 
    public static final String name2; 
    public static final Integer value3; 

    static { 
     Properties props = new Poperties(); 
     props.load(...); 

     NewConstants newConstants = new NewConstants(props); 

     name1 = newConstants.getName1(); 
     name2 = newConstants.getName2(); 

     ... 
    } 
}