先让咱们来总结下,@Autowired的使用方式有哪些。根据文档上的说明,@Autowired注解有三种经常使用方式。一种是在构造器的前面使用,后续咱们简称为“constructor方式”;一种是在setter方法前面使用,后续简称为“setter方式”;最后一种是直接在声明的类属性上使用,后续简称“field方式” spring
Spring依赖注入的@Autowired总结一些想法。最近笔者看到一则实践建言,建议Spring的依赖注入采用构造器的方式(下面会提到)。看到这条,难免心中会有疑惑,为何? 工具
相信看过Spring依赖注入示例代码、项目源码的人,对笔者上面提到的三种方式不会陌生,应该都有所见闻,并且应该知道@Autowired默认是依据类型来进行注入的。既然提供了三种方式,为何那条建言推荐使用constructor方式呢? 测试
@Controller
public class ItemStoreController{
private final UserService userService;
@Autowired
public ItemStoreController (UserService userService){
this.userService = userService;
}
//...省略
}
优点:
1.能够将依赖的属性设为final,避免了不指望的可变性。 this
2.在写测试用例时,这种方式的优点会更明显。写测试代码时,依赖的属性对象通常是经过mock实现的,可是脱离了Spring的依赖注入机制,如何将mock的对象进行赋值呢?经过构造器能够很好的完成这项任务。写过测试用例的人,应该对此很是赞同。但笔者对此不太苟同,假设采用了field方式,spring-test包如今提供了工具类能够实现将mock的对象赋值给测试对象的属性。 spa
劣势: code
1. 没法再次配置或再次注入,当指望再次注入bean时,则必须从新构造一个新的实例。这项跟上面的优点对比,自相矛盾了,具体还得看你的应用场景了。但笔者未想到须要再次注入的场景,在一篇博客中看到一句“Management through JMX MBeans is a compelling use case”,但愿了解的朋友能够指点一二。 对象
2. 若是当前类依赖的bean过多,那么构造器就会变得很笨拙了。从表象上看,这项说的确实在理,但从另外一个角度来看,这项未必算是劣势。假如当前类依赖了过多的bean,这个类是否须要从新审视一下,是否须要重构呢? 文档
3. 存在循环依赖的风险。这点说的应该算戳中痛点了,若采用constructor方式,切记避免循环依赖。 源码
@Controller
public class ItemStoreController{
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
//...省略
}
优点: 博客
1. 对比constructor方式,很明显setter方式“重塑性”强。
2. 一样能够解决测试时绑定mock对象的问题。
3. 避免了依赖bean太多致使的构造器笨拙的现象。
劣势:
1. 假设代码的应用场景渴望不可变性的话,那么setter方式天然暴露了太多。有人可能会提出,把依赖注入的属性设为final如何?既然你的本意不变,又暴露了可变的方法,岂不是本身打脸么。
@Controller
public class ItemStoreController{
@Autowired
private UserService userService;
//...省略
}
优点:
1. 咋一看,是否是“清爽”了许多?能够没有专门的构造器和setter方法,直接注解在属性上,简单明了。
2. 避免了依赖bean太多致使的构造器笨拙的现象。但每每容易忽视了过多依赖致使的类结构的合理性,因此此项是优是劣,不是绝对的。
劣势:
1. 写测试用例时没法直接绑定mock的依赖对象。但spring-test包中能够解决这个问题了。
2. 没有“重塑性”。
3. 综上所述,@Autowired的三种用法其实没有所谓的孰优孰劣,笔者也就不在此提倡哪一种用法被喷了。使用者须要根据具体的应用场景,例如可变性的要求、“重塑性”的须要、测试时的方便、代码结构合理性等等因素吧,采用适合本身的方式。
4. 笔者在作上述内容的思考时,想到了另一个“话题”,不在本篇中叙述了,否则有些跑题。能够先抛个引子,在后续博文中探讨。“假设采用field方式注入bean,又想保证不可变性的话,可否把属性设为final呢?”