关于java:面试官Autowired-Resource-Inject-三个注解的区别一下懵了

18次阅读

共计 5850 个字符,预计需要花费 15 分钟才能阅读完成。

作者:Richard_Yi\
起源:juejin.cn/post/6844904056230690824

前言

本章的内容次要是想探讨咱们在进行 Spring 开发过程当中,对于依赖注入的几个知识点。感兴趣的读者能够先看下以下问题:

  • @Autowired, @Resource, @Inject 三个注解的区别
  • 当你在应用 @Autowired 时,是否有呈现过 Field injection is not recommended 的正告?你晓得这是为什么吗?
  • Spring 依赖注入有哪几种形式?官网是怎么倡议应用的呢?

如果你对上述问题都理解,那我集体感觉你的开发教训应该是不错的👍。

上面咱们就顺次对上述问题进行解答,并且总结知识点。

@Autowired, @Resource, @Inject 三个注解的区别

Spring 反对应用@Autowired, @Resource, @Inject 三个注解进行依赖注入。上面来介绍一下这三个注解有什么区别。

@Autowired

@Autowired为 Spring 框架提供的注解,须要导入包org.springframework.beans.factory.annotation.Autowired

这里先给出一个示例代码,不便解说阐明:

public interface Svc {void sayHello();
}

@Service
public class SvcA implements Svc {

    @Override
    public void sayHello() {System.out.println("hello, this is service A");
    }

}

@Service
public class SvcB implements Svc {

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

}

@Service
public class SvcC implements Svc {

    @Override
    public void sayHello() {System.out.println("hello, this is service C");
    }
}

测试类:

@SpringBootTest
public class SimpleTest {

    @Autowired
    // @Qualifier("svcA")
    Svc svc;

    @Test
    void rc() {Assertions.assertNotNull(svc);
        svc.sayHello();}

}

拆卸程序:

1. 依照 type 在上下文中查找匹配的 bean,查找 type 为 Svc 的 bean

2. 如果有多个 bean,则依照 name 进行匹配

  • 如果有 @Qualifier 注解,则依照 @Qualifier 指定的 name 进行匹配,查找 name 为 svcA 的 bean
  • 如果没有,则依照变量名进行匹配,查找 name 为 svcA 的 bean

3. 匹配不到,则报错。(@Autowired(required=false),如果设置 requiredfalse(默认为true),则注入失败时不会抛出异样)

@Inject

在 Spring 的环境下,@Inject@Autowired 是雷同的,因为它们的依赖注入都是应用AutowiredAnnotationBeanPostProcessor 来解决的。

@Inject是 JSR-330 定义的标准 ,如果应用这种形式,切换到Guice 也是能够的。

Guice 是 google 开源的轻量级 DI 框架

如果硬要说两个的区别,首先 @Inject 是 Java EE 包里的,在 SE 环境须要独自引入。另一个区别在于 @Autowired 能够设置 required=false@Inject并没有这个属性。

@Resource

@Resource是 JSR-250 定义的注解。Spring 在 CommonAnnotationBeanPostProcessor实现了对 JSR-250 的注解的解决,其中就包含@Resource

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

拆卸程序:

  1. 如果同时指定了 nametype,则从 Spring 上下文中找到惟一匹配的 bean 进行拆卸,找不到则抛出异样。
  2. 如果指定了name,则从上下文中查找名称(id)匹配的 bean 进行拆卸,找不到则抛出异样。
  3. 如果指定了type,则从上下文中找到类型匹配的惟一 bean 进行拆卸,找不到或是找到多个,都会抛出异样。
  4. 如果既没有指定 name,又没有指定type,则默认依照byName 形式进行拆卸;如果没有匹配,依照byType h 进行拆卸。

IDEA 提醒 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 的形式进行依赖注入。对于必须的依赖,永远应用断言来确认。

比方如下代码:

@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 Boot 基础教程及实战示例:

https://github.com/javastacks…


首先咱们须要晓得,Spring 中有这么 3 种依赖注入的形式

  • 基于 field 注入(属性注入)
  • 基于 setter 注入
  • 基于 constructor 注入(结构器注入)

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 注入尽管简略,然而却会引发很多的问题。这些问题在我平时开发浏览我的项目代码的时候就常常遇见。

容易违反了繁多职责准则

应用这种基于 field 注入的形式,增加依赖是很简略的,就算你的类中有十几个依赖你可能都感觉没有什么问题,一般的开发者很可能会有意识地给一个类增加很多的依赖。

然而当应用结构器形式注入,到了某个特定的点,结构器中的参数变得太多以至于很显著地发现 something is wrong。领有太多的依赖通常意味着你的类要承当更多的责任,显著违反了繁多职责准则(SRP:Single responsibility principle)。

这个问题在我司的我的项目代码真的很常见。

依赖注入与容器自身耦合

依赖注入框架的核心思想之一就是受容器治理的类不应该去依赖容器所应用的依赖。换句话说,这个类应该是一个简略的 POJO(Plain Ordinary Java Object)可能被独自实例化并且你也能为它提供它所需的依赖。

这个问题具体能够体现在:

  • 你的类和依赖容器强耦合,不能在容器外应用
  • 你的类不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器能力实例化,这更像是集成测试

不能应用属性注入的形式构建不可变对象(final 润饰的变量)

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 注入的一个长处是,这个依赖能够很不便的被扭转或者从新注入

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿(2022 最新版)

2. 劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0