究竟什么是Field Injection以及如何避免它?

T. *_*ung 90 java portlet dependency-injection spring-mvc autowired

我在一些帖子中读过,Spring MVCPortlets建议不要进行现场注射.因为我试图得到一个所以我问自己我是否使用现场注射,我无法回答它.据我所知场注入是,如果你注入一个Bean与一个属性@Autowired是这样的:

CartController.java:

...
@Autowired
private Cart cart;
...
Run Code Online (Sandbox Code Playgroud)

BookshopConfiguartion.java:

@Configuration
public class BookShopConfiguration {

@Bean
public Cart cart(){
    return new Cart();
}
//more configuration
Run Code Online (Sandbox Code Playgroud)

Cart.java习惯于在购物车中存储和提供有关书籍的信息.

在我的研究期间,我读到了构造函数注入:

MyComponent.java:

...
public class MyComponent{
private Cart cart;

@Autowired
public MyComponent(Cart cart){
   this.cart = cart;
}
...
Run Code Online (Sandbox Code Playgroud)

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


编辑1:由于此问题被标记为此问题的重复,我检查了它.因为在问题和答案中都没有任何代码示例,我不清楚我是否正确猜测我正在使用哪种注射类型.

Voj*_*cka 158

注射类型

如何将依赖项注入bean中有三种选择:

  1. 通过构造函数
  2. 通过二传手或其他方法
  3. 通过反思,直接进入田野

您正在使用选项3.这就是您@Autowired直接在场上使用时发生的情况.


注射指南

Spring推荐的一般准则(参见基于构造函数的DI基于Setter的DI部分)如下:

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

现场注射缺点

现场注射不赞成的原因如下:

  • 您不能像构造函数注入那样创建不可变对象
  • 您的类与DI容器紧密耦合,不能在其外部使用
  • 没有反射,您的类无法实例化(例如在单元测试中).您需要DI容器来实例化它们,这使您的测试更像集成测试
  • 您的真实依赖项是从外部隐藏的,不会反映在您的界面中(构造函数或方法)
  • 拥有十个依赖关系真的很容易.如果你正在使用构造函数注入,你将拥有一个带有十个参数的构造函数,这将表明某些东西是可疑的.但是您可以无限期地使用场注入添加注入的字段.拥有太多的依赖关系是一个红旗,该类通常不止一件事,并且它可能违反单一责任原则.

结论

根据您的需要,您应该主要使用构造函数注入或构造函数和setter注入的混合.现场注入有许多缺点,应该避免.现场注入的唯一优点是写入更方便,这不会超过所有缺点.


进一步阅读

我写了一篇博客文章,说明为什么通常不推荐现场注入:字段依赖注入被认为是有害的.

  • 一般不是好主意,并且告诉世界"应该避免现场注入"并不好.展示利弊,让其他人来决定自己;)许多人有其他经验和自己的看待事物的方式. (8认同)
  • 这可能是这种情况,但在其他情况下,社群已达成普遍共识以阻止某些事情.以匈牙利表示法为例. (5认同)
  • 您在可测试性和依赖项可见性方面给出了一些优点,但我并不完全同意。构造函数注入有没有缺点?在类中注入 5 或 6 个字段来执行真正的调用组合可能是可取的。我也不同意你的不变性。具有 final 字段并不是使类不可变的强制性要求。这是优选的。这是非常不同的。 (2认同)
  • 我指的是答案开头的链接,该链接链接到 spring 文档 (2认同)

Dan*_*ski 34

这是软件开发中永无止境的讨论之一,但业内主要影响者对该主题的看法越来越多,并开始建议将构造函数注入作为更好的选择.

构造函数注入

优点:

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

缺点:

  • 更多代码(但现代IDE减轻了痛苦).

基本上,现场注入是相反的.

  • 可测试性,是的,模拟现场注入的bean对我来说是一场噩梦。有一次,我使用了构造函数注入,我不需要做任何不必要的模拟 (5认同)

die*_*ter 23

味道很重要.这是你的决定.

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

  1. 我不希望实现我所有的构造@Service,@Repository@Controller豆类.我的意思是,大约有40-50个豆子或更多.每次如果我添加一个新字段,我都必须扩展构造函数.不,我不想要,我不需要.

  2. 如果您的Bean(服务或控制器)需要注入大量其他bean,该怎么办?具有8个参数的构造函数非常难看.

  3. 如果我使用CDI,构造函数不关心我.


编辑:Vojtech Ruzicka说:

class有太多依赖,可能违反单一责任原则,应该重构

是.理论与现实.这是一个示例:DashboardController映射到单个路径*:8080/dashboard.

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

  • 非常丑陋的构造函数有8个依赖项实际上很棒,因为它是一个错误的红色标志,类有太多的依赖项,可能违反单一责任原则,应该重构.这实际上是件好事. (25认同)
  • @VojtechRuzicka肯定不是很好,但有时您无法避免。 (6认同)
  • 我会说3的经验法则,更不用说40-50,任何类的依赖都应该是你需要重构的标志.具有40个依赖关系的类不可能坚持单一责任主体或开放/关闭主体. (4认同)
  • @AminJ规则很棒,但现实却不同.公司我工作超过20年,我们有很多遗留代码.重构是一个好主意,但它需要花钱.另外我不知道为什么这么说但是我并不是指40-50个依赖,我的意思是40-50个bean,组件,模块...... (4认同)
  • @dit,您的情况显然是技术债务使您做出次优选择的情况。用您自己的话说,您处于决策受20年以上旧代码严重影响的情况。在开始新项目时,您仍然会建议使用字段注入,而不是构造函数注入吗?也许您应该在回答中做出警告,以表明在哪种情况下您将选择现场注入。 (4认同)
  • 很高兴看到一个答案,它考虑了成本和时间以及构造函数注入决策的时间。 (2认同)
  • @vphilipnyc 现在,自我回答以来已经两年多了,我仍然确信构造函数注入对我来说是反模式。它使代码的可读性降低。我想要的只是代码注入,而不是实例构建。我们还将它用于简单的 Bean,而不仅仅是“DashboardController”。 (2认同)
  • 我同意,他们提供了 @Autowired 符号并告诉我们不要使用它,我无法理解 (2认同)