乐趣区

你还在用Autowired吗

看题目是不是吓一跳,用了好多年的 @Autowired 用错了吗?没那么夸大,本篇仅仅是讨论一下咱们 Spring 中最罕用的依赖注入形式,目前注入形式有三种,别离是:构造函数注入、办法注入、属性注入。咱们来看一小段代码

public class HelloController {
  
        @Autowired
    private BeanA a;
    
    private BeanB b;

    @Autowired
    public void setB(BeanB b) {this.b = b;}

    private final BeanC c;
    
//    @Autowired
    public HelloController(BeanC c) {this.c = c;}
}

是不是十分相熟,让我猜一下,大多数人用的是哪一种,我猜必定是第一种属性注入形式,因为我在学 Java 刚入门时也始终在用第一种。那为什么题目说你还在用 @Autowired 吗,是不须要了?还是有代替计划了,还是什么。如果还有不晓得 @Autowired 是作啥的同学请本人科普一下,其实在我写这篇文章之前,我也不晓得 @Autowired 的详情,我再提两个注解,@Required @Inject,有没有同学用过,我自学 Java 时候刚刚接触 Spring 时还是用的 Required,当年记得还探讨过 用 @Autowired 还是 @Required,也查问过材料,但那时没有记录的习惯,这也不是本篇探讨的重点。那重点是啥,当初如果不必 @Autowired 用什么,为什么不必了,有什么区别。读完本篇文章就能够学到。

还是先看下面的那段代码,有没有发现第三种办法构造函数注入形式的 @Autowired 是被正文掉的,会不会有疑难,正文掉之后注入办法还能胜利吗?这个问题,或者说这类问题,我集体学习时都会有比拟明确的办法,就是《官网文档》,咱们一起看一下 Spring Boot 2.0 的

17. Spring Bean 和依赖注入

您能够自在应用任何规范的 Spring Framework 技术来定义 bean 及其注入的依赖项。为简略起见,咱们常常发现应用 @ComponentScan(查找您的 bean)和应用@Autowired(进行构造函数注入)成果很好。

如果依照下面的倡议结构代码(将应用程序类放在根包中),则能够增加 @ComponentScan 任何参数。您的所有应用程序组件(的 @Component@Service@Repository@Controller 等)主动注册为春豆。

以下示例显示了一个 @Service 应用构造函数注入来获取所需RiskAssessorBean 的 Bean:

package com.example.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class DatabaseAccountService implements AccountService {

    private final RiskAssessor riskAssessor;

    @Autowired
    public DatabaseAccountService(RiskAssessor riskAssessor) {this.riskAssessor = riskAssessor;}
    // ...
}

如果 bean 具备一个构造函数,则能够省略@Autowired,如以下示例所示:

@Service
public class DatabaseAccountService implements AccountService {

    private final RiskAssessor riskAssessor;

    public DatabaseAccountService(RiskAssessor riskAssessor) {this.riskAssessor = riskAssessor;}
    // ...
}

请留神,应用构造函数注入如何将该 riskAssessor 字段标记为final,批示该字段 随后无奈更改。

也就是官网举荐的应用形式目前是 @Autowired 注解的构造函数注入,并且 @Autowired 能够省略。不晓得大家有没有写过非 Spring 框架的 Java 程序,如果有,能够回顾一下,构造函数用的多吗?为什么用?这个问题留给小伙伴们本人摸索。咱们持续探讨,为什么要有三种注入办法,什么时候用,优缺点是什么。

为了摸索这个问题,我在网上科普了一下相干材料,首先要搞明确问题,就须要从本源登程,有人喜爱看源码,搞清楚原理,而我喜爱看文档,对于这件事,我第一个疑难就是 @Autowired 是何时呈现的,从 Spring1.0 开始翻,2.0->3.0->2.5, 终于让我在 Spring2.5 的文档中找到了他的出世信息。

从 Spring 2.5 开始,您能够依赖 BeanFactory 的 主动拆卸 作为实现 BeanFactoryAware 接口的另一种抉择。当初,“传统” 构造函数 byType主动拆卸模式(如第 3.3.5 节“主动拆卸合作器”一节中所述)可能别离为结构函数参数或 setter 办法参数提供 BeanFactory 类型的依赖项。为了取得更大的灵活性(包含主动拆卸字段和应用多个参数办法的能力),请思考应用新的基于正文的主动拆卸性能。在这种状况下,BeanFactory只有有问题的字段,构造函数或办法带有 @Autowired 批注,它将主动连贯到冀望 BeanFactory 类型 的字段,结构函数参数或办法参数。无关更多信息,请参见题目为第 3.10.1 节“@Autowired”的局部。

而且最早呈现时,@Autowired 并不反对属性注入形式,那没有 @Autowired 之前就是另外两种,并且 1.0 时官网举荐的是 Setter 办法注入,到了 2.0 官网就始终是举荐构造函数注入了。有趣味的同学能够看一卡这篇文章,较早版本的就不再探讨了。

https://spring.io/blog/2007/0…

持续聊 @Autowired,他是从什么时候开始大量应用的,Spring3.0,这时 Spring 反对了 @Autowired 给属性应用, 但官网并没有举荐应用,到了 Spring4.3 时,官网又对构造函数进行了阐明,当一个类只有一个构造函数时,能够省略 @Autowired 的正文。那么为什么 @Autowired 注解属性的局势还会始终存在并连续至今呢,我通过与敌人探讨时获知了一部分起因,通过写代码尝试和网络上的科普最终汇总了几种状况

@Autowired 属性注入模式长处:

1、能够在单例模式下容许循环依赖注入的存在,即,解决了循环依赖注入的问题。如果有业务必须应用循环依赖注入,能够思考应用。

2、应用简略间接给属性注解即可,代码量少。

3、使代码整洁,好看。

@Autowired 属性注入模式毛病:

1、不容许申明不可变域,也就是无奈对属性进行 final 润饰。

2、容易违反繁多职责设计准则,举个例子,一个类应用 10 个以上的 @Autowired,代码看起来并无异样,但一个有 10 个参数的构造函数,曾经违反了一个良好的代码标准,从而思考是否应该优化该类的应用职责。

3、注入程序靠后,容易呈现空指针调用,举个例子,被注入的类用构造函数给其余属性赋值时,因为注入属性还为被反射注入,造成空指针异样。

4、与依赖注入容器严密耦合,即,在来到 Spring 容器外,代码将失去作用,例如单元测试。

5、没有对依赖进行查看

而后再一一剖析一下长处,其中比拟有争议的就是第一点,我集体感觉第一点并不算是长处,循环依赖这种问题,如果没有非凡状况,从架构设计上讲自身就是违反原则的一种状况,应该即时查看并发现错误的存在并排除此类问题(结构器办法,和 Setter 办法都能够做到而结构器能够在编译时发现,Setter 是在运行时发现),不用说必定是编译时发现更适合。

其余两点都趋向于可读性和好看方面的长处,其实通过共事的启发结构器形式也能够做到同样的成果,比方利用 lombok 的注解 @AllArgsConstructor, 不过这样会暗藏实现,不相熟的人会造成额定的浏览麻烦,并不举荐。

@AllArgsConstructor
@RestController
public class HelloController {

    private final BeanA a;
    private final BeanB b;
    private final BeanC c;

    @GetMapping("/hi")
    public String sayHi() {a.sayHi();
        return "Hi";
    }
}

总结

其实 @Autowired 属性注入还是 Setter 注入或者构造函数注入,在大多数状况下都不会是问题,所以其实依据集体习惯就好,另外想说的就是多关注一下所用技术的官网材料,更新,补丁等问题。最初放出 Spring5 的原文翻译。

基于构造函数或基于 setter 的 DI?

因为能够混合应用基于构造函数的 DI 和基于 setter 的 DI,因而,将构造函数用于强制性依赖项,将 setter 办法或配置办法用于可选性依赖项是一个很好的教训法令。请留神,能够 在 setter 办法上应用 @Required 批注,以使该属性成为必须的依赖项。然而,最好应用带有参数的程序验证的构造函数注入。

Spring 团队通常提倡应用构造函数注入,因为它能够让您将应用程序组件实现为不可变对象,并确保不存在必须的依赖项null。此外,注入构造函数的组件始终以齐全初始化的状态返回给客户端(调用)代码。附带阐明一下,大量的构造函数自变量是一种不好的代码味,这表明该类可能承当了太多的职责,应进行重构以更好地解决关注点拆散问题。

Setter 注入次要应仅用于能够在类中调配正当的默认值的可选依赖项。否则,必须在代码应用依赖项的任何中央执行非空查看。setter 注入的一个益处是,setter 办法可使该类的对象在当前重新配置或从新注入。因而,通过 JMX MBean 进行治理是用于 setter 注入的引人注目的用例。

应用最适宜特定班级的 DI 格调。有时,在解决您没有源代码的第三方类时,将为您做出抉择。例如,如果第三方类未公开任何 setter 办法,则构造函数注入可能是 DI 的惟一可用模式。

循环依赖

如果次要应用构造函数注入,则可能会创立无奈解决的循环依赖计划。

例如:类 A 通过构造函数注入须要类 B 的实例,而类 B 通过构造函数注入须要类 A 的实例。如果您将 A 类和 B 类的 bean 配置为互相注入,则 Spring IoC 容器会在运行时检测到此循环援用,并抛出 BeanCurrentlyInCreationException

一种可能的解决方案是编辑某些类的源代码,这些类的源代码由设置者而不是构造函数来配置。或者,防止构造函数注入,而仅应用 setter 注入。换句话说,只管不倡议这样做,然而能够应用 setter 注入配置循环依赖关系。

与典型状况(没有循环依赖关系)不同,bean A 和 bean B 之间的循环依赖关系迫使其中一个 bean 在齐全初始化之前被注入另一个 bean(经典的“养鸡和养猪”计划)

留神
只有 一个每类正文构造函数 能够作为标记 要求 ,但多个非必须的构造函数能够被注解。在这种状况下,每个候选对象都将被思考在内,Spring 将应用 最贪心的 构造函数,该构造函数能够满足其依存关系,即参数数量最多的构造函数。@Autowired倡议在正文上应用的 必填 属性 @Required。将 所需的 属性示意不须要属性主动拆卸的目标,如果它不能主动拆卸的属性被疏忽。@Required另一方面,它更弱小,因为它能够强制执行通过容器反对的任何形式设置的属性。如果未注入任何值,则会引发相应的异样。

文献资料

https://docs.spring.io/spring…

https://docs.spring.io/spring…

https://docs.spring.io/spring…

https://docs.spring.io/spring…

本文由博客群发一文多发等经营工具平台 OpenWrite 公布

退出移动版