什么是字段注入,如何避免它?

我在一些关于Spring MVC和portlet的文章中读到不推荐字段注入。根据我的理解,字段注入是当你像这样用@Autowired注入Bean时:

@Component
public class MyComponent {
@Autowired
private Cart cart;
}

在我的研究中,我还阅读了构造函数注入:

@Component
public class MyComponent {
private final Cart cart;


@Autowired
public MyComponent(Cart cart){
this.cart = cart;
}
}

这两种注射方式的优缺点是什么?


编辑1:因为这个问题被标记为这个问题的副本,我检查了它。因为没有任何代码示例,无论是在问题中还是在答案中,我不清楚如果我的猜测是正确的,我正在使用哪种注入类型。

155866 次浏览

品味问题。这是你的决定。

但我可以解释,为什么我从不使用构造函数注入

  1. 我不想为所有的@Service@Repository@Controller bean实现一个构造函数。我的意思是,大约有40-50颗豆子,甚至更多。每次如果我添加一个新字段,我就必须扩展构造函数。不。我不想要,也没必要。

  2. 如果您的Bean(服务或控制器)需要注入许多其他Bean,该怎么办?带有4个以上参数的构造函数非常丑陋。

  3. 如果我使用CDI,构造函数与我无关。


< p > 编辑# 1: Vojtech Ruzicka说:

类有太多依赖项,可能违反了single 责任原则,应该重构

< p >是的。理论和现实。 下面是一个例子:DashboardController映射到单个路径*:8080/dashboard.

我的DashboardController从其他服务收集了大量信息,并将它们显示在仪表板/系统概述页面中。我需要这个单独的控制器。所以我必须只保护这一条路径(基本认证或用户角色过滤器)。

< p > 编辑# 2: 由于每个人都关注构造函数中的8个参数……这是一个真实的例子——客户遗留代码。我已经改变了。同样的论证也适用于4+参数。< / p >

这完全是关于代码注入,而不是实例构造。

注入类型

如何将依赖项注入到bean中有三个选项:

  1. 通过构造函数
  2. 通过设置或其他方法
  3. 通过反射,直接射入田野

您正在使用选项3。这就是当你直接在你的字段上使用@Autowired时发生的事情。


注射指南

一般的指导原则,Spring推荐哪种(参见Constructor-based迪Setter-based迪部分)如下所示:

  • 对于强制依赖项或当目标是不可变性时,使用构造函数注入
  • 对于可选或可更改的依赖项,使用setter注入
  • 大多数情况下避免现场注入

现场注入缺陷

字段注入不受欢迎的原因如下:

  • 不能像构造函数注入那样创建不可变对象
  • 您的类与DI容器紧密耦合,不能在容器之外使用
  • 如果没有反射,就不能实例化类(例如在单元测试中)。您需要DI容器来实例化它们,这使您的测试更像集成测试
  • 你真正的依赖是隐藏在外部的,并且没有反映在你的接口中(无论是构造函数还是方法)。
  • 有10个依赖关系是很容易的。如果你在使用构造函数注入,你将有一个带有十个参数的构造函数,这将表明有些事情是可疑的。但是您可以使用字段注入无限期地添加已注入的字段。有太多的依赖项是一个危险信号,说明类通常做不止一件事,并且可能违反单一职责原则。

结论

根据需要,您应该主要使用构造函数注入或构造函数和setter注入的混合使用。现场注入有很多缺点,应该避免。字段注入的唯一优点是更方便编写,这并没有超过所有的缺点。


进一步的阅读

我写了一篇关于为什么字段注入通常不推荐的博客文章:

这是软件开发中永无止境的讨论之一,但业界的主要影响者对这个话题越来越有见解,并开始建议构造函数注入是更好的选择。

构造函数注入

优点:

  • 更好的可测试性。单元测试中不需要任何模拟库或Spring上下文。你可以用关键字创建一个你想测试的对象。这样的测试总是更快,因为它们不依赖于反射机制。(这个问题在30分钟后被问到。如果作者使用了构造函数注入,它就不会出现)。
  • 不变性。一旦设置了依赖关系,就不能更改它们。
  • 更安全的代码。执行构造函数后,你的对象就可以使用了,因为你可以验证任何作为参数传递的东西。对象可以是就绪的,也可以不是,中间没有状态。使用字段注入,当对象是脆弱的时,引入一个中间步骤。
  • 更清晰地表达强制依赖项。在这个问题上,字段注入是模棱两可的。
  • 让开发人员思考设计。dit写了一个有8个参数的构造函数,这实际上是一个糟糕的设计和God对象反模式. dit的标志。一个类是否在构造函数中或字段中有8个依赖关系并不重要,它总是错误的。比起通过字段,人们更不愿意向构造函数添加更多依赖项。它向你的大脑发出信号,告诉你应该停下来思考一下你的代码结构。

缺点:

  • 更多的代码(但是现代ide减轻了痛苦)。

基本上,现场注入正好相反。

还有一条评论——Vojtech Ruzicka说Spring以三种方式注入bean(得分最多的答案):

  1. 通过构造函数
  2. 通过设置或其他方法
  3. 通过反射,直接射入田野
这个答案是错误的——因为每一种注入弹簧都使用反射! 使用IDE,设置setter /构造函数上的断点,并检查。< / p > 这可能是一个品味问题,但也可能是一个CASE问题。 @dieter提供了一个现场注射效果更好的绝佳案例。如果您在设置Spring上下文的集成测试中使用字段注入-类的可测试性参数也是无效的-除非您想稍后在测试中编写集成测试;)