首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >依赖注入:字段注入与构造器注入?

依赖注入:字段注入与构造器注入?
EN

Software Engineering用户
提问于 2015-10-23 16:47:18
回答 3查看 52.6K关注 0票数 76

我知道这是一场激烈的辩论,随着时间的推移,人们对最佳做法的看法往往会发生变化。

我以前在类中只使用字段注入,直到我开始在不同的博客(例如:佩特里凯南沙哈德福勒)上阅读构造函数注入的好处。从那时起,我已经将我的方法转换为对所需的依赖项使用构造函数注入,对于可选的依赖项使用setter注入。

然而,我最近进行了一场辩论和JMockit的作者--一个模仿的框架--他认为构造函数和setter注入是不好的做法,并表明JEE社区同意他的观点。

在当今世界,是否有一种更好的注射方式?现场注射是首选吗?

在过去的几年中,我从字段注入转到构造函数注入,我发现使用它要清楚得多,但我想知道是否应该重新审视我的观点。JMockit (罗格里奥·利森菲尔德)的作者显然精通DI,因此考虑到他强烈反对构造函数/setter注入,我觉得有义务回顾我的方法。

EN

回答 3

Software Engineering用户

发布于 2015-10-23 19:11:08

对于我的口味来说,现场注射有点“远处的诡异动作”。

考虑一下您在Google组文章中提供的示例:

代码语言:javascript
复制
public class VeracodeServiceImplTest {


    @Tested(fullyInitialized=true)
    VeracodeServiceImpl veracodeService;
    @Tested(fullyInitialized=true, availableDuringSetup=true)
    VeracodeRepositoryImpl veracodeRepository;
    @Injectable private ResultsAPIWrapper resultsApiWrapper;
    @Injectable private AdminAPIWrapper adminApiWrapper;
    @Injectable private UploadAPIWrapper uploadApiWrapper;
    @Injectable private MitigationAPIWrapper mitigationApiWrapper;

    static { VeracodeRepositoryImpl.class.getName(); }
...
}

所以,基本上您要说的是,“我有这个带有私有状态的类,我已经附加了@injectable注释,这意味着状态可以由外部的某个代理自动填充,即使我的状态都被声明为私有状态。”

我理解这件事的动机。这是一种尝试,以避免许多仪式,这是固有的,建立一个班级适当。基本上,人们说的是“我厌倦了编写所有的样板,所以我只想注释我所有的状态,让DI容器负责为我设置它。”

这是完全正确的观点。但它也是一种解决语言特性的方法,可以说是不应该使用的。还有,为什么停在那里?传统上,DI依赖于每个类都有一个配套的接口。为什么不也消除所有这些带有注释的接口呢?

考虑另一种选择(这将是C#,因为我更了解它,但在Java中可能有一个完全相同的):

代码语言:javascript
复制
public class VeracodeService
{        
    private readonly IResultsAPIWrapper _resultsApiWrapper;
    private readonly IAdminAPIWrapper _adminApiWrapper;
    private readonly IUploadAPIWrapper _uploadApiWrapper;
    private readonly IMitigationAPIWrapper _mitigationApiWrapper;

    // Constructor
    public VeracodeService(IResultsAPIWrapper resultsApiWrapper, IAdminAPIWrapper adminApiWrapper, IUploadAPIWrapper uploadApiWrapper,         IMitigationAPIWrapper mitigationApiWrapper)
    {
         _resultsAPIWrapper = resultsAPIWrapper;
         _adminAPIWrapper = adminAPIWrapper;
         _uploadAPIWrapper = uploadAPIWrapper;
         _mitigationAPIWrapper = mitigationAPIWrapper;
    }
}

我已经知道一些关于这门课的事了。它是一个不可变的类;状态只能在构造函数中设置(在这种情况下,引用)。而且,由于一切都来自接口,所以我可以在构造函数中交换实现,这就是您的模拟的来源。

现在,我的DI容器所要做的就是对构造函数进行反射,以确定它需要更新哪些对象。但是这种反射是以一流的方式在公共成员上进行的;也就是说,元数据已经是类的一部分,已经在构造函数中声明了,这种方法的明确目的是为类提供它所需要的依赖关系。

当然,这是很多样板,但这就是设计语言的方式。注释似乎是对应该内置于语言本身的东西的一种肮脏的攻击。

票数 54
EN

Software Engineering用户

发布于 2015-10-25 11:23:29

现场注射得到了我明确的反对票。

就像罗伯特·哈维( Robert )一样,对我的口味来说,它有点太自动化了。与隐式代码相比,我更喜欢显式代码,并且只能容忍间接,因为它提供了明显的好处,因为它使代码更难理解和推理。

与Maciejłapuk一样,我不喜欢使用反射或DI/IoC框架来实例化类。就像开车去罗马从阿姆斯特丹到巴黎一样。

请注意,我爱DI和它带来的所有好处。只是不确定是否需要框架来实例化一个类。特别是IoC容器或其他DI框架对测试代码可能产生的影响。

我喜欢我的测试是非常直接的。我不喜欢经历设置IoC容器或其他DI框架的间接过程,然后让它们设置我的类。只是觉得很尴尬。

它使我的测试依赖于框架的全局状态。也就是说,我不能再并行地运行两个测试,这些测试需要用不同的依赖项来设置类X的实例。

至少对于构造函数和setter注入,您可以选择在测试中不使用框架和/或反射。

票数 23
EN

Software Engineering用户

发布于 2018-02-19 18:26:33

这似乎是一场激烈的讨论。需要解决的第一件事是,字段注入不同于Setter注入。我已经和一些认为Field和Setter注入是相同的人一起工作了。

因此,要明确的是:

现场注射:

代码语言:javascript
复制
@Autowired
private SomeService someService;

赛特注射:

代码语言:javascript
复制
@Autowired
public void setSomeService(SomeService someService) {
    this.someService = someService;
}

但是,对我来说,两个人都有同样的担忧。

我最喜欢的构造函数注入:

代码语言:javascript
复制
@AutoWired
public MyService(SomeService someService) {
    this.someService = someService;
}

我有以下理由相信构造函数注入优于setter/field注入(我将引用一些Spring链接来说明我的观点):

  1. 防止循环依赖:正如@Tomasz所解释的,Spring能够检测Beans之间的循环依赖关系。也见春队那个。
  2. 类的依赖性是显而易见的:类在构造函数中很明显。的依赖关系,但隐藏在字段/setter注入中。我觉得我的课程就像一个“黑匣子”,里面有字段/设置器注入。而且(根据我的经验)有一种倾向,即开发人员不去关心具有许多依赖项的类,当您尝试使用构造函数注入来模拟类时,这一点很明显(请参阅下一项)。一个困扰开发人员的问题最有可能得到解决。另外,有太多依赖项的类可能违反了单一责任原则(SRP)。
  3. 模拟简单可靠:与现场注射相比,在单元测试中更容易模拟,因为您不需要任何上下文模拟或反射技术来到达类中声明的私有Bean,并且您不会是被它愚弄。您只需实例化类并传递模拟bean。简单易懂,简单易懂。
  4. 代码质量工具可以帮助您: Sonar这样的工具可以警告您类变得太复杂了,因为一个具有很多参数的类可能是一个太复杂的类,需要一些重构。当类使用Field/Setter注入时,此工具无法识别相同的问题。
  5. 更好且独立的代码:在代码中传播的@AutoWired越少,代码对框架的依赖性就越小。我知道,简单地更改项目中的DI框架的可能性并不能证明这是正当的(谁会这么做,对吧?)但对我来说,这表明了更好的代码(我在带有EJB和查找的硬道中学到了这一点)。使用Spring,您甚至可以使用构造函数注入删除@AutoWired注释。
  6. 更多的注释并不总是正确的答案: -一些原因,我真的尝试使用注释只有当它们是真正有用的。
  7. 你可以让注射保持不变。不变性是非常受欢迎的一个特性。

如果您想了解更多信息,我推荐Spring中的这篇老文章(但仍然相关),告诉我们为什么会使用如此多的setter注入和使用构造函数注入的建议:

setter注入的使用频率比您预期的要高得多,因为像Spring这样的框架通常更适合使用setter注入来配置,而不是构造函数注入。

关于为什么他们认为构造函数更适合应用程序代码的原因的说明:

我认为构造函数注入比框架代码更适用于应用程序代码。在应用程序代码中,对需要配置的可选值的需求相对较小。

最后的想法

如果您不太确定构造函数的优点,那么也许将setter (不是字段)与构造函数注入混合的想法是一个选项,正如弹簧队所解释的那样:

由于您可以同时使用基于构造函数和基于设置的DI,因此使用构造函数参数作为强制依赖项和使用Setter来处理可选依赖项是一个很好的经验规则。

但是请记住,字段注入可能是三个字段中的一个要避免

票数 10
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/300706

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档