如何正确地进行依赖注入(在Spring中)?

时间:2014-06-21 01:34:27

标签: java spring dependency-injection

我怀疑使用Spring将对象注入到类中。我在我的项目中使用了这种代码:

@Resource // or @Autowired even @Inject
private PersonRepository personRepository;

然后在方法上正常使用它:

personRepository.save(p);

否则我在Spring示例中找到了注入构造函数:

private final PersonRepository personRepository;

@Autowired
public PersonController(PersonRepository personRepository) {
  this.personRepository = personRepository;
}

所以两者都是正确的?或者每个都有它的属性和用法?

2 个答案:

答案 0 :(得分:71)

tl; dr - 构造函数注入是进行DI的最佳方式

后者是正确的,这不是因为Spring或任何依赖注入容器,而是面向对象的类设计原则。

详细

应设计一种类型,以便您只能从中创建处于有效状态的实例。为实现此目的,该类型所需的所有必需依赖项都是构造函数参数。这意味着,这些依赖关系可以null - 检查,分配给最终字段以提升不变性。除此之外,在使用代码时,对于该实例的调用者(或创建者),它必须立即显示它必须提供哪些依赖关系(通过浏览API文档或在IDE中使用代码完成)。 / p>

通过现场注射,所有这一切都是不可能的。你没有从外面看到依赖关系,你需要一些黑魔法来注入依赖关系,除非你盲目信任,否则你永远不能确定它们不是null容器。

最后但并非最不重要的方面实际上是,通过字段注入,为您的类添加大量依赖项不那么痛苦,这本身就是一个设计问题。随着构造函数变得更加痛苦,这是一个好东西因为它告诉你关于你的类设计的东西:这个类有太多的责任。首先不需要计算它的指标,当你尝试扩展它时就会感觉到它。

容器

人们经常争辩说,这只是学术废话,因为你无论如何都可以依赖于容器。以下是我对此的看法:

  • 仅仅因为容器存在,它并不意味着你必须抛弃所有基本的面向对象设计原则,是吗?即使存在止汗剂,你仍然会洗澡,对吗?

  • 即使是设计用于容器的类型,也将手动使用:在单元测试中。如果你不编写单元测试......那么那就是另一个话题。

  • 一个额外的构造函数所谓的冗长("我可以通过单行注入来实现同样的目标!!" - "不,你不能。实际上,你可以通过编写一行代码来获得的东西。")可以通过像Lombok这样的东西来缓解。使用Spring和Lombok的构造函数注入组件如下所示:

    @Component
    @RequiredArgsConstructor
    class MyComponent implements MyComponentInterface {
    
      private final @NonNull MyDependency dependency;
    
      …
    }
    

Lombok将负责生成一个构造函数,为每个final字段获取一个参数,并在分配之前检查null的给定参数。因此,您可以有效地获得现场注入的简洁性和构造注入的设计优势。

后记

我最近与一些非Java人员进行了讨论,我对使用术语"注入"非常感到困惑。对于构造函数DI。实际上,他们认为 - 并且有很多事实 - 通过构造函数传递依赖关系根本不是注入,因为它是将对象交给其他人的最自然的方式(与注入形成鲜明对比)任何种类。)

也许我们应该为这种风格注入不同的术语?依赖性喂养,也许?

资源

答案 1 :(得分:2)

从学术上讲,我同意构造函数只是创建对象的更好方法。然而,java bean规范建立在mutators的前提下,以便于反思。围绕这种易于访问的范例构建了太多的工具和框架。对于服务,DAO和其他单例场景,我认为只应使用构造函数注入,因为mutators打破了古老的规则“只有朋友可以看到你的私有部分”。