注入私人,包装或公共领域或提供制定者?

我看到许多Java示例使用dependency injection私有字段而没有像这样的公共setter:

public SomeClass { @Inject private SomeResource resource; } 

但是,当应该手动执行注射时,例如在unit testing中,这是一个坏主意。

有几种可能性来解决这个问题:

  • 添加一个公共setter: setSomeResource(SomeResource r)
  • 让这个领域公开
  • 使字段包受到保护

我想避开二传手,因为它没有真正发生。 所以我更喜欢公共或包裹保护。 您有什么推荐的吗?

我更喜欢二传手

  • 它更容易调试(在setter中放置断点而不是字段访问/修改)
  • 更容易记录
  • 更容易添加一些validation(虽然这并不总是最好的地方)
  • 更容易支持双向维护(尽管IOC容器可以处理)
  • 任何其他“手动AOP”的目的

但那只是我的个人意见

避免为字段创建setter的一种方法是使用构造函数注入。 这甚至允许您将该字段声明为final。

它是这样的:

 public class SomeClass { private final SomeResource resource; @Inject public SomeClass(SomeResource resource) { this.resource = resource; } } 

添加setter不是最佳解决方案,因为您要添加不需要的生产代码。

另一种方法是使用Spring的ReflectionTestUtils类使用reflection注入测试依赖项,请参阅http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/util/ReflectionTestUtils.html

EDIT(2017):然而,反思是比添加setter更糟糕的解决方案。 造成这种混乱的原因是Spring可以在没有setter或构造函数的情况下注入值。 我目前的立场是坚持使用其中任何一种,并避免使用黑魔法注射做法。

我建议使用setter。 在这个问题中使用getter和setter的好处。

借助我(与此相关)问题的答案:

应用服务器如何注入私有字段?

我编写了这个关于如何在没有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 

使用基于字段的注入,您会遇到通过测试描述的问题。 此外,对于基于setter的注入,如果您忘记设置某些依赖项,则在运行测试时,可以在不完整的状态下创建类的实例。 我最近一直在练习构造函数注入,因为它会强制您在测试期间创建类的实例时设置所有依赖项。 Andre Rodrigues的上述答案解释了如何实现这一目标。

可能的解决方案:

  • 使用像JGlue CDI-Unit这样的CDI感知测试框架。 这样你就不需要setter了。 您只在测试中定义依赖项 – 通常使用Mockito模拟对象。 恕我直言这是最好的解决方案,因为它不需要你做任何额外的测试。

  • 注入构造函数或setter。 没错,你可以注入安装者! 更多细节在这里。

  • 使用受保护的setter。 简单,适用于所有情况。 由于它受到保护,您可以从测试类(它应该与测试类具有相同的包定义)访问它,并且没有其他包可以访问它。

  • 使用getter并在测试时覆盖它。 在您的测试类中,创建一个新的内部类,扩展测试的类并覆盖getter。 但是,这有一个很大的缺点:您的测试类必须在内部使用getter而不是字段。 很多可能漏洞的样板……