Spring注解依赖注入的三种方式的优缺点以及优先选择

当咱们在使用依赖注入的时候,一般有三种方式:函数

1.经过构造器来注入;单元测试

2.经过setter方法来注入;测试

3.经过filed变量来注入;this

那么他们有什么区别吗?应该选择哪一种方式更好?spa

 

代码示例:翻译

Constructor

 1 private DependencyA dependencyA;
 2 private DependencyB dependencyB;
 3 private DependencyC dependencyC;
 4  
 5 @Autowired
 6 public DI(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {
 7     this.dependencyA = dependencyA;
 8     this.dependencyB = dependencyB;
 9     this.dependencyC = dependencyC;
10 }

Setter

 1 private DependencyA dependencyA;
 2 private DependencyB dependencyB;
 3 private DependencyC dependencyC;
 4  
 5 @Autowired
 6 public void setDependencyA(DependencyA dependencyA) {
 7     this.dependencyA = dependencyA;
 8 }
 9  
10 @Autowired
11 public void setDependencyB(DependencyB dependencyB) {
12     this.dependencyB = dependencyB;
13 }
14  
15 @Autowired
16 public void setDependencyC(DependencyC dependencyC) {
17     this.dependencyC = dependencyC;
18 }

Field

1 @Autowired
2 private DependencyA dependencyA;
3  
4 @Autowired
5 private DependencyB dependencyB;
6  
7 @Autowired
8 private DependencyC dependencyC;

 

 

三种方式的区别小结:3d

1.基于constructor的注入,会固定依赖注入的顺序;该方式不容许咱们建立bean对象之间的循环依赖关系,这种限制实际上是一种利用构造器来注入的益处 - 当你甚至没有注意到使用setter注入的时候,Spring能解决循环依赖的问题;代理

2.基于setter的注入,只有当对象是须要被注入的时候它才会帮助咱们注入依赖,而不是在初始化的时候就注入;另外一方面若是你使用基于constructor注入,CGLIB不能建立一个代理,迫使你使用基于接口的代理或虚拟的无参数构造函数。指针

3.相信不少同窗都选择使用直接在成员变量上写上注解来注入,正如咱们所见,这种方式看起来很是好,精短,可读性高,不须要多余的代码,也方便维护;code

 

缺点:

1.当咱们利用constructor来注入的时候,比较明显的一个缺点就是:假如咱们须要注入的对象特别多的时候,咱们的构造器就会显得很是的冗余、很差看,很是影响美观和可读性,维护起来也较为困难;

2.当咱们选择setter方法来注入的时候,咱们不能将对象设为final的;

3.当咱们在field变量上来实现注入的时候

    a.这样不符合JavaBean的规范,并且颇有可能引发空指针;

    b.同时也不能将对象标为final的;

  c.类与DI容器高度耦合,咱们不能在外部使用它;

    d.类不经过反射不能被实例化(例如单元测试中),你须要用DI容器去实例化它,这更像集成测试;

    ... etc.

 

来自Spring官方文档的建议:  
在Spring 3.x 中,Spring团队建议咱们使用setter来注入:

大体是说大量的构造器参数会显得很是笨重,尤为是当属性是可选的时候。setter方法可使类的对象在后来从新配置或者从新注入。提供全部的依赖意味着对象老是返回一个彻底初始化状态的client客户端(调用)。缺点是对象变得不那么适合从新配置和从新注入。

 

而在Spring 4.x 中,Spring团队再也不建议咱们使用setter来注入,改成了constructor:

Spring团队一般建议使用构造器来注入,由于它容许一个应用程序组件实现为不可变对象,并确保所需的依赖项不是空。此外构造器注入组件老是返回一个彻底初始化状态的client客户端(调用)。附注,大量的构造函数参数是一个糟糕的代码习惯,看起来也很坏,这意味着类可能有太多的责任,应该被重构,以更好地解决适当的关注点分离。

setter方法只应该主要的用在能够在类中指定合理的默认值的可选的依赖关系。不然,用到依赖的全部地方都应该进行非空检查。setter注入的一个好处是,setter方法使类的对象能够在以后从新配置或者从新注入。

(以上是本人的渣渣英语翻译结合有道得来。。大佬看到请轻喷)

 

接下来插播一条Spring 4.3 的新特征:

在Spring 4.3 之后,若是咱们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入,上代码:

以前:

 1 @Service
 2 public class FooService {
 3 
 4     private final FooRepository repository;
 5 
 6     @Autowired
 7     public FooService(FooRepository repository) {
 8         this.repository = repository
 9     }
10 }

在Spring 4.3 以后:

1 @Service
2 public class FooService {
3 
4     private final FooRepository repository;
5 
6     public FooService(FooRepository repository) {
7         this.repository = repository
8     }
9 }

如咱们所见,我去掉了构造器上的@Autowired注解,经测试后发现,程序能正常运行,repository的依赖也被成功注入了,当时感受就很amazing。。有兴趣的同窗能够试试~

 

总结:

1.强制性的依赖性或者当目标不可变时,使用构造函数注入(应该说尽可能都使用构造器来注入

2.可选或多变的依赖使用setter注入(建议可使用构造器结合setter的方式来注入

3.在大多数的状况下避免field域注入(感受大多数同窗可能会有异议,毕竟这个方式写起来很是简便,可是它的弊端确实远大于这些优势

4.Spring 4.3+ 的同窗能够试一试构造器的隐式注入,采用此方式注入后,使得咱们的代码更优雅,更独立,减小了对Spring的依赖性。

 

ps: 转载请标注出处谢谢。

相关文章
相关标签/搜索