我有一些类需要一些特定于环境的配置。我转向使用属性文件,这些文件在类的构造函数中加载。
public class MyClass {
public MyClass() {
try {
ValidatedEnvironmentProperties props = new ValidatedEnvironmentProperties();
props.load(MyClass.class.getResourceAsStream("/myclass.properties"));
ValidatedEnvironmentProperties
延伸Properties
。基本上,它使用Java System Property来设置密钥前缀。我将系统属性设置为production
,在属性文件中,我有staging.url=...
和production.url=...
。这允许我构建/运行时选择使用哪个配置而不需要更改属性文件名。
myclass.properties
存储在src/main/resources
。
这很好用,我更喜欢它的工作原理。我的问题是我有点被TestNG困住了。我想在TestNG单元测试中测试一堆其他属性。这导致我创建了src/test/resources/myclass.properties
。而不是"环境"键,我使用像bad_url_test.url=this_ain't_a_url
这样的测试名称。
我认为src/test/resources
会在classpath / classloader(术语?)中获得更高的优先级,从而导致加载特定于测试的属性。然后,对于我的各种测试,我只是将Java System属性设置为bad_url_test
,实例化MyClass
,测试我的断言,然后将System属性设置为新测试,实例化一个新对象,然后重复。
我认为问题的根源是MyClass
的构造函数中的这一行:
props.load(MyClass.class.getResourceAsStream("/myclass.properties"));
在MyClassTest
中,我将这些内容用于尝试了解发生了什么:
File f = new File(MyClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
System.out.println("MyClass classpath: " + f.toString());
f = new File(MyClassTest.class.getProtectionDomain().getCodeSource().getLocation().getPath());
System.out.println("MyClassTest classpath: " + f.toString());
输出是:
MyClass classpath: /home/fandingo/code/project/build/classes/main
MyClassTest classpath: /home/fandingo/code/project/build/classes/test
MyClassTest是正确的,但我需要在MyClass
的构造函数中可访问的内容,在运行测试时会自动更喜欢/src/test/resources/
,而在正常运行时会/src/main/resources/
。
答案 0 :(得分:1)
您需要inversion of control。即MyClass
需要被告知从哪里获取其财产而不是自己决定。
有很多方法可以做到这一点,但它们都归结为相同的想法:MyClass
在编译时不应该知道其属性来自何处。
e.g。
在MyClass.java中
ValidatedEnvironmentProperties props = new ValidatedEnvironmentProperties();
String resourceSupplierClassName = System.getProperty("resource-supplier-class-name",
MyClass.class.getName());
Class<?> resourceSupplierClass = Class.forName(resourceSupplierClassName);
props.load(resourceSupplierClass.getResourceAsStream("/myclass.properties"));
在MyClassTest.java中
System.setProperty("resource-supplier-class-name", MyClassTest.class.getName());
同样,有很多方法可以做到这一点。您也可以使用dependency injection(一种控制反转形式)来代替传递系统属性等。您可以更改MyClass
构造函数以将props
作为参数,然后将责任放在MyClass
的实例化器上以提供props
,或者您可以使用依赖注入Spring或Guice等框架,用于管理为main / test执行创建适当的props
实例,并根据需要将其提供给需要它的对象。