我在一些关于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:由于这个问题被标记为这个问题的副本,我检查了它。因为没有任何代码示例,无论是在问题中还是在答案中,我不清楚如果我的猜测是正确的,我正在使用哪种注入类型。


当前回答

注入类型

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

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

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


注射指南

Spring推荐的一个通用指南(参见关于基于构造函数的DI或基于setter的DI的章节)如下:

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


现场注入缺陷

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

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.


结论

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


进一步的阅读

我写过一篇博客文章,关于为什么字段注入通常不推荐:字段依赖注入被认为是有害的。

其他回答

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

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

我不想为所有的@Service、@Repository和@Controller bean实现一个构造函数。我的意思是,大约有40-50颗豆子,甚至更多。每次如果我添加一个新字段,我就必须扩展构造函数。不。我不想要,也没必要。 如果您的Bean(服务或控制器)需要注入许多其他Bean,该怎么办?带有4个以上参数的构造函数非常丑陋。 如果我使用CDI,构造函数与我无关。


编辑# 1: Vojtech Ruzicka说:

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

是的。理论和现实。 这里有一个例子:DashboardController映射到单个路径*:8080/dashboard。

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

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

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

注入类型

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

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

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


注射指南

Spring推荐的一个通用指南(参见关于基于构造函数的DI或基于setter的DI的章节)如下:

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


现场注入缺陷

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

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.


结论

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


进一步的阅读

我写过一篇博客文章,关于为什么字段注入通常不推荐:字段依赖注入被认为是有害的。

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

构造函数注入

优点:

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 new keyword. Such tests are always faster because they do not rely on the reflection mechanism. (This question was asked 30 minutes later. If the author had used constructor injection it would not have 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 introduce an 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.

缺点:

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

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

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

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

这个答案是错误的——因为每一种注射弹簧都使用反射! 使用IDE,设置setter /构造函数上的断点,并检查。

这可能是一个品味的问题,但也可能是一个案例的问题。 @dieter提供了一个现场注射效果更好的绝佳案例。如果你在设置Spring上下文的集成测试中使用字段注入-类的可测试性参数也是无效的-除非你想在以后的测试中编写集成测试;)