Spring 中不同依赖注入方式的对比与剖析

用户头像
Deecyn
关注
发布于: 2020 年 05 月 13 日

一、前言

在 Controller 层注入 Service 层的依赖时,我们通常会使用 @Autowired 自动注入,例如:

@Controller
public class AnimalController {
@Autowired
private DogService dogService;
}

这时 IntelliJ IDEA 通常会显示 Field injection is not recommended 的警告:

查看可以得知,官方不推荐使用 Field 进行注解,而推荐使用构造器或 Setter 的方式进行注解。



另外,当使用 @Resource 进行变量注入时,并不会有警告,例如:

@Resource
private UserService userService;

那么,使用 @Autowired 与 @Resource 变量注入与构造方法注入、Setter 注入之间有什么区别呢?

二、Field 变量注入

注解 @Autowired 与 @Resource 的效果差不多,主要的区别是: @Autowired 注解是 Spring 中的,默认按类型注入;@Resource 注解是 Java EE 中自带的,默认按名称注入的,不建议使用。这里主要使用的是 Spring 中的 @Autowired 注解。



基于 Filed 变量注入的示例:

@Controller
public class AnimalController {
@Autowired
private DogService dogService;
@Autowired
private CatService catService;
}

(1) 优点:

  • 使用简单,只需要把 @Autowired 放到待注入依赖的变量之上就行了。新增 Field 时也不用修改什么代码,方便维护。

  • 减少了大量冗余代码,使代码看起来简洁明了,可读性高。



(2) 缺点:

  • 当注入的对象依赖其它的对象,而被依赖的对象没被创建的话,就容易引起空指针异常。

  • 类与 DI 容器高度耦合,类不通过反射不能被实例化,需要用 DI 容器去实例化它,所以不能在外部使用或重用它。(另一方面,这对于单元测试也很不友好,更像集成测试)

  • 不能将对象标记为 final 的,该属性实例化后可能被修改。从理论上讲,该类可以在实例化注入的属性后,对其进行修改。

  • 可能会导致循环依赖的问题,即 A 里面注入 B,B 里面又注入 A。

三、Setter 注入

基于 Setter 方法注入的示例:

@Controller
public class AnimalController {
private DogService dogService;
private CatService catService;
@Autowired
public void setDogService(DogService dogService) {
this.dogService = dogService;
}
@Autowired
public void setCatService(CatService catService) {
this.catService = catService;
}
}



优点:

  • 当待注入的属性过多时,使用构造方法显得很笨重,Setter 方法更轻便。

  • 方便让类在实例化之后重新对该属性进行配置或注入。

缺点:Setter 方法过多,会导致代码冗余,且维护起来很麻烦。



注:在 Spring 3.x 刚推出的时候,官方推荐使用的注入方式就是这种。但在 Spring 4.x 的时候,官方换成推荐使用构造方法注入了。

四、构造方法注入

基于构造方法注入的示例:

@Controller
public class AnimalController {
private DogService dogService;
private CatService catService;
@Autowired
public AnimalController(DogService dogService, CatService catService) {
this.dogService = dogService;
this.catService = catService;
}
}

结合 Spring 官方的推荐理由,使用构造方法注入的优点如下:

  1. 依赖不可变:可以加入 final 来约束 FIeld,防止属性在实例化后被修改。也叫做保证注入的组件不可变。

  2. 依赖不为空:在实例化的时候会检查构造方法参数是否为空,如果为空(未找到该类型的实例对象)则会抛出异常。

  3. 单一职责原则:当使用构造方法注入时,如果参数过多,你会发现当前类的职责过大,需要进行拆分。而使用 Field 注入时,你并不会意识到此问题。

  4. 更利于单元测试:按照其他两种方式注入,在进行单元测试时需要初始化整个 Spring 的环境,而采用构造方法注入时,只需要初始化需要的类即可,即可以直接实例化需要的类。

  5. 避免循环依赖:若使用构造方法注入,Spring 在项目启动的时候会检测循环依赖,抛出 BeanCurrentlyInCreationException 异常。

  6. 保证返回客户端(调用方)的代码的时候是完全初始化的状态。

五、总结

综上,这里推荐优先使用构造方法进行注入,然后结合 Filed 变量注入和 Setter 方法注入的优点,搭配使用。



参考:


如有问题,欢迎交流~(完)

发布于: 2020 年 05 月 13 日 阅读数: 86
用户头像

Deecyn

关注

Hello ! 2018.07.25 加入

Java 后端程序员

评论

发布
暂无评论
Spring 中不同依赖注入方式的对比与剖析