在哪里初始化 java Properties 对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/376798/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Where to initialize a java Properties object?
提问by Dave Ray
I inherited an application which uses a java properties file to define configuration parameters such as database name.
我继承了一个应用程序,它使用 java 属性文件来定义配置参数,例如数据库名称。
There is a class called MyAppProps that looks like this:
有一个名为 MyAppProps 的类,如下所示:
public class MyAppProps {
protected static final String PROP_FILENAME = "myapp.properties";
protected static Properties myAppProps = null;
public static final String DATABASE_NAME = "database_name";
public static final String DATABASE_USER = "database_user";
// etc...
protected static void init() throws MyAppException {
try {
Classloader loader = MyAppException.class.getClassLoader();
InputStream is = loader.getResourceAsStream(PROP_FILENAME);
myAppProps = new Properties();
myAppProps.load(is);
} catch (Exception e) {
threw new MyAppException(e.getMessage());
}
}
protected static String getProperty(String name) throws MyAppException {
if (props==null) {
throw new MyAppException("Properties was not initialized properly.");
}
return props.getProperty(name);
}
}
Other classes which need to get property values contain code such as:
其他需要获取属性值的类包含如下代码:
String dbname = MyAppProps.getProperty(MyAppProps.DATABASE_NAME);
Of course, before the first call to MyAppProps.getProperty, MyAppProps needs to be initialized like this:
当然,在第一次调用 MyAppProps.getProperty 之前,MyAppProps 需要像这样初始化:
MyAppProps.init();
I don't like the fact that init()needs to be called. Shouldn't the initialization take place in a static initialization block or in a private constructor?
我不喜欢init()需要被调用的事实。初始化不应该在静态初始化块或私有构造函数中进行吗?
Besides for that, something else seems wrong with the code, and I can't quite put my finger on it. Are properties instances typically wrapped in a customized class? Is there anything else here that is wrong?
除此之外,代码似乎还有其他问题,我无法完全理解它。属性实例通常包装在自定义类中吗?这里还有什么不对的吗?
回答by krosenvold
If I make my own wrapper class like this; I always prefer to make strongly typed getters for the values, instead of exposing all the inner workings through the static final variables.
如果我像这样制作自己的包装类;我总是喜欢为值制作强类型的 getter,而不是通过静态最终变量公开所有内部工作。
private static final String DATABASE_NAME = "database_name"
private static final String DATABASE_USER = "database_user"
public String getDatabaseName(){
return getProperty(MyAppProps.DATABASE_NAME);
}
public String getDatabaseUser(){
return getProperty(MyAppProps.DATABASE_USER);
}
A static initializer looks like this;
静态初始化器看起来像这样;
static {
init();
}
This being said, I will readily say that I am no big fan of static initializers.
话虽如此,我很容易说我不是静态初始化程序的忠实粉丝。
You may consider looking into dependency injection (DI) frameworks like spring or guice, these will let you inject the appropriate value directly into the places you need to use them, instead of going through the indirection of the additional class. A lot of people find that using these frameworks reduces focus on this kind of plumbing code - but only after you've finished the learning curve of the framework. (DI frameworks are quick to learn but take quite some time to master, so this may be a bigger hammer than you really want)
您可以考虑研究诸如 spring 或 guice 之类的依赖注入 (DI) 框架,这些将让您将适当的值直接注入需要使用它们的地方,而不是通过附加类的间接调用。很多人发现使用这些框架会减少对这种管道代码的关注——但前提是你已经完成了框架的学习曲线。(DI 框架学起来很快,但需要相当长的时间才能掌握,所以这可能是一个比你真正想要的更大的锤子)
回答by Dave Ray
Reasons to use static initializer:
使用静态初始化器的原因:
- Can't forget to call it
- 不能忘记叫它
Reasons to use an init() function:
使用 init() 函数的原因:
- You can pass parameters to it
- Easier to handle errors
- 你可以给它传递参数
- 更容易处理错误
I've created property wrappers in the past to good effect. For a class like the example, the important thing to ensure is that the properties are truly global, i.e. a singleton really makes sense. With that in mind a custom property class can have type-safe getters. You can also do cool things like variable expansion in your custom getters, e.g.:
我过去创建了属性包装器,效果很好。对于像示例这样的类,要确保的重要一点是属性是真正全局的,即单例确实有意义。考虑到这一点,自定义属性类可以具有类型安全的 getter。你还可以在你的自定义 getter 中做一些很酷的事情,比如变量扩展,例如:
myapp.data.path=${myapp.home}/data
Furthermore, in your initializer, you can take advantage of property file overloading:
此外,在您的初始化程序中,您可以利用属性文件重载:
- Load in "myapp.properties" from the classpath
- Load in "myapp.user.properties" from the current directory using the Properties override constructor
- Finally, load System.getProperties() as a final override
- 从类路径加载“myapp.properties”
- 使用属性覆盖构造函数从当前目录加载“myapp.user.properties”
- 最后,加载 System.getProperties() 作为最终覆盖
The "user" properties file doesn't go in version control, which is nice. It avoids the problem of people customizing the properties file and accidentally checking it in with hard-coded paths, etc.
“用户”属性文件不在版本控制中,这很好。它避免了人们自定义属性文件并使用硬编码路径意外签入等问题。
Good times.
美好时光。
回答by Adeel Ansari
You can use either, a static block or a constructor. The only advice I have is to use ResourceBundle, instead. That might better suit your requirement. For more please follow the link below.
您可以使用静态块或构造函数。我唯一的建议是改用 ResourceBundle。那可能更适合您的要求。更多信息请点击以下链接。
回答by Edward Ames
The problem with static methods and classes is that you can't override them for test doubles. That makes unit testing much harder. I have all variables declared final and initialized in the constructor. Whatever is needed is passed in as parameters to the constructor (dependency injection). That way you can substitute test doubles for some of the parameters during unit tests.
静态方法和类的问题在于您不能为测试替身覆盖它们。这使得单元测试更加困难。我将所有变量声明为 final 并在构造函数中初始化。无论需要什么,都作为参数传递给构造函数(依赖注入)。这样你就可以在单元测试期间用测试替身替换一些参数。
For example:
例如:
public class MyAppProps {
protected static final String PROP_FILENAME = "myapp.properties";
protected Properties props = null;
public String DATABASE_NAME = "database_name";
public String DATABASE_USER = "database_user";
// etc...
public MyAppProps(InputStream is) throws MyAppException {
try {
props = new Properties();
props.load(is);
} catch (Exception e) {
threw new MyAppException(e.getMessage());
}
}
public String getProperty(String name) {
return props.getProperty(name);
}
// Need this function static so
// client objects can load the
// file before an instance of this class is created.
public static String getFileName() {
return PROP_FILENAME;
}
}
Now, call it from production code like this:
现在,从生产代码中调用它,如下所示:
String fileName = MyAppProps.getFileName();
Classloader loader = MyAppException.class.getClassLoader();
InputStream is = loader.getResourceAsStream(fileName);
MyAppProps p = new MyAppProps(is);
The dependency injection is when you include the input stream in the constructor parameters. While this is slightly more of a pain than just using the static class / Singleton, things go from impossible to simple when doing unit tests.
依赖注入是在构造函数参数中包含输入流时。虽然这比仅使用静态类/单例更痛苦,但在进行单元测试时,事情会从不可能变为简单。
For unit testing, it might go something like:
对于单元测试,它可能类似于:
@Test
public void testStuff() {
// Setup
InputStringTestDouble isTD = new InputStreamTestDouble();
MyAppProps instance = new MyAppProps(isTD);
// Exercise
int actualNum = instance.getProperty("foo");
// Verify
int expectedNum = 42;
assertEquals("MyAppProps didn't get the right number!", expectedNum, actualNum);
}
The dependency injection made it really easy to substitute a test double for the input stream. Now, just load whatever stuff you want into the test double before giving it to the MyAppProps constructor. This way you can test how the properties are loaded very easily.
依赖注入使得用测试替身替代输入流变得非常容易。现在,只需将您想要的任何内容加载到测试替身中,然后再将其提供给 MyAppProps 构造函数。通过这种方式,您可以非常轻松地测试属性的加载方式。

