使静态变量可注射

时间:2015-01-15 21:31:37

标签: java dependency-injection

我正在构建一个需要调用特定端点的http API客户端,如下所示:

public class MyApiClient {
    private static final String ENDPOINT ="http://myapi....";

}

此处端点不会改变,因此它的常量。但是,我希望能够覆盖它进行测试,以便我可以测试模拟http服务器。

这是最好的方法吗?它只是使它成为一个实例变量并为它提供一个起始值:

private String endpoint = ="http://myapi....";

public void setEndpoint(String endpoint){
   ...
}

1 个答案:

答案 0 :(得分:3)

嗯,当然有很多解决方案,其中一种方法是使用具有默认值的系统属性:

private static final String DEFAULT_ENDPOINT = "http://myapi....";
private static final String ENDPOINT = 
        System.getProperty("my.endpoint", DEFAULT_ENDPOINT);

通过这种方式,您可以获得解决问题的可配置方式。如果在初始化静态常量时需要更大的灵活性,还可以使用静态初始化器

private static final String ENDPOINT;
static {
    // do initialization here but do not throw any exceptions (bad practice)
    // you can e.g. read from files etc...

    // Then assign your constant...
    ENDPOINT = 
}

系统属性在命令行上作为-D参数传递,例如:

java -Dmy.endpoint=http://...

但在我看来,一个更好的方法实际注入该值正在使用它的类:

public class ClassThatIsUsingTheConfig {
    private final String endpoint;
    public ClassThatIsUsingTheConfig(final String endpoint) {
        this.endpoint = endpoint;
    }

    public void someMethod() {
        // use endpoint
    }
}

然后,选择要在调用者类中使用的端点。从测试用例来看,这很容易被模拟。

public class MyTest {
    @Test
    public void testMethod() {
        ClassThatIsUsingTheConfig var = new ClassThatIsUsingTheConfig(TEST_ENDPOINT);
        var.someMethod();
    }
}

public class MyProdClass {
    public void prodMethod() {
        ClassThatIsUsingTheConfig var = new ClassThatIsUsingTheConfig(PROD_ENDPOINT);
        var.someMethod();
    }
}

您可以阅读有关dependency injection here的更多信息。


另外,如果您使用某种框架来管理依赖关系,例如Spring FrameworkCDI,则通常能够以各种方式注入属性和常量(例如,基于当前正在运行的环境)。例如,使用Spring Framework时,您可以在属性文件中声明所有常量,并使用注释注入属性:

@Autowired
public ClassWhoIsUsingTheConfig(@Value("my.endoint") final String endpoint) {
    this.endpoint = endpoint;
}

prod的属性文件可以是:

my.endpoint=http://prodserver...


用于测试的属性文件如下所示:

my.endpoint=http://testserver...

使用依赖注入引擎的方法允许以非常灵活的方式处理外部常量,路径,资源等,并简化您在测试代码时的生活。