从Java中的Properties文件动态填充String

时间:2011-09-01 08:21:47

标签: java properties

有没有人知道加载属性文件的方法,并动态创建与键值具有相同名称的字符串?

我正在尝试通过将所有系统消息等移出逻辑并进入属性文件来清理我的代码,但是要避免必须包含由以下几十行组成的类:

final String COMMS_ERROR = properties.getProperty(COMMS_ERROR);

我正在努力实现的一个例子:

for (String key : properties.getPropertyValues()) {
    final String <key> = properties.getProperty(key)
}

显然这不起作用,编译器会抛出一个合适的。但我想知道是否有一个优雅的解决方案来做同样的事情 - 使用属性文件中的键名创建新的字符串 - 无论是通过单独的库还是我自己的代码。

我想到的一个解决方案是使用属性文件中的键/值填充HashMap,但这意味着不太优雅的代码形式为:

import com.x.y.messages;
...
    throw new Exception(HM.get("COMMS_ERROR"));

其中HM是位于com.x.y.messages ...

中的HashMap

理想情况下,我希望能够做到:

import com.x.y.messages;
....
    throw new Exception(COMMS_ERROR);

任何想法/建议都表示赞赏。

6 个答案:

答案 0 :(得分:3)

如果编译后这些属性可以更改(如果没有,那么为什么会使用它们)你没有任何机会动态创建和使用这些字符串。当然,有一些方法可以动态创建代码(如AOP运行时编织),但该代码在正常编译过程中不可用。

那么编译器如何知道COMMS_ERROR实际上存在于此行throw new Exception(COMMS_ERROR);中?它不能,因此你需要采用HashMap方法。请注意,Properties实际上是Map<String, String>(好吧,从Java 6开始是Hashtable<Object, Object>,但它的作用类似于Map<String, String>),因此无需创建新的package yourpackage; public class Props { private static Properties props; public static String prop(String prop) { return props.getProperty( prop ); } } 之一。

编辑:你可以做的是使用这样的静态导入:

import static yourpackage.Props.prop;

....

prop("someKey");

像这样使用:

{{1}}

请注意,静态导入有一些缺点,就好像方法是它所使用的类的一部分一样,所以我只想提供一个替代方案,让你决定是否使用它。

答案 1 :(得分:1)

有什么问题
        Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources( "com/x/y/message.properties" );

        while( resources.hasMoreElements() ) {
            URL url = resources.nextElement();
            Properties p = new Properties();
            p.load( url.openStream() );
            ...
        }

我不明白为什么要将Properties的数据存储到HashMap

import com.x.y.messages;
    ....
throw new Exception(p.getProperty("COMMS_ERROR"));

答案 2 :(得分:0)

您无法动态声明局部变量,但可以使用地图:

Map<String, String> messages = new HashMap<String, String>();

for (String key : properties.getPropertyValues()) {
    messages.put(key, properties.getProperty(key));
}

使用它们:

throw new Exception( messages.get( "KEY" ) )

请参阅http://download.oracle.com/javase/6/docs/api/java/util/Map.html

但实际上正如托马斯上面指出的那样,你不需要新的HashMap

throw new Exception( properties.getProperties(key) );

答案 3 :(得分:0)

我不知道有一个工具可以做到这一点,它不符合正常的Java做事方式。 (在Java中,您无法动态添加新变量...例如,与Javascript不同。)

理论上可以在这些方面实现某些东西,但它可能需要为每种属性文件生成和编译一个类,并针对这些类API重新编译其余的代码。除非你有大量的这些属性文件,否则手动编写类更容易。 (如果你确实拥有大量的这些属性文件,我会倾向于看看是否有更好的方法来处理这些文件中的信息。)


  

是的,这就是我所希望的 - 一个包含必要魔法的图书馆

不幸的是,没有普通的库可以做到这一点。生成/重新编译必须在构建时进行。 (库可以在运行时生成类,甚至可以编译和加载它们。但是让它在运行时重新编译应用程序的其余部分是最困难的,通常是不可能的......因为源代码不可用。)

答案 4 :(得分:0)

我之前编写了一个帮助程序类,它使一个Properties文件与一个Constants类保持同步。但这只有在你坚持惯例的情况下才有效。

假设你有一个这样的课程:

public final class Constants{

    private Constants(){}
    public static final String SOME_PROPERTY_NAME = "some.property.name";
    public static final String THIS_ONE_NOT_SET_YET = null;
    public static final String PROPERTY_NOT_DEFINED = "property.not.defined";

}

和这样的属性文件:

some.property.name=Hello World
no.constant.for.this.yet=Hello again

我的助手类会做的是遍历所有属性和所有常量,进行匹配并识别那些与任何东西不对应的东西。

所以在这种情况下:

A)

在Constants.java中,

public static final String THIS_ONE_NOT_SET_YET = null;

将更改为

public static final String THIS_ONE_NOT_SET_YET = "this.one.not.set.yet";

并在属性文件中引入此行:

this.one.not.set.yet=

b)中

在属性文件中,将添加此行

property.not.defined=

c)中

在Constants.java中,将添加以下行:

public static final String NO_CONSTANT_FOR_THIS_YET = "no.constant.for.this.yet";

这并不完美,但这样你就可以获得伪编译时的安全性。您可以针对常量进行编译,并且您的帮助程序会使这些常量与其属性保持同步。

显然,如果您有更多高级方案,这种方法会变得更加复杂。 E.g。

  • 以“foo”开头的属性。存储在“foo.properties”中,而属性名为“bar”。存储在“bar.properties”
  • 国际化:现在你有foo.properties,foo.properties.es,foo.properties.de等。保持同步是一个重要的新生事物。

或许要考虑的一件事是在构建过程中从一个或多个属性文件动态创建常量类。您的代码生成器(Main类,Groovy脚本甚至shell脚本)基本上只需执行此操作(伪代码):

properties = readProperties()
writeClassHeader()
for prop : properties
    writeln "public static final String " 
            + prop.name.upperCase().replace(".","_") + "= \"" + prop.name + "\";"
writeClassFooter()

答案 5 :(得分:0)

它看起来几乎就是我的图书馆所做的!看看:http://owner.aeonbits.org

示例:

# Properties file (call it the same name as the Java class and put 
# it in the same package
port=80
hostname=foobar.com
maxThreads=100

//properties mapper interface
public interface ServerConfig extends Config {
    int port();
    String hostname();
    int maxThreads();
}

// how you use it:

ServerConfig cfg = ConfigFactory.create(ServerConfig.class);
System.out.println("Server " + cfg.hostname() + ":" + cfg.port() + " will run " + cfg.maxThreads());

但是,你可以用OWNER库做更多的事情。