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 综上所述

在这里插入图片描述

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 注入的一个优点是,这个依赖可以很方便的被改变或者重新注入。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值