Spring依赖注入方式详解

  • 1. Spring中的三种依赖注入方式
  • 1.1 Field Injection
  • 1.2 Constructor Injection
  • 1.1 Setter Injection
  • 2. Spring三种依赖注入对比分析
  • 2.1 可靠性
  • 2.2 可维护性
  • 2.3 可测试性
  • 2.4 灵活性
  • 2.5 循环关系的检测
  • 2.6 性能表现
  • 2.7 综上所述
  • 3. Spring官方推荐构造器注入原因
  • 3.1 Autowired注解警告Field injection is not recommended
  • 3.2 Spring开发团队建议注入方式


1. Spring中的三种依赖注入方式

1.1 Field Injection

1.Autowired注入
@Autowired注解的一大使用场景就是Field Injection。

注解原理
这种注入方式通过Java的反射机制实现,所以private的成员也可以被注入具体的对象。

引入依赖
@Autowired为Spring 框架提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired。

装配顺序:
1.按照type在上下文中查找匹配的bean,查找type为Svc的bean
2.如果有多个bean,则按照name进行匹配如果有@Qualifier注解,则按照@Qualifier指定的name进行匹配,查找name为svcA的bean如果没有,则按照变量名进行匹配查找name为svc的bean。
3.匹配不到,则报错。(@Autowired(required=false),如果设置required为false(默认为true),则注入失败时不会抛出异常)。

// 方式一:Autowired注入,@Autowired(required=false)注入失败不校验
    @Autowired
    @Qualifier("InjectionAServiceImpl")
    InjectionService injectionService;

2.Resource注入
@Resource是 JSR-250 定义的注解。Spring 在 CommonAnnotationBeanPostProcessor实现了对JSR-250的注解的处理,其中就包括@Resource。

@Resource有两个重要的属性:name和type,而Spring 将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。

装配顺序:
1.如果同时指定了name和type,则从 Spring 上下文中找到唯一匹配的 bean 进行装配,找不到则抛出异常。
2.如果指定了name,则从上下文中查找名称(id)匹配的 bean 进行装配,找不到则抛出异常。
3.如果指定了type,则从上下文中找到类型匹配的唯一 bean 进行装配,找不到或是找到多个,都会抛出异常。
4.如果既没有指定name,又没有指定type,则默认按照byName方式进行装配;如果没有匹配,按照byType进行装配。

@Resource(name="InjectionAServiceImpl",type = Class)
    InjectionService injectionService;

1.2 Constructor Injection

Constructor Injection是构造器注入,是我们日常最为推荐的一种使用方式。
将各个必需的依赖全部放在带有注解构造方法的参数中,并在构造方法中完成对应变量的初始化,这种方式,就是基于构造方法的注入。

这种注入方式很直接,通过对象构建的时候建立关系,所以这种方式对对象创建的顺序会有要求,当然Spring会为你搞定这样的先后顺序,除非你出现循环依赖,然后就会抛出异常。

注:在 Spring 4.3 及以后的版本中,如果这个类只有一个构造方法,那么这个构造方法上面也可以不写 @Autowired 注解。比如:

// 方式二:构造器注入
    private final InjectionService injectionService;
    @Autowired
    public InjectionController(@Qualifier("InjectionAServiceImpl")InjectionService injectionService) {
        this.injectionService = injectionService;
    }

1.1 Setter Injection

Setter Injection也会用到@Autowired注解,但使用方式与Field Injection有所不同,Field Injection是用在成员变量上,而Setter Injection的时候,是用在成员变量的Setter函数上。
注:在 Spring 4.3 及以后的版本中,setter 上面的 @Autowired 注解是可以不写的。

// 方式三:setter注入
    private InjectionService injectionService;
    @Autowired
    public void setInjectionService(@Qualifier("InjectionAServiceImpl") InjectionService injectionService) {
        this.injectionService = injectionService;
    }

2. Spring三种依赖注入对比分析

2.1 可靠性

从对象构建过程和使用过程,看对象在各阶段的使用是否可靠来评判:
由于构造函数有严格的构建顺序和不可变性,一旦构建就可用,且不会被更改。
Field Injection:不可靠
Constructor Injection:可靠
Setter Injection:不可靠

2.2 可维护性

主要从更容易阅读、分析依赖关系的角度来评判:
还是由于依赖关键的明确,从构造函数中可以显现的分析出依赖关系,对于我们如何去读懂关系和维护关系更友好。
Field Injection:差
Constructor Injection:好
Setter Injection:差

2.3 可测试性

当在复杂依赖关系的情况下,考察程序是否更容易编写单元测试来评判
Constructor Injection和Setter Injection的方式更容易Mock和注入对象,所以更容易实现单元测试。
Field Injection:差
Constructor Injection:好
Setter Injection:好

2.4 灵活性

主要根据开发实现时候的编码灵活性来判断
由于Constructor Injection对Bean的依赖关系设计有严格的顺序要求,所以这种注入方式不太灵活。相反Field Injection和Setter Injection就非常灵活,但也因为灵活带来了局面的混乱,也是一把双刃剑
Field Injection:很灵活
Constructor Injection:不灵活
Setter Injection:很灵活

2.5 循环关系的检测

对于Bean之间是否存在循环依赖关系的检测能力:
Field Injection:不检测
Constructor Injection:自动检测
Setter Injection:不检测

2.6 性能表现

不同的注入方式,对性能的影响
主要影响就是启动时间,由于Constructor Injection有严格的顺序要求,所以会拉长启动时间。
Field Injection:启动快
Constructor Injection:启动慢
Setter Injection:启动快

2.7 综上所述

spring boot 依赖注入条件 spring依赖注入方式有哪些_spring boot 依赖注入条件

3. Spring官方推荐构造器注入原因

3.1 Autowired注解警告Field injection is not recommended

在使用 IDEA 进行 Spring 开发的时候,当你在字段上面使用@Autowired注解的时候,你会发现 IDEA 会有警告提示:

Field injection is not recommended Inspection info: Spring Team Recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies.

翻译过来就是这个意思:
不建议使用基于 field 的注入方式。Spring 开发团队建议:在你的Spring Bean 永远使用基于constructor 的方式进行依赖注入。对于必须的依赖,永远使用断言来确认。

将光标放到@Autowired处,使用Alt + Enter 快捷进行修改之后,代码就会变成基于 Constructor 的注入方式。如果按照 Spring 团队的建议,如果svc是必须的依赖,应该使用Assert.notNull(svc, “svc must not be null”)来确认。

3.2 Spring开发团队建议注入方式

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.

简单来说,就是强制依赖就用构造器方式可选、可变的依赖就用 setter 注入。当然你可以在同一个类中使用这两种方法。构造器注入更适合强制性的注入旨在不变性,Setter 注入更适合可变性的注入。

Spring 这样推荐的理由,首选基于构造方法注入,
❝The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.❞

Spring 团队提倡使用基于构造方法的注入,因为这样一方面可以将依赖注入到一个不可变的变量中 (注:final 修饰的变量),另一方面也可以保证这些变量的值不会是 null。此外,经过构造方法完成依赖注入的组件 (注:比如各个 service),在被调用时可以保证它们都完全准备好了。与此同时,从代码质量的角度来看,一个巨大的构造方法通常代表着出现了代码异味,这个类可能承担了过多的责任。

而对于基于 setter 的注入,他们是这么说的:
❝Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later.❞

基于 setter 的注入,则只应该被用于注入非必需的依赖,同时在类中应该对这个依赖提供一个合理的默认值。如果使用 setter 注入必需的依赖,那么将会有过多的 null 检查充斥在代码中。使用 setter 注入的一个优点是,这个依赖可以很方便的被改变或者重新注入。