5

我有一些代码使用一个包含大量硬编码常量的类。这是它的样子:

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文件。

我的问题是:最好的方法是什么,必须尽可能少地重写代码?

我想过使用一个配置类,它在实例化时从文件中读取属性,但是我必须将所有静态值调用替换为对该类实例的调用,并且我必须更改现有方法来传递这个将实例配置到其中。有没有更好的办法?

4

3 回答 3

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);
    }
}
于 2013-07-23T11:45:16.667 回答
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" ) );
} 

请注意,这只是一个快速而肮脏的解决方案,并且做了很多假设。处理个别异常会更好,如果配置为空或不是数字,您还必须处理NumberFormatException可能引发的异常 。Integer.valueOf(...)

另一个注意事项:您也可以尝试使用一些非静态或至少非最终配置,以便能够在运行时更改属性。

编辑:我为流添加了自动关闭,但请注意,在 Java 7 之前,您必须自己处理。

于 2013-07-23T11:41:58.167 回答
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( );

        ...
    }
}
于 2013-07-23T11:43:37.867 回答