我在一些关于Portlets
和Bean
的帖子中读到了不建议进行现场注射的内容。因为我试图得到一个所以我问自己我是否正在使用现场注射而我无法回答它。据我了解,字段注入是指您将@Autowired
注入...
@Autowired
private Cart cart;
...
的属性,如下所示:
CartController.java:
@Configuration
public class BookShopConfiguration {
@Bean
public Cart cart(){
return new Cart();
}
//more configuration
BookshopConfiguartion.java:
Cart.java
我的...
public class MyComponent{
private Cart cart;
@Autowired
public MyComponent(Cart cart){
this.cart = cart;
}
...
用于存储和提供购物车中书籍的相关信息。
在我的研究中,我读到了构造函数注入:
MyComponent.java:
con.commit()
这两种注射剂有哪些优点和缺点?
编辑1:由于此问题被标记为this question的重复,我检查了它。因为在问题和答案中都没有任何代码示例,我不清楚我是否正确猜测我正在使用哪种注射类型。
答案 0 :(得分:130)
注射类型
如何将依赖项注入bean中有三个选项:
您正在使用选项3.这就是您在字段上直接使用@Autowired
时发生的情况。
注射指南
一般准则which is recommended by Spring(请参阅Constructor-based DI或Setter-based DI部分)如下:
现场注入缺陷
现场注射不赞成的原因如下:
<强>结论强>
根据您的需要,您应该主要使用构造函数注入或构造函数和setter注入的混合。现场注入有许多缺点,应该避免。现场注入的唯一优势是写入更方便,这不会超过所有缺点。
进一步阅读
我写了一篇博客文章,说明为什么通常不建议进行现场注射:Field Dependency Injection Considered Harmful。
答案 1 :(得分:31)
这是软件开发中永无止境的讨论之一,但业内主要影响者对该主题的看法越来越多,并开始建议将构造函数注入作为更好的选择。
构造函数注入
优点:
缺点:
基本上,现场注入是相反的。
答案 2 :(得分:19)
品味问题。这是你的决定。
但我可以解释一下,为什么我从不使用构造函数注入。
我不想为我的所有@Service
,@Repository
和@Controller
bean实施构造函数。我的意思是,大约有40-50个豆子或更多。每次如果我添加一个新字段,我都必须扩展构造函数。不,我不想要它而且我不需要。
如果您的Bean(服务或控制器)需要注入大量其他bean,该怎么办?具有8个参数的构造函数非常难看。
如果我使用CDI,构造函数与我无关。
修改强>: Vojtech Ruzicka说:
类有太多依赖项,可能违反了单个 责任原则,应该重构
是。理论与现实。
以下是示例:DashboardController
映射到单个路径*:8080/dashboard
。
我的DashboardController
从其他服务收集了大量信息,以便在仪表板/系统概述页面中显示这些信息。我需要这个单控制器。所以我必须只保护这一条路径(基本身份验证或用户角色过滤器)。
答案 3 :(得分:3)
Field
@Autowired
private DependencyA dependencyA;
@Autowired
private DependencyB dependencyB;
@Autowired
private DependencyC dependencyC;
怎么了? 如您所见,Field变体看起来非常漂亮。它非常简短,简洁,没有样板代码。该代码易于阅读和浏览。您的班级只专注于重要的课程,不会受到DI样板的污染。您只需将@Autowired注释放在字段上方即可。没有专门为DI容器提供依赖项的特殊构造函数或设置方法。 Java本身非常冗长,因此欢迎您提供所有缩短代码的机会,对吧?
违反单一责任原则
添加新的依赖项非常容易。也许太容易了。添加六个,十个甚至十个依赖关系没有问题。当您将构造函数用于DI时,在特定点之后,构造函数参数的数量变得过多,并且很明显,出了点问题。依赖关系过多通常意味着该类承担太多职责。这可能违反了“单一责任原则”和关注点分离,并且是一个很好的指示,表明该类需要进一步检查和可能的重构。直接注入字段时没有这种危险信号,因为这种方法可以无限期扩展。
依赖隐藏
使用DI容器意味着该类不再负责管理其自己的依赖项。从类中提取获取依赖项的责任。现在,其他人负责提供依赖性-DI容器或在测试中手动分配它们。当类不再负责获取其依赖项时,应使用公共接口(方法或构造函数)明确地与它们进行通信。这样,可以清楚地知道类的要求,以及它是可选的(setter)还是强制的(构造函数)。
DI容器联轴器
DI框架的核心思想之一是,托管类不应依赖于所使用的DI容器。换句话说,它应该只是一个普通的POJO,只要您将其传递给所有必需的依赖项,就可以独立地实例化。这样,您可以在单元测试中实例化它,而无需启动DI容器并单独进行测试(使用一个可以进行集成测试的容器)。如果没有容器耦合,则可以将该类用作托管或非托管类,甚至可以切换到新的DI框架。
但是,当直接注入字段时,您没有提供直接实例化具有所有必需依赖关系的类的方法。这意味着:
有一种方法(通过调用默认构造函数)在缺少某些强制协作者且使用状态会导致NullPointerException
的状态下使用new创建对象。
此类不能在DI容器(测试,其他模块)之外重用,因为除了反射以外,没有其他方法可以为其提供所需的依赖关系。
不变性
与构造函数不同,字段注入不能用于将依赖项分配给最终字段,从而有效地使您的对象可变。
答案 4 :(得分:3)
另一条评论-Vojtech Ruzicka指出,Spring通过以下三种方式注入豆子(得分最高的答案):
这个答案是错误的-因为每一种注射弹簧都需要反射! 使用IDE,在setter /构造函数上设置断点,然后检查。
这可能是口味问题,但也可能是案例问题。 当场注入更好时,@ dieter提供了一个很好的例子。如果您在建立Spring上下文的集成测试中使用字段注入-带有类可测试性的参数也是无效的-除非您想稍后在测试中编写集成测试;)