2013-07-23 7 views
5

Ho un codice che usa una classe che tiene un sacco di costanti hardcoded. Questo è ciò che sembra:Come passare da un file di configurazione statico con hardcoded a un file .properties?

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

Queste costanti sono utilizzati in tutto il codice come Constants.name1.

Quello che devo fare ora è di rendere possibile specificare valori per queste costanti in un file di configurazione, probabilmente un file *.properties.

La mia domanda è: qual è il modo migliore per farlo, dovendo riscrivere il minor numero possibile di codice?

Ho pensato di utilizzare una singola classe di configurazione che legge le proprietà dal file quando è istanziata, ma poi dovrò sostituire tutte le chiamate statiche di valori alle chiamate a un'istanza di quella classe e dovrò modificare l'esistente metodi per passare questa istanza di configurazione in loro. C'è un modo migliore?

+0

penso il tuo concetto può essere raggiunto utilizzando il concetto NLS di Eclipse. Sebbene sia un vaso osgi, può essere usato in progetti non-osgi. –

risposta

4

Ecco un pezzo di codice che ho usato in passato - che può essere adattato per il tuo esempio:

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; Mi piace l'uso dell'enumerazione sulla classe, anche se a prima vista esso rimanda e/o ignora i tipi di configurazione. –

+0

@DaveNewton Sì, si tratta solo di stringhe. Ma lo stesso principio può essere applicato ai membri finali statici privati, che possono essere inizializzati nel blocco statico. – assylias

+0

Questa è l'unica soluzione non manuale, +1 per quello – skiwi

3

Caricare le proprietà dal file utilizzando Properties.load(...) e assegnare le costanti da tali proprietà.

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")); 
} 

Si noti che questa è solo una soluzione rapida e sporca e fa molte ipotesi. Sarebbe meglio gestire le singole eccezioni e dovresti anche gestire lo NumberFormatException che potrebbe essere lanciato da Integer.valueOf(...) se la configurazione è vuota o non è un numero.

Un'altra nota: si potrebbe anche provare a utilizzare alcune configurazioni non statiche o almeno non finali per poter modificare le proprietà in fase di esecuzione.

Modifica: Ho aggiunto autoclose per lo stream, ma nota che prima di Java 7 dovevi gestirlo tu stesso.

+0

@DaveNewton o potrei semplicemente dirgli cosa fare e lasciarlo così. Il (esempio di codice in arrivo) aveva lo scopo di fornirgli una risposta rapida e di informarlo di ricontrollare tra qualche istante. – Thomas

+0

'p.load()' si occupa di chiudere 'FileInputStream'? – skiwi

+0

@skiwi no, non è così. Aggiungerò un suggerimento – Thomas

2

Come attacco rapido, è possibile leggere il file delle proprietà nell'inizializzatore static della classe, quindi non è necessario modificare la natura statica dei campi classe immediatamente, ma suggerisco di farlo comunque nel tempo.

Creare una nuova classe che contiene tutti i vecchi valori costanti. D'ora in poi, inserisci questo oggetto di configurazione nel tuo nuovo codice.

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"); 
     ... 
    } 
} 

Ora refactoring del vecchio Constants classe

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(); 

     ... 
    } 
}