[Field injection is not recommended]为什么Spring的@Autowire不推荐注解在属性上?
背景
大家都知道spring中的bean可以使用@Autowire注解进行自动装配注入。其中有三种方式:
Constructor-based dependency injection 基于构造函数的依赖注入
Setter-based dependency injection 基于setter方法的依赖注入
Field-based dependency injection 基于属性字段的依赖注入
但是在Idea中,使用基于属性的注入(代码示例如下),会吃到“黄牌”warning
。
<pre mdtype="fences" cid="n6" contenteditable="true" lang="java" class="md-fences md-end-block md-fences-with-lineno ty-contain-cm modeLoaded md-focus" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px 0px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> @Autowire
private JdbcTemplate jdbcTemplate</pre>
warning
信息:
Field injection is not recommended
Spring Team recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies".
为什么不推荐使用属性注入?
- 属性注入字段不允许用final修饰
基于属性字段的注入得到的bean不允许声明为final类型。只能通过基于构造函数的注入来声明不可变的bean(immutable bean)。
- 违反单一原则
如果过多地使用属性注入,很容易写很多个bean进行装配。而使用构造注入时,bean作为构造函数的参数,如果数量太多,更容易让人发现该类包含了太多协作者(违反了单一原则),进而将类拆分为更小、更易于维护的碎片。
- 和spring的IOC机制紧耦合
当你使用基于字段的依赖注入方式,完全把控制权全给Spring的IOC机制,在Spring容器之外,其它类无法重新设置某个注入的属性bean(反射机制除外)。
- 不方便对注入的bean进行校验
基于字段的依赖注入方式,类构造时并没有拿到被注入的属性的值,只有在被业务使用的时候才会拿到,不方便对该属性进行校验。假如使用构造注入,就可以在构造函数中进行校验,比如非空校验(避免NPE)。当然使用Spring的校验注解也可以实现。
- 隐藏了依赖关系
当你使用Spring的IOC的时候,被注入的类应当使用一些public类型(构造方法,和setter类型方法)的方法来向外界表达:我需要什么依赖。基于字段的依赖注入的方式,基本都是private形式的,外界不易理解所有依赖关系。
结论
避免使用基于字段的依赖注入,推荐使用基于构造函数和基于setter的依赖注入。对于必需的依赖项,建议使用基于构造函数的注入,并声明为final类型,同时可以在构造函数中校验防止它们为null。对于可选的依赖项,建议使用基于Setter的注入。