Java 如何从硬编码的静态配置文件切换到.properties文件?

Java 如何从硬编码的静态配置文件切换到.properties文件?,java,refactoring,Java,Refactoring,我有一些代码使用了一个包含大量硬编码常量的类。这就是它看起来的样子: 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 我现在需要

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

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

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


我曾想过使用一个单一的配置类,在实例化时从文件中读取属性,但随后我必须将所有静态值调用替换为对该类实例的调用,并且必须更改现有方法,以将此配置实例传递给它们。有更好的方法吗?

使用
属性从文件中加载属性。加载(…)
并从这些属性中分配常量

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(…)
引发的
NumberFormatException

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


编辑:我为流添加了autoclose,但请注意,在Java 7之前,您必须自己处理该问题。

作为一种快速破解,您可以读取类的
静态
初始值设定项中的属性文件,然后不必立即更改类字段的静态性质,但我建议你以后还是这样做

创建一个包含所有旧常量值的新类。从现在起,将此配置对象注入到新代码中

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" );
       ...
   }
}
现在重构旧的
常量

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

        ...
    }
}

下面是我过去使用过的一段代码,可以根据您的示例进行调整:

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);
    }
}
公共枚举配置{
属性1(“属性1.name”、“默认值1”),
PROPERTY2(“PROPERTY2.name”,“default_value_2”);
私有最终字符串密钥;
私有字符串默认值;
配置(字符串键){
这个(键,NA);
}
配置(字符串键、字符串默认值){
this.key=key;
this.defaultValue=defaultValue;
}
私有最终静态记录器Logger=LoggerFactory.getLogger(Configuration.class);
私有最终静态字符串NA=“不适用”;
私有最终静态字符串CONFIG_FILE=“properties/CONFIG.properties”;
private final static String NOT_A_VALID_KEY=“NOT A VALID property KEY”;
私有最终静态映射配置=新EnumMap(configuration.class);
静止的{
readConfigurationFrom(配置文件);
}
私有静态void readConfigurationFrom(字符串文件名){
info(“读取资源:{}”,文件名);
try(InputStream resource=Configuration.class.getClassLoader().getResourceAsStream(文件名);){
属性=新属性();
load(resource);//如果资源未找到,则抛出NPE
for(字符串键:properties.stringPropertyNames()){
put(getConfigurationKey(key),properties.getProperty(key));
}
}捕获(IllegalArgumentException | IOException | NullPointerException e){
error(“读取属性文件{}时出错”,文件名e);
填充的默认值();
}
}
私有静态配置getConfigurationKey(字符串密钥){
对于(配置c:values()){
如果(c.键等于(键)){
返回c;
}
}
抛出新的IllegalArgumentException(无效的密钥+”:“+密钥);
}
私有静态void填充的默认值(){
对于(配置c:values()){
配置.put(c,c.defaultValue);
}
}
/**
*@返回键对应的属性,如果找不到则返回null
*/
公共字符串get(){
返回配置。获取(此);
}
}

我认为您的概念可以通过使用Eclipse的NLS概念来实现。虽然它是osgi jar,但它可以在非osgi项目中使用;我喜欢在类上使用enum,尽管乍一看它会推迟和/或忽略配置类型。@DaveNewton是的,它只基于字符串。但同样的原则也可以应用于私有静态最终成员,这些成员可以在静态块中初始化。@DaveNewton或我可以告诉他该做什么,然后保持这种状态。(接下来的代码示例)旨在为他提供一个快速答案,并通知他稍后再次检查。是否
p.load()
负责关闭
FileInputStream
?@skiwi否,它不负责。我要补充一点提示。