为什么我们在使用Spring的时候应该使用构造方法注入bean

问题

对于使用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的方式

  1. 构造方法注入
  2. set方法注入
  3. 字段注入,即@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。

你可能感兴趣的:(为什么我们在使用Spring的时候应该使用构造方法注入bean)