原创

Field injection is not recommended(不再推荐使用字段注入)

缘起

最近想用springboot做一个小项目,然后使用的spring framerwork版本比较新,写代码的时候就发现idea在注入的@Autowired上给出一个警告,就像这个:
file

查了文档了解了下,原来这个提示是spring 4.0后出现的,spring4.0之后就不建议使用属性注入,改为推荐构造器注入或者setter注入。

咱下面就给大伙展示一下今后使用spring依赖注入的不同方式,以及适用情况:

缘解

尽管对于spring framerwork 5.1.3的文档只定义了两种主要的依赖注入类型,但实际是有三种的:

  • 基于构造函数的依赖注入
  • 基于setter的依赖注入
  • 基于字段的依赖注入

其中,基于字段的依赖注入被广泛的使用(这玩意确实方便),但是idea或者其他一些代码分析工具会给出这些提示。都不推荐这种写法;

基于构造函数的依赖注入

在基于构造函数的依赖注入中,类构造函数被标注为@Autowired,并包括了许多与要注入对象相关的参数。

@RestController
public class AdvertiserController {

    private final AdvertiserService advertiserService;

    @Autowired
    public AdvertiserController(AdvertiserService advertiserService) {
        this.advertiserService = advertiserService;
    }
}

然后在spring的官方文档中,@Autowired注解也是可以省略的。

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

基于Setter的依赖注入

在基于setter的依赖注入中,setter方法被标注为@Autowired。一旦使用无参数构造函数或无参数静态工厂方法实例化Bean,为了注入Bean的依赖项,Spring容器将调用这些setter方法。

@RestController
public class AdvertiserController {

    private AdvertiserService advertiserService;

    @Autowired
    public void setAdvertiserService(AdvertiserService advertiserService) {
        this.advertiserService = advertiserService;
    }

和基于构造器的依赖注入一样,在官方文档中,基于Setter的依赖注入中的@Autowired也可以省去的。

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

基于属性的依赖注入

在基于属性的依赖注入中,字段/属性被标注为@Autowired。一旦类被实例化,Spring容器将设置这些字段。

@Component
public class FieldBasedInjection {
    @Autowired
    private InjectedBean injectedBean;
}

正如所看到的,这是我们常用的方式,看起来也是依赖注入中最干净的方法,因为它避免了添加样板代码,并且不需要声明类的构造函数。代码看起来很干净简洁,但是正如代码检查器已经向我们暗示的那样,这种方法有一些缺点。

基于字段的依赖注入缺陷

不允许声明不可变域

基于字段的依赖注入在声明为final/immutable的字段上不起作用,因为这些字段必须在类实例化时实例化。声明不可变依赖项的惟一方法是使用基于构造器的依赖注入。

容易违反单一职责设计原则

在面向对象的编程中,五大设计原则SOLID被广泛应用,(国内一般为六大设计原则),用以提高代码的重用性,可读性,可靠性和可维护性

S在SOLID中代表单一职责原则,即即一个类应该只负责一项职责,这个类提供的所有服务都应该只为它负责的职责服务。

使用基于字段的依赖注入,高频使用的类随着时间的推移,我们会在类中逐渐添加越来越多的依赖项,我们用着很爽,很容易忽略类中的依赖已经太多了。但是如果使用基于构造函数的依赖注入,随着越来越多的依赖项被添加到类中,构造函数会变得越来越大,我们一眼就可以察觉到哪里不对劲。

有一个有超过10个参数的构造函数是一个明显的信号,表明类已经转变一个大而全的功能合集,需要将类分割成更小、更容易维护的块。

因此,尽管属性注入并不是破坏单一责任原则的直接原因,但它隐藏了信号,使我们很容易忽略这些信号。

与依赖注入容器紧密耦合

使用基于字段的依赖注入的主要原因是为了避免getter和setter的样板代码或为类创建构造函数。最后,这意味着设置这些字段的唯一方法是通过Spring容器实例化类并使用反射注入它们,否则字段将保持null。

依赖注入设计模式将类依赖项的创建与类本身分离开来,并将此责任转移到类注入容器,从而允许程序设计解耦,并遵循单一职责和依赖项倒置原则(同样可靠)。因此,通过自动装配(autowiring)字段来实现的类的解耦,最终会因为再次与类注入容器(在本例中是Spring)耦合而丢失,从而使类在Spring容器之外变得无用。

这意味着,如果您想在应用程序容器之外使用您的类,例如用于单元测试,您将被迫使用Spring容器来实例化您的类,因为没有其他可能的方法(除了反射)来设置自动装配字段。

隐藏依赖关系

在使用依赖注入时,受影响的类应该使用公共接口清楚地公开这些依赖项,方法是在构造函数中公开所需的依赖项,或者使用方法(setter)公开可选的依赖项。当使用基于字段的依赖注入时,实质上是将这些依赖对外隐藏了。

缘灭

说了这么多,就是尽量要减少基于字段的依赖注入的使用。可不要等到后期,项目繁重的很难受的时候,才后悔莫及。早干啥去了!趁着项目刚开始做,就做好相关的设计。该遵循的规范就遵守一下。毕竟是前辈们趟过的坑。

正文到此结束
该篇文章的评论功能已被站长关闭
本文目录