当前位置: 首页 > news >正文

深入拆解Spring思想:DI(依赖注入)

  • 在简单了解IoC与DI中我们已经了解了DI的基本操作,接下来我们来详解DI。(IoC详解请看这里)
  • 我们已经知道DI是“你给我,我不用自己创建”的原则。现在我们来看看Spring是如何实现“给”这个动作的,也就是依赖注入的几种方式。

Spring主要提供了三种注入方式,都是通过 @Autowired 注解配合完成的:

  1. 属性注入 (Field Injection):直接在字段上使用 @Autowired
  2. 构造方法注入 (Constructor Injection):在类的构造方法上使用 @Autowired
  3. Setter 注入 (Setter Injection):在Setter方法上使用 @Autowired

三种注入方式

属性注入

方式:直接在需要注入的属性(字段)上加上 @Autowired

代码示例
首先,我们有一个 UserService Bean:

@Service // 告诉Spring:我是UserService,请你管理我
public class UserService {public void sayHi() {System.out.println("Hi, UserService");}
}

然后,在 UserController 中注入 UserService

@Controller
public class UserController {@Autowired // 告诉Spring:我需要一个UserService,请你直接注入到这个属性里private UserService userService;public void sayHi() {System.out.println("Hi, UserController...");userService.sayHi(); // 使用注入的UserService}
}

获取并使用

@SpringBootApplication
public class SpringTocDemoApplication {public static void main(String[] args) {ApplicationContext context = SpringApplication.run(SpringTocDemoApplication.class, args);UserController userController = context.getBean(UserController.class);userController.sayHi();}
}

运行结果

Hi, UserController...
Hi, UserService

解释:Spring成功地将 UserService 实例注入到了 UserControlleruserService 属性中。

注意:如果你尝试在没有Spring容器的情况下直接调用 UserControllersayHi() 方法,userService 会是 null,导致空指针异常(NPE),因为Spring没有机会注入它。

构造方法注入

方式:在类的构造方法上加上 @Autowired。Spring会在创建这个类实例时,通过调用这个构造方法来注入依赖。

代码示例

@Controller
public class UserController2 {private UserService userService;@Autowired // 告诉Spring:请通过这个构造方法注入UserServicepublic UserController2(UserService userService) {this.userService = userService;}public void sayHi() {System.out.println("Hi, UserController2...");userService.sayHi();}
}

注意

  • 如果一个类只有一个构造方法,那么即使不加 @Autowired,Spring也会自动使用它进行注入(Spring 4.3+)。

[!TIP] 此时默认的无参构造方法不生效了,最好的习惯是补上默认的无参构造方法

  1. Spring默认的是使用无参的构造方法(如有多个构造方法)
  • 这样会导致之后在使用到userService的地方有空指针报错异常(可以通过打印日志的方法来确定具体使用哪一个方法)
  • 所以有多个构造方法的情况下,需要在需要的构造方法上加上 @Autowired注解表示指定使用哪一个

Setter 注入

方式:在Setter方法上加上 @Autowired。Spring会先创建对象,然后调用对应的Setter方法来注入依赖。

代码示例

@Controller
public class UserController3 {private UserService userService;@Autowired // 告诉Spring:请通过这个Setter方法注入UserServicepublic void setUserService(UserService userService) {this.userService = userService;}public void sayHi() {System.out.println("Hi, UserController3...");userService.sayHi();}
}

[!QUESTION] 如果 setUserService 方法上没有 @Autowired,Spring还能注入吗?
不能。Spring需要 @Autowired 来知道这个方法是用来注入依赖的。

三种注入方式优缺点分析

#面经

方式优点缺点
属性注入最简洁,使用方便。1. 违反单一职责原则(SRP)。
2. 难以测试(只能用于IoC容器,并且在使用的时候才会出现空指针异常)。
3. 无法声明为 final
构造方法注入1. 依赖明确,强制依赖。
2. 可以声明为 final(推荐)。
3. 保证对象完全初始化。因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法.
4. 更好的可测试性。
5. 通用性好,因为构造方法是JDK支持的,所以换任何框架都是使用的。
1. 依赖过多时,构造方法参数列表会很长。
Setter 注入1. 依赖可选(不强制)。
2. 可以在对象创建后进行配置。
1. 无法声明为 final
2. 可能会被多次调用,存在风险。

推荐:在大多数情况下,构造方法注入是最佳实践。它确保了依赖的不可变性,并使对象在创建时就处于完全可用的状态。

@Autowired 存在问题?

问题:如果Spring容器中有多个相同类型的Bean,@Autowired 会怎么做?

代码示例
我们定义了两个 User 类型的Bean,名字分别是 user1user2

@Configuration
public class BeanConfig {@Bean("user1")public User user1() { /* ... */ }@Bean("user2")public User user2() { /* ... */ }
}

现在,我们在 UserController 中尝试注入 User

@Controller
public class UserController {@Autowiredprivate UserService userService; // 假设只有一个UserService@Autowiredprivate User user; // 尝试注入Userpublic void sayHi() {System.out.println("Hi, UserController...");userService.sayHi();System.out.println(user); // 打印注入的User}
}

运行结果

// 报错:NoUniqueBeanDefinitionException: No qualifying bean of type 'User' available: expected single matching bean but found 2

解释:Spring发现有两个 User 类型的Bean(user1user2),它不知道该注入哪一个,所以报错了。

如何解决“多个相同类型Bean”的问题?

Spring提供了几种方式来解决歧义:

  1. @Primary:优先注入。
  2. @Qualifier:指定Bean的名称。
    • @Qualifier 优先级比 @Primary
  3. @Resource:按名称注入(JDK标准)。
@Primary

作用:当有多个相同类型的Bean时,给其中一个加上 @Primary,表示它是首选的。

代码示例

@Configuration
public class BeanConfig {@Bean("user1")@Primary // 告诉Spring:当需要User类型时,优先注入我public User user1() { /* ... */ }@Bean("user2")public User user2() { /* ... */ }
}

现在,UserController 再次注入 User

@Controller
public class UserController {@Autowiredprivate User user; // 会自动注入user1// ...
}

解释:当Spring需要注入 User 类型时,它会优先选择带有 @Primaryuser1

@Qualifier

作用:明确指定要注入哪个Bean的名称。

代码示例

@Controller
public class UserController {@Autowired@Qualifier("user2") // 告诉Spring:我要注入名为“user2”的User Beanprivate User user;// ...
}

解释@Qualifier 必须和 @Autowired 一起使用。它告诉Spring,即使有多个 User 类型的Bean,我只想要那个名字是 user2 的。

[!IMPORTANT] @Qualifier注解不能单独使⽤,必须配合@Autowired使⽤

@Resource

作用@Resource 是JDK提供的注解,它默认是按名称进行注入。如果找不到同名的Bean,再尝试按类型注入。

代码示例

@Controller
public class UserController {@Resource(name = "user1") // 告诉Spring:我要注入名为“user1”的Beanprivate User user;// ...
}

[[四种不同情况反应构造Spring框架中Bean的依赖注入和自动装配的不同规则]]

#面经
@Autowired vs @Resource 的区别:

  1. @Autowired是spring提供的注解,@Resource是JDK提供的注解
  • @Autowired:Spring特有的,默认按类型注入。如果类型有多个,再尝试按名称匹配。
  • @Resource:JDK标准,默认按名称注入。如果名称找不到,再尝试按类型匹配,并且其支持更多的参数设置,如name:根据名称获取Bean

Autowired装配顺序

在这里插入图片描述

http://www.dtcms.com/a/273698.html

相关文章:

  • Python-正则表达式-信息提取-滑动窗口-数据分发-文件加载及分析器-浏览器分析-学习笔记
  • 榫卯企业云平台自服务中心模块(SSC)关键能力解读
  • 数据库报错:Column ‘xxx‘ in field list is ambiguous
  • 2025年体育科学与健康大数据国际会议(ICSSHBD 2025)
  • 在 GitHub 上创建私有仓库
  • 如何在 Windows 10 上安装设置 Apache Kafka
  • hive/spark sql中unix_timestamp 函数的坑以及时间戳相关的转换
  • AI技术正以前所未有的速度重塑职业生态与行业格局,尤其在自动化测试领域,AI驱动的测试框架通过智能化、低代码化重构传统测试流程。
  • PySpark中python环境打包和JAR包依赖
  • spark3 streaming 读kafka写es
  • Google Benchmark 介绍和使用指南
  • 流批一体的“奥卡姆剃刀”:Apache Cloudberry 增量物化视图应用解析
  • CReFT-CAD 笔记 带标注工程图dxf,png数据集
  • 【EGSR2025】材质+扩散模型+神经网络相关论文整理随笔(四)
  • Jenkins 项目类型及配置项
  • FPGA实现SDI转LVDS视频发送,基于GTP+OSERDES2原语架构,提供工程源码和技术支持
  • 资源分享-FPS, 矩阵, 骨骼, 绘制, 自瞄, U3D, UE4逆向辅助实战视频教程
  • 飞算 JavaAI 深度体验:开启 Java 开发智能化新纪元
  • 【拓扑空间】示例及详解4
  • python的社区残障人士服务系统
  • 了解环网式 CAN 转光纤中继器
  • BPE(Byte Pair Encoding)分词算法
  • leetcode-hot100(283.移动零)
  • 政安晨【零基础玩转开源AI项目】ACE-Step —— 迈向音乐生成基础模型的重要一步:AI自动谱曲与自动演唱的免费开源框架部署实践
  • RLHF:人类反馈强化学习 | 对齐AI与人类价值观的核心引擎
  • python实现DoIP基本通信(收发报文)
  • 第十二章:网络编程
  • Typescript -字面量类型
  • Linux的基础I/O
  • 买小屏幕的时候注意避坑