其他分享
首页 > 其他分享> > spring为什么推荐构造器方法注入

spring为什么推荐构造器方法注入

作者:互联网

再回答这个问题之前,可先看看一下问题

下面我依次来回答这个问题

@Autowired注解

该注解为Spring框架提供的注解。

装配顺序:

通过byType在上下文中查找匹配的bean,若想通过byName的方式来注入,则需要配合@Qualifier注解一起使用,若匹配不到,则在spring启动时会报错,除非设置required=false

@Inject

该注解使用的较少,且与Autowired功能相同,唯一的区别在于两者的Inject注解没有required=false这个属性

@Resource

该注解为jdk自带注解,由J2EE提供,jdk1.8之后若想使用需要引入额外的依赖

装配顺序:

1.如果同时指定了name和type,则从spring上下文中找到我唯一匹配的bean进行装配,找不到则抛出异常

2.如果指定了name,则从上下文中查找名称匹配的bean进行装配,找不到则抛出异常

3.如果指定了type,则从上下文中找到类似屁匹配的唯一bean进行装配,找不到或者找到多个,则抛出异常

4.如果既没有指定name,也没有指定type,则默认按照byName方式进行装配

在使用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".

【355期】Spring 有哪几种依赖注入方式?官方是怎么建议使用的呢?

翻译过来就是这个意思:

不建议使用基于 field 的注入方式。

Spring 开发团队建议:在你的Spring Bean 永远使用基于constructor 的方式进行依赖注入。对于必须的依赖,永远使用断言来确认。

比如如下代码:

@Service
public class HelpService {
    @Autowired
    @Qualifier("svcB")
    private Svc svc;

    public void sayHello() {
        svc.sayHello();
    }
}

public interface Svc {
    void sayHello();
}

@Service
public class SvcB implements Svc {
    @Override
    public void sayHello() {
        System.out.println("hello, this is service B");
    }
}

将光标放到@Autowired处,使用Alt + Enter 快捷进行修改之后,代码就会变成基于Constructor的注入方式,修改之后

@Service
public class HelpService {
    private final Svc svc;
    
    @Autowired
    public HelpService(@Qualifier("svcB") Svc svc) {
        // Assert.notNull(svc, "svc must not be null");
        this.svc = svc;
    }
    
    public void sayHello() {
        svc.sayHello();
    }
}

如果按照Spring 团队的建议,如果svc是必须的依赖,应该使用Assert.notNull(svc, "svc must not be null")来确认。

修正这个警告提示固然简单,但是我觉得更重要是去理解为什么Spring 团队会提出这样的建议?直接使用这种基于 field 的注入方式有什么问题?


首先我们需要知道,Spring 中有这么3种依赖注入的方式

1. 基于 field 注入

所谓基于 field 注入,就是在bean的变量上使用注解进行依赖注入。本质上是通过反射的方式直接注入到field。这是我平常开发中看的最多也是最熟悉的一种方式,同时,也正是 Spring 团队所不推荐的方式。比如:

@Autowired
private Svc svc;

2. 基于 setter 方法注入

通过对应变量的setXXX()方法以及在方法上面使用注解,来完成依赖注入。比如:

private Helper helper;

@Autowired
public void setHelper(Helper helper) {
    this.helper = helper;
}

注:在 Spring 4.3 及以后的版本中,setter 上面的 @Autowired 注解是可以不写的。

3. 基于 constructor 注入

将各个必需的依赖全部放在带有注解构造方法的参数中,并在构造方法中完成对应变量的初始化,这种方式,就是基于构造方法的注入。比如:

private final Svc svc;
    
@Autowired
public HelpService(@Qualifier("svcB") Svc svc) {
    this.svc = svc;
}

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

基于 field 注入的好处

正如你所见,这种方式非常的简洁,代码看起来很简单,通俗易懂。你的类可以专注于业务而不被依赖注入所污染。你只需要把@Autowired扔到变量之上就好了,不需要特殊的构造器或者set方法,依赖注入容器会提供你所需的依赖。

基于 field 注入的坏处

成也萧何败也萧何

基于 field 注入虽然简单,但是却会引发很多的问题。这些问题在我平常开发阅读项目代码的时候就经常遇见。

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.

简单来说,就是

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

 

标签:依赖,Autowired,Spring,svc,构造,spring,注解,注入
来源: https://blog.csdn.net/sy_white/article/details/121495562