Java 究竟什么是场注入以及如何避免它?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39890849/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What exactly is Field Injection and how to avoid it?
提问by T. Jung
I read in some posts about Spring MVC and Portlets that field injectionis not recommended. As I understand it, field injectionis when you inject a Bean with @Autowired
like this:
我在一些关于 Spring MVC 和 Portlets 的帖子中读到不建议使用字段注入。据我了解,字段注入是当您@Autowired
像这样注入一个 Bean 时:
@Component
public class MyComponent {
@Autowired
private Cart cart;
}
During my research I also read about constructor injection:
在我的研究过程中,我还阅读了构造函数注入:
@Component
public class MyComponent {
private final Cart cart;
@Autowired
public MyComponent(Cart cart){
this.cart = cart;
}
}
What are the advantages and the disadvantages of both of these types of injections?
这两种注射方式的优缺点是什么?
EDIT 1:As this question is marked as duplicate of this questioni checked it. Cause there aren't any code examples neither in the question nor in the answers it's not clear to me if i'm correct with my guess which injection type i'm using.
编辑 1:由于这个问题被标记为这个问题的重复,我检查了它。因为在问题和答案中都没有任何代码示例,我不清楚我是否正确猜测我正在使用哪种注入类型。
采纳答案by Vojtech Ruzicka
Injection types
注射类型
There are three options for how dependencies can be injected into a bean:
关于如何将依赖项注入到 bean 中,有以下三个选项:
- Through a constructor
- Through setters or other methods
- Through reflection, directly into fields
- 通过构造函数
- 通过 setter 或其他方法
- 通过反射,直接进入领域
You are using option 3. That is what is happening when you use @Autowired
directly on your field.
您正在使用选项 3。这就是您@Autowired
直接在您的领域使用时发生的情况。
Injection guidelines
注射指南
A general guideline, which is recommended by Spring(see the sections on Constructor-based DIor Setter-based DI) is the following:
Spring 推荐的一般准则(请参阅基于Constructor 的 DI或基于 Setter 的 DI 部分)如下:
- For mandatory dependencies or when aiming for immutability, use constructor injection
- For optional or changeable dependencies, use setter injection
- Avoid field injection in most cases
- 对于强制依赖或以不变性为目标时,请使用构造函数注入
- 对于可选或可变的依赖项,使用 setter 注入
- 在大多数情况下避免场注入
Field injection drawbacks
场注入缺点
The reasons why field injection is frowned upon are as follows:
现场注入不受欢迎的原因如下:
- You cannot create immutable objects, as you can with constructor injection
- Your classes have tight coupling with your DI container and cannot be used outside of it
- Your classes cannot be instantiated (for example in unit tests) without reflection. You need the DI container to instantiate them, which makes your tests more like integration tests
- Your real dependencies are hidden from the outside and are not reflected in your interface (either constructors or methods)
- It is really easy to have like ten dependencies. If you were using constructor injection, you would have a constructor with ten arguments, which would signal that something is fishy. But you can add injected fields using field injection indefinitely. Having too many dependencies is a red flag that the class usually does more than one thing, and that it may violate the Single Responsibility Principle.
- 您不能像构造函数注入一样创建不可变对象
- 你的类与你的 DI 容器紧密耦合,不能在它之外使用
- 没有反射,你的类不能被实例化(例如在单元测试中)。您需要 DI 容器来实例化它们,这使您的测试更像集成测试
- 你真正的依赖是从外部隐藏的,不会反映在你的接口中(构造函数或方法)
- 拥有十个依赖项真的很容易。如果您使用构造函数注入,您将拥有一个带有 10 个参数的构造函数,这将表明某些事情是可疑的。但是您可以无限期地使用字段注入来添加注入的字段。依赖太多是一个危险信号,表明该类通常会做不止一件事情,并且可能违反单一职责原则。
Conclusion
结论
Depending on your needs, you should primarily use constructor injection or some mix of constructor and setter injection. Field injection has many drawbacks and should be avoided. The only advantage of field injection is that it is more convenient to write, which does not outweigh all the cons.
根据您的需要,您应该主要使用构造函数注入或构造函数和 setter 注入的某种混合。场注入有很多缺点,应该避免。场注入唯一的优点就是写起来更方便,不失为弊大于利。
Further reading
进一步阅读
I wrote a blog article about why field injection is usually not recommended: Field Dependency Injection Considered Harmful.
我写了一篇关于为什么通常不推荐字段注入的博客文章:字段依赖注入被认为是有害的。
回答by dieter
Matter of taste. It is your decision.
口味问题。这是你的决定。
But I can explain, why I never use constructor injection.
但我可以解释,为什么我从不使用构造函数注入。
I don't want to implement a constructor for all my
@Service
,@Repository
and@Controller
beans. I mean, there are about 40-50 beans or more. Every time if I add a new field I would have to extend the constructor. No. I don't want it and I don't have to.What if your Bean (Service or Controller) requires a lot of other beans to be injected? A constructor with 4+ parameters is very ugly.
If I'm using CDI, constructor does not concern me.
我不想为我的所有
@Service
,@Repository
和@Controller
bean实现构造函数。我的意思是,大约有 40-50 个豆子或更多。每次如果我添加一个新字段,我都必须扩展构造函数。不,我不想要,也没有必要。如果您的 Bean(服务或控制器)需要注入大量其他 Bean 怎么办?带有 4 个以上参数的构造函数非常难看。
如果我使用 CDI,构造函数与我无关。
EDIT #1: Vojtech Ruzicka said:
编辑 #1:Vojtech Ruzicka 说:
class has too many dependencies and is probably violating single responsibility principle and should be refactored
类依赖太多,可能违反单一职责原则,应该重构
Yes. Theory and reality.
Here is en example: DashboardController
mapped to single path *:8080/dashboard
.
是的。理论与现实。这是一个示例:DashboardController
映射到单个路径*:8080/dashboard
。
My DashboardController
collects a lot of informations from other services to display them in a dashboard / system overview page. I need this single controller. So I have to secure only this one path (basic auth or user role filter).
我DashboardController
从其他服务收集了大量信息,以将它们显示在仪表板/系统概览页面中。我需要这个单一的控制器。所以我只需要保护这一条路径(基本身份验证或用户角色过滤器)。
EDIT #2: Since everyone is focused on the 8 parameters in the constructor... This was a real-world example - an customers legacy code. I've changed that. The same argumentation applies to me for 4+ parameters.
编辑 #2:由于每个人都关注构造函数中的 8 个参数......这是一个真实的例子 - 客户遗留代码。我已经改变了。对于 4 个以上的参数,同样的论证适用于我。
It's all about code injection, not instance construction.
这完全是关于代码注入,而不是实例构建。
回答by Daniel Olszewski
This is one of the never-ending discussions in software development, but major influencers in the industry are getting more opinionated about the topic and started to suggest constructor injection as the better option.
这是软件开发中永无止境的讨论之一,但行业中的主要影响者对这个话题越来越有意见,并开始建议构造函数注入作为更好的选择。
Constructor injection
构造函数注入
Pros:
优点:
- Better testability. You do not need any mocking library or a Spring context in unit tests. You can create an object that you want to test with the newkeyword. Such tests are always faster because they not rely on the reflection mechanism. (This questionwas asked 30 minutes later. If the author had used constructor injection it would have not appeared).
- Immutability. Once the dependencies are set they cannot be changed.
- Safer code. After execution of a constructor your object is ready to use as you can validate anything that was passed as a parameter. The object can be either ready or not, there is no state in-between. With field injection you an introduce intermediate step when the object is fragile.
- Cleaner expression of mandatory dependencies. Field injection is ambiguous in this matter.
- Makes developers think about the design. dit wrote about a constructor with 8 parameters, which actually is the sign of a bad design and the God object anti-pattern. It does not matter whether a class has 8 dependencies in its constructor or in fields, it is always wrong. People are more reluctant to add more dependencies to a constructor than via fields. It works as a signal to your brain that you should stop for a while and think about your code structure.
- 更好的可测试性。在单元测试中您不需要任何模拟库或 Spring 上下文。您可以使用new关键字创建要测试的对象。此类测试总是更快,因为它们不依赖于反射机制。(这个问题是30分钟后问的,如果作者用了构造函数注入就不会出现了)。
- 不变性。一旦设置了依赖项,它们就无法更改。
- 更安全的代码。执行构造函数后,您的对象就可以使用了,因为您可以验证作为参数传递的任何内容。对象可以准备好也可以不准备好,中间没有状态。使用场注入,您可以在对象脆弱时引入中间步骤。
- 强制依赖的更清晰的表达。场注入在这件事上是模棱两可的。
- 让开发者思考设计。dit 写了一个带有 8 个参数的构造函数,这实际上是一个糟糕的设计和上帝对象反模式的标志。一个类在其构造函数或字段中是否有 8 个依赖项并不重要,它总是错误的。与通过字段相比,人们更不愿意向构造函数添加更多依赖项。它向您的大脑发出信号,告诉您应该停下来思考一下您的代码结构。
Cons:
缺点:
- More code(but modern IDEs alleviate the pain).
- 更多代码(但现代 IDE 减轻了痛苦)。
Basically, the field injection is the opposite.
基本上,场注入是相反的。
回答by wujek.oczko
One more comment - Vojtech Ruzicka stated that Spring injects beans in such three ways (the answer with the biggest numbers of points) :
还有一条评论 - Vojtech Ruzicka 表示 Spring 以三种方式注入 bean(得分最多的答案):
- Through a constructor
- Through setters or other methods
- Through reflection, directly into fields
- 通过构造函数
- 通过 setter 或其他方法
- 通过反射,直接进入领域
This answer is WRONG - because FOR EVERY KIND OF INJECTION SPRING USES REFLECTION! Use IDE, set breakpoint on setter / constructor, and check.
这个答案是错误的 - 因为对于每种注射弹簧都使用反射!使用 IDE,在 setter/constructor 上设置断点,然后检查。
This can be a matter of taste but it can also be a matter of a CASE. @dieter provided an excellent case when field injection is better. If You're using field injection in integration tests that are setting up Spring context - the argument with testability of the class is also invalid - unless You want to write later on tests to Your integration tests ;)
这可能是品味问题,但也可能是 CASE 问题。@dieter 提供了一个很好的例子,当场注入更好时。如果您在设置 Spring 上下文的集成测试中使用字段注入 - 类的可测试性参数也是无效的 - 除非您想稍后编写集成测试的测试;)