Spring 中不同依赖注入方式的对比与剖析
一、前言
在 Controller 层注入 Service 层的依赖时,我们通常会使用 @Autowired 自动注入,例如:
这时 IntelliJ IDEA 通常会显示 Field injection is not recommended 的警告:
查看可以得知,官方不推荐使用 Field 进行注解,而推荐使用构造器或 Setter 的方式进行注解。
另外,当使用 @Resource 进行变量注入时,并不会有警告,例如:
那么,使用 @Autowired 与 @Resource 变量注入与构造方法注入、Setter 注入之间有什么区别呢?
二、Field 变量注入
注解 @Autowired 与 @Resource 的效果差不多,主要的区别是: @Autowired 注解是 Spring 中的,默认按类型注入;@Resource 注解是 Java EE 中自带的,默认按名称注入的,不建议使用。这里主要使用的是 Spring 中的 @Autowired 注解。
基于 Filed 变量注入的示例:
(1) 优点:
使用简单,只需要把 @Autowired 放到待注入依赖的变量之上就行了。新增 Field 时也不用修改什么代码,方便维护。
减少了大量冗余代码,使代码看起来简洁明了,可读性高。
(2) 缺点:
当注入的对象依赖其它的对象,而被依赖的对象没被创建的话,就容易引起空指针异常。
类与 DI 容器高度耦合,类不通过反射不能被实例化,需要用 DI 容器去实例化它,所以不能在外部使用或重用它。(另一方面,这对于单元测试也很不友好,更像集成测试)
不能将对象标记为 final 的,该属性实例化后可能被修改。从理论上讲,该类可以在实例化注入的属性后,对其进行修改。
可能会导致循环依赖的问题,即 A 里面注入 B,B 里面又注入 A。
三、Setter 注入
基于 Setter 方法注入的示例:
优点:
当待注入的属性过多时,使用构造方法显得很笨重,Setter 方法更轻便。
方便让类在实例化之后重新对该属性进行配置或注入。
缺点:Setter 方法过多,会导致代码冗余,且维护起来很麻烦。
注:在 Spring 3.x 刚推出的时候,官方推荐使用的注入方式就是这种。但在 Spring 4.x 的时候,官方换成推荐使用构造方法注入了。
四、构造方法注入
基于构造方法注入的示例:
结合 Spring 官方的推荐理由,使用构造方法注入的优点如下:
依赖不可变:可以加入 final 来约束 FIeld,防止属性在实例化后被修改。也叫做保证注入的组件不可变。
依赖不为空:在实例化的时候会检查构造方法参数是否为空,如果为空(未找到该类型的实例对象)则会抛出异常。
单一职责原则:当使用构造方法注入时,如果参数过多,你会发现当前类的职责过大,需要进行拆分。而使用 Field 注入时,你并不会意识到此问题。
更利于单元测试:按照其他两种方式注入,在进行单元测试时需要初始化整个 Spring 的环境,而采用构造方法注入时,只需要初始化需要的类即可,即可以直接实例化需要的类。
避免循环依赖:若使用构造方法注入,Spring 在项目启动的时候会检测循环依赖,抛出 BeanCurrentlyInCreationException 异常。
保证返回客户端(调用方)的代码的时候是完全初始化的状态。
五、总结
综上,这里推荐优先使用构造方法进行注入,然后结合 Filed 变量注入和 Setter 方法注入的优点,搭配使用。
参考:
如有问题,欢迎交流~(完)
版权声明: 本文为 InfoQ 作者【Deecyn】的原创文章。
原文链接:【http://xie.infoq.cn/article/574e5059d971d00246602a340】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论