我看到许多Java示例使用依赖注入私有字段而没有像这样的公共setter:
public SomeClass {
@Inject
private SomeResource resource;
}
但是,当应该手动执行注射时,例如在单元测试中,这是一个坏主意。
有几种可能性来解决这个问题:
setSomeResource(SomeResource r)
我想避免使用setter,因为它没有真正发生。所以我更喜欢公共或包裹保护。您有什么推荐的吗?
答案 0 :(得分:14)
避免为字段创建setter的一种方法是使用构造函数注入。这甚至允许您将该字段声明为最终字段。
它是这样的:
public class SomeClass {
private final SomeResource resource;
@Inject
public SomeClass(SomeResource resource) {
this.resource = resource;
}
}
答案 1 :(得分:8)
添加setter不是最佳解决方案,因为您要添加不需要的生产代码。
另一种方法是使用Spring的ReflectionTestUtils类使用反射注入测试依赖项,请参阅http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/util/ReflectionTestUtils.html
EDIT(2017):然而,反思是比添加setter更糟糕的解决方案。造成这种混乱的原因是Spring可以在没有setter或构造函数的情况下注入值。我目前的立场是坚持使用其中任何一种并避免使用黑魔注射法。
答案 2 :(得分:7)
我更喜欢setter
但这只是我的意见
答案 3 :(得分:4)
我建议使用setter。在this question中使用getter和setter是有好处的。
答案 4 :(得分:3)
借助我(与此相关)问题的答案:
How do app servers inject into private fields?
我编写了这个关于如何在没有setter的情况下进行注入的简单示例。 也许有帮助
//......................................................
import java.lang.annotation.*;
import java.lang.reflect.*;
//......................................................
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface Inject {
}
//......................................................
class MyClass {
@Inject
private int theValue = 0;
public int getTheValue() {
return theValue;
}
} // class
//......................................................
public class Example {
//......................................................
private static void doTheInjection(MyClass u, int value) throws IllegalAccessException {
Field[] camps = u.getClass().getDeclaredFields();
System.out.println("------- fields : --------");
for (Field f : camps) {
System.out.println(" -> " + f.toString());
Annotation an = f.getAnnotation(Inject.class);
if (an != null) {
System.out.println(" found annotation: " + an.toString());
System.out.println(" injecting !");
f.setAccessible(true);
f.set(u, value);
f.setAccessible(false);
}
}
} // ()
//......................................................
public static void main(String[] args) throws Exception {
MyClass u = new MyClass();
doTheInjection(u, 23);
System.out.println(u.getTheValue());
} // main ()
} // class
运行输出:
------- fields : --------
-> private int MyClass.theValue
found annotation: @Inject()
injecting !
23
答案 5 :(得分:0)
使用基于字段的注入,您会遇到通过测试描述的问题。此外,对于基于setter的注入,如果您忘记设置某些依赖项,则在运行测试时可以在不完整的状态下创建类的实例。我最近一直在练习构造函数注入,因为它会强制您在测试期间创建类的实例时设置所有依赖项。 Andre Rodrigues 上面的答案解释了如何实现这一目标。
答案 6 :(得分:0)
可能的解决方案:
使用像JGlue CDI-Unit这样的CDI感知测试框架。这样你就不需要setter了。您只需在测试中定义依赖项 - 通常使用Mockito模拟对象。恕我直言,这是最好的解决方案,因为它不需要你做任何额外的测试。
注入构造函数或setter。没错,你可以注入二传手! More details here.
使用受保护的setter。简单,适用于所有情况。由于它受到保护,您可以从测试类(它应该与测试类具有相同的包定义)访问它,并且没有其他包可以访问它。
使用getter并在测试时覆盖它。在您的测试类中,创建一个新的内部类,扩展测试的类并覆盖getter。但是,这有一个很大的缺点:您的测试类必须在内部使用getter而不是字段。很多可能漏洞的样板...