问题
对于使用Spring框架的java开发人员对下面的代码应该很熟悉:
@Autowired
private HelloService helloService;
但是对于上面的代码,Sonar会提示:Remove this annotation and use constructor injection instead.
翻译成中文即:移除@Autowired
注解使用构造器注入方式替代。
IntelliJ IDEA也会提示Field injection is not recommended
翻译成中文即:不推荐使用字段注入
那么他们为什么这么建议呢?
首先我们先看一下Spring有哪些注入bean的方式
- 构造方法注入
- set方法注入
- 字段注入,即
@Autowired
注解
如何使用这些方式
构造方法注入
在Spring4.3版本之前,我们必须要在构造方法上加@Autowired
注解;在新版本中如果当前类只有一个构造方法@Autowired
注解就是可选的。
只有一个构造方法示例:
@Controller
public class ValidationController {
private final HelloService helloService;
public ValidationController(HelloService helloService) {
this.helloService = helloService;
}
}
多个构造方法示例:
@Controller
public class ValidationController {
private HelloService helloService;
@Autowired
public ValidationController(HelloService helloService) {
this.helloService = helloService;
}
public ValidationController() {
}
}
set方法注入
这种方式Spring会找到 @Autowired
注解并且调用set方法来注入所需的依赖。
@Controller
public class ValidationController {
private HelloService helloService;
public HelloService getHelloService() {
return helloService;
}
@Autowired
public void setHelloService(HelloService helloService) {
this.helloService = helloService;
}
}
字段注入
通过基于字段的注入,Spring在使用@Autowired
注释进行注释时,直接将所需的依赖项分配给字段。
@Controller
public class ValidationController {
@Autowired
private HelloService helloService;
}
这些方式有什么优缺点
既然要移除@Autowired
注解使用构造器注入方式替代,那么我们主要讨一下这些方式的优缺点。
字段注入方式的优点
相比较另外两种方式,字段注入方式的代码量更少、更整齐、更简洁
构造方法注入的优点
容易发现代码的坏味道
set方法注入和字段注入会间接违反单一职责原则。
因为在一个类依赖很多其他类的时候,如果使用构造方法注入就会发现构造方法的参数太多,这会让开发人员反思这个类真的需要这么多依赖吗?当前类是不是职责过多?
而使用字段注入时,就会把一些例如sonar的提示屏蔽掉,让开发人员误以为这样做没有问题
可以创建不可变类
在使用构造方法注入时因为构造方法是创建依赖对象的唯一方式,这非常有助于让我们创建不可变的对象。
想象一下创建一个bean之后你可以通过set方法随意修改此类的依赖,在出现问题时是很难定位的。
@Autowired
的源码有一段注释如下:Fields are injected right after construction of a bean, before any config methods are invoked. Such a config field does not have to be public.
大意是使用@Autowired
注解时,bean是在构造当前的bean之后,并且在任何的其他方法调用之前注入,因此无法设置成final类型的字段。
更明显的声明所有的依赖
使用构造方法注入,在使用这个类时就会暴露给使用者说我要依赖构造方法中的类。
但是使用字段注入时,使用者其实并不知道这个类依赖了哪些类,除非我到此类中查看这个类有多少个字段是有@Autowired
注解。
不方便迁移
spring实现了DI(控制反转),但并非是DI本身;
使用构造方法注入时,除了在类上面有@Service
、@Component
等的注解,没有其他的Spring相关的更多的注解。
使用字段注入时,除了在类上面有@Service
、@Component
等的注解之外又使用了Spring的@Autowired
注解,如果把此类迁移到其他没有spring的环境时是完成不了注入的。
不方便测试
在使用构造方法注入时,单元测试时开发人员可以直接传入一个mock的类或者其他的任何被测试类依赖的子类;
当然我们也可以使用set方式注入一个mock的类,但是如果代码修改了新增了一个依赖,那么我们很容易忘掉在测试代码中set新增的依赖,直到运行的时候我们才会看到可能有NPE异常爆出;但是构造方法就不必有这种烦恼,因为如果新增了一个依赖,测试方法会马上编译不通过。
使用字段注入,必须依赖Spring去帮助注入依赖的类
总结
通过构造方法注入bean是我们更容易创建不可变类,代码更健壮、更具有可测试性、更容易避免NPE。
网友评论