java 注入私有,包或公共字段或提供一个setter?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2021716/
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
Inject into private, package or public field or provide a setter?
提问by deamon
I see many Java examples using dependency injection with private fields without a public setter like this:
我看到许多 Java 示例使用带有私有字段的依赖注入,而没有像这样的公共 setter:
public SomeClass {
@Inject
private SomeResource resource;
}
But that is a bad idea when the injection should be performed manually for example in unit tests.
但是,当应该手动执行注入(例如在单元测试中)时,这是一个坏主意。
There are several possibilities to solve this:
有几种可能来解决这个问题:
- add a public setter:
setSomeResource(SomeResource r) - make the field public
- make the field package protected
- 添加一个公共设置器:
setSomeResource(SomeResource r) - 公开该领域
- 使字段包受保护
I'd like to avoid the setter, since nothing really happens in it. So I'd prefer public or package protected. What do you recommend?
我想避免使用二传手,因为它没有真正发生任何事情。所以我更喜欢公共或包保护。你有什么建议吗?
回答by Andre Rodrigues
One way to avoid creating a setter for the field is using constructor injection. This even allows you to declare the field as final.
避免为字段创建 setter 的一种方法是使用构造函数注入。这甚至允许您将该字段声明为 final。
It goes like this:
它是这样的:
public class SomeClass {
private final SomeResource resource;
@Inject
public SomeClass(SomeResource resource) {
this.resource = resource;
}
}
回答by Adriaan Koster
Adding setters is not an optimal solution, since you are adding production code which is not needed.
添加 setter 不是最佳解决方案,因为您正在添加不需要的生产代码。
An alternative is to use Spring's ReflectionTestUtils class to inject your test dependencies using reflection, see http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/util/ReflectionTestUtils.html
另一种方法是使用 Spring 的 ReflectionTestUtils 类使用反射注入测试依赖项,请参阅http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/util/ReflectionTestUtils.html
EDIT (2017): However, reflection is an even worse solution than adding setters. The cause of this mess is the fact that Spring makes it possible to inject values without setters or constructors. My current stance is to stick to using either of those and avoid using black magic injection practices.
编辑(2017 年):但是,反射是比添加 setter 更糟糕的解决方案。造成这种混乱的原因是 Spring 可以在没有 setter 或构造函数的情况下注入值。我目前的立场是坚持使用其中任何一种,避免使用黑魔法注入实践。
回答by Eran Medan
I prefer the setter
我更喜欢二传手
- it is easier to debug (put a breakpoint in a setter rather than on field access / modification)
- easier to log
- easier to add some validation (although this is not always the best place)
- easier to support bidirectional maintainance (though IOC container can take care of that)
- any other "manual AOP" purpose
- 更容易调试(在设置器中而不是在字段访问/修改中放置断点)
- 更容易登录
- 更容易添加一些验证(虽然这并不总是最好的地方)
- 更容易支持双向维护(尽管 IOC 容器可以解决这个问题)
- 任何其他“手动 AOP”目的
But that's just my opinion
但那只是我的个人意见
回答by Daniel Moura
I recommend using setter. In this questionare the benefits of using getters and setters.
我推荐使用setter。在这个问题中是使用 getter 和 setter 的好处。
回答by cibercitizen1
With the help of the answer to my (related to this one) question:
借助我的(与此相关的)问题的答案:
How do app servers inject into private fields?
I coded this simple example on how to inject without setters. Perhaps it helps
我编写了这个关于如何在没有 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
Run output:
运行输出:
------- fields : --------
-> private int MyClass.theValue
found annotation: @Inject()
injecting !
23
回答by gogogadgetgeek
With field based injection, you run into the issue you describe with testing. Also with setter based injection, an instance of a class can be created in an incomplete state when running tests if you forget to set some of the dependencies. I have been practicing constructor injection most recently due to the fact that it forces you to set all dependencies whenever you create an instance of a class during testing. The answer above by Andre Rodriguesexplains how this would be accomplished.
使用基于字段的注入,您会遇到您在测试中描述的问题。同样使用基于 setter 的注入,如果您忘记设置某些依赖项,则在运行测试时可以在不完整状态下创建类的实例。我最近一直在练习构造函数注入,因为它强制您在测试期间创建类的实例时设置所有依赖项。安德烈·罗德里格斯( Andre Rodrigues) 的上述回答解释了如何实现。
回答by Hubert Grzeskowiak
Possible solutions to this:
可能的解决方案:
Use a CDI-aware testing framework like JGlue CDI-Unit. This way you need no setter. You only define the dependency inside your tests - usually using a Mockito mock object. IMHO this is the best solution, since it doesn't require you to do anything extra for testing.
Inject into Constructor or setter. That's right, you can inject into setters! More details here.
Use a protected setter. Simple and works in every case. Since it's protected, you can access it from your test class (which should have the same package definition as your tested class), and no other packages can access it.
Use a getter and override it when testing. In your test class, create a new inner class that extends the tested class and override the getter. This, however, has a big disadvantage: your test class has to use the getter internally instead of the field. Lots of potentially bugged boilerplate...
使用 CDI 感知测试框架,如JGlue CDI-Unit。这样你就不需要二传手了。您只需在测试中定义依赖项 - 通常使用 Mockito 模拟对象。恕我直言,这是最好的解决方案,因为它不需要您为测试做任何额外的事情。
注入构造函数或setter。没错,你可以注入到setter中!更多细节在这里。
使用受保护的 setter。简单且适用于所有情况。由于它是受保护的,您可以从您的测试类(它应该与您的测试类具有相同的包定义)访问它,并且没有其他包可以访问它。
使用 getter 并在测试时覆盖它。在您的测试类中,创建一个新的内部类来扩展测试类并覆盖 getter。但是,这有一个很大的缺点:您的测试类必须在内部使用 getter 而不是字段。许多潜在的错误样板...

