Spring为什么建议使用构造器来注入?

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进行匹配

  3. 如果有@Qualifier注解,则按照@Qualifier指定的name进行匹配查找name为svcA的bean

  4. 如果没有,则按照变量名进行匹配查找name为svc的bean

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

@Inject

=======

在Spring 的环境下,@Inject和@Autowired 是相同的 ,因为它们的依赖注入都是使用AutowiredAnnotationBeanPostProcessor来处理的。

Spring为什么建议使用构造器来注入?

@Inject是 JSR-330 定义的规范 ,如果使用这种方式,切换到Guice也是可以的。

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

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

@Resource

=========

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

Spring为什么建议使用构造器来注入?

@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进行装配。

另外,如果您正在学习Spring Cloud,推荐一个连载多年还在继续更新的免费教程:

https://blog.didispace.com/spring-cloud-learning/

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”.

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种依赖注入的方式 :

  • 基于 field 注入(属性注入)

  • 基于 setter 注入

  • 基于 constructor 注入(构造器注入)

1. 基于 field 注入

===============

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

@Autowired

private Svc svc;

2. 基于 setter 方法注入

==================

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

private Helper helper;@Autowiredpublic 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.

简单来说,就是

Docker步步实践

目录文档:

①Docker简介

②基本概念

③安装Docker

④使用镜像:

⑤操作容器:

⑥访问仓库:

⑦数据管理:

⑧使用网络:

⑨高级网络配置:

⑩安全:

⑪底层实现:

⑫其他项目:

[外链图片转存中…(img-cSfxWRVE-1721157718786)]

⑥访问仓库:

[外链图片转存中…(img-kNBFEBjr-1721157718786)]

⑦数据管理:

[外链图片转存中…(img-oPO2X09h-1721157718787)]

⑧使用网络:

[外链图片转存中…(img-a3SLZFID-1721157718787)]

⑨高级网络配置:

[外链图片转存中…(img-dfuAqXN9-1721157718788)]

⑩安全:

[外链图片转存中…(img-wV55XFaw-1721157718788)]

⑪底层实现:

[外链图片转存中…(img-jrlUJqFK-1721157718789)]

⑫其他项目:

[外链图片转存中…(img-c2uaahuC-1721157718789)]

  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值