我知道这是一场激烈的辩论,随着时间的推移,人们对最佳做法的看法往往会发生变化。
我以前在类中只使用字段注入,直到我开始在不同的博客(例如:佩特里凯南、沙哈德和福勒)上阅读构造函数注入的好处。从那时起,我已经将我的方法转换为对所需的依赖项使用构造函数注入,对于可选的依赖项使用setter注入。
然而,我最近进行了一场辩论和JMockit的作者--一个模仿的框架--他认为构造函数和setter注入是不好的做法,并表明JEE社区同意他的观点。
在当今世界,是否有一种更好的注射方式?现场注射是首选吗?
在过去的几年中,我从字段注入转到构造函数注入,我发现使用它要清楚得多,但我想知道是否应该重新审视我的观点。JMockit (罗格里奥·利森菲尔德)的作者显然精通DI,因此考虑到他强烈反对构造函数/setter注入,我觉得有义务回顾我的方法。
发布于 2015-10-23 19:11:08
对于我的口味来说,现场注射有点“远处的诡异动作”。
考虑一下您在Google组文章中提供的示例:
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中可能有一个完全相同的):
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容器所要做的就是对构造函数进行反射,以确定它需要更新哪些对象。但是这种反射是以一流的方式在公共成员上进行的;也就是说,元数据已经是类的一部分,已经在构造函数中声明了,这种方法的明确目的是为类提供它所需要的依赖关系。
当然,这是很多样板,但这就是设计语言的方式。注释似乎是对应该内置于语言本身的东西的一种肮脏的攻击。
发布于 2015-10-25 11:23:29
现场注射得到了我明确的反对票。
就像罗伯特·哈维( Robert )一样,对我的口味来说,它有点太自动化了。与隐式代码相比,我更喜欢显式代码,并且只能容忍间接,因为它提供了明显的好处,因为它使代码更难理解和推理。
与Maciejłapuk一样,我不喜欢使用反射或DI/IoC框架来实例化类。就像开车去罗马从阿姆斯特丹到巴黎一样。
请注意,我爱DI和它带来的所有好处。只是不确定是否需要框架来实例化一个类。特别是IoC容器或其他DI框架对测试代码可能产生的影响。
我喜欢我的测试是非常直接的。我不喜欢经历设置IoC容器或其他DI框架的间接过程,然后让它们设置我的类。只是觉得很尴尬。
它使我的测试依赖于框架的全局状态。也就是说,我不能再并行地运行两个测试,这些测试需要用不同的依赖项来设置类X的实例。
至少对于构造函数和setter注入,您可以选择在测试中不使用框架和/或反射。
发布于 2018-02-19 18:26:33
这似乎是一场激烈的讨论。需要解决的第一件事是,字段注入不同于Setter注入。我已经和一些认为Field和Setter注入是相同的人一起工作了。
因此,要明确的是:
现场注射:
@Autowired
private SomeService someService;赛特注射:
@Autowired
public void setSomeService(SomeService someService) {
this.someService = someService;
}但是,对我来说,两个人都有同样的担忧。
我最喜欢的构造函数注入:
@AutoWired
public MyService(SomeService someService) {
this.someService = someService;
}我有以下理由相信构造函数注入优于setter/field注入(我将引用一些Spring链接来说明我的观点):
@AutoWired越少,代码对框架的依赖性就越小。我知道,简单地更改项目中的DI框架的可能性并不能证明这是正当的(谁会这么做,对吧?)但对我来说,这表明了更好的代码(我在带有EJB和查找的硬道中学到了这一点)。使用Spring,您甚至可以使用构造函数注入删除@AutoWired注释。如果您想了解更多信息,我推荐Spring中的这篇老文章(但仍然相关),告诉我们为什么会使用如此多的setter注入和使用构造函数注入的建议:
setter注入的使用频率比您预期的要高得多,因为像Spring这样的框架通常更适合使用setter注入来配置,而不是构造函数注入。
关于为什么他们认为构造函数更适合应用程序代码的原因的说明:
我认为构造函数注入比框架代码更适用于应用程序代码。在应用程序代码中,对需要配置的可选值的需求相对较小。
如果您不太确定构造函数的优点,那么也许将setter (不是字段)与构造函数注入混合的想法是一个选项,正如弹簧队所解释的那样:
由于您可以同时使用基于构造函数和基于设置的DI,因此使用构造函数参数作为强制依赖项和使用Setter来处理可选依赖项是一个很好的经验规则。
但是请记住,字段注入可能是三个字段中的一个要避免。
https://softwareengineering.stackexchange.com/questions/300706
复制相似问题