一、什么是DI?

DI(Dependency Injection,依赖注入)是 IoC(控制反转) 思想的最典型实现方式,核心目标只有一个:

让对象不再自己“找”依赖,而是由外部容器“送”依赖进来,从而彻底解耦。

一句话看懂DI:以创建狗这个实例举例

过去:自己 new

//当我们创建狗这个实例时,需要通过 new 关键实现对象的实例化
public class testDiBlog {public static void main(String[] args) {Dog dog=new Dog();}
}

现在:依赖注入(这里是构造注入)

public class testDiBlog {private Dog dog; //只声明,不创建,容器将实例送进来public testDiBlog(Dog dog) { //解耦this.dog = dog;}
}

spring 依赖注入的方式有三种,上述是其中一种(构造方法注入)其他两种分别是:属性注入、Setter方法注入,接下来让我们仔细的来看看这三种注入方式该如何进行书写。

二、依赖注入

 首先创建 Dog 类,创建 run 方法

@Getter @Setter
public class Dog {private String name;public void run(){System.out.println("running....");}
}

将 Dog 类交给Spring 进行管理(PS:@Bean(方法注解) 需要搭配 五大类注解(@Controller、@Service、@Component、@Repository、@Configuration)使用)

@Configuration
public class DogConfig {@Beanpublic Dog dog(){Dog dog=new Dog();dog.setName("旺财");return dog;}}

对上面代码的解释:

  1. Spring 管理的对象 = dog() 方法返回的那只 Dog 实例

  2. 类型 = Dog(返回值类型)

  3. Bean 名称 = dog(默认等于方法名,等价于 @Bean("dog")

2.1 属性注入

属性注入是使用 @Autowired 注解

@SpringBootTest
class SpringPrincipleApplicationTests {@Autowired //属性注入,使用注解Dog dog; //拿到 dog 实例@Testvoid DogTest(){dog.run(); //调用实例方法}
}

当 @Autowired 被注释掉时,此时的执行结果显示空指针异常

2.2 构造方法注入

使用构造方法注入Bean

//注入Bean
@Controller
public class TestDog2 {private Dog dog;public TestDog2(Dog dog) { this.dog = dog;}public void run(){dog.run();}}//启动spring(此段代码与上面的代码在idea中不是位于同一个类中,这里是为了方便观看写在一起)@SpringBootApplication
public class SpringPrincipleApplication {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringPrincipleApplication.class, args);TestDog2 bean = context.getBean(TestDog2.class);bean.run();}
}

执行结果如下:

此时 TestDog2 方法中只有一个构造方法,我们知道如果当类中没有构造方法时,编译器会默认调用一个无参的构造方法,既然现在是构造方法注入,如果现在我们将这个默认的无参构造函数加上,程序执行的结果还会是这样吗??

@Controller
public class TestDog2 {private Dog dog;public TestDog2() {System.out.println("无参构造方法...");//这里打印一下日志为了方便观察}public TestDog2(Dog dog) {System.out.println("有参构造方法...");this.dog = dog;}public void run(){dog.run();}}

结果执行如下:

可以看到这里执行了这个无参的构造方法,且报出了空指针异常,报空指针异常是因为没有执行下面的有参构造方法,dog 没有进行赋值,后面调用了 dog 的 run 方法,此时 dog 为null。此时的解决方法是在有参构造方法上添加 @Autowried 注解,当添加注解后会告诉 Spring  默认帮我执行带注解的构造方法。修改如下:

@Controller
public class TestDog2 {private Dog dog;public TestDog2() {System.out.println("无参构造方法...");}@Autowired //添加注解,指定默认的构造方法public TestDog2(Dog dog) {System.out.println("有参构造方法...");this.dog = dog;}public void run(){dog.run();}}

执行结果如下:

不知道有没有小伙伴注意到下图这里的 dog 参数,在代码中我们没有给它传递参数,这个 dog 是从哪儿来的呢?构造函数注入时,Spring 必须能把所有参数都解析成容器里的 Bean,去进行查找,如果找到了就进行相应的赋值,只要有一个参数匹配不到Bean(类型+名称)启动就会失败并抛出:

No qualifying bean of type 'xxx.xxx' available: expected at least 1 bean which qualifies as autowire candidate.

2.3 Setter方法注入

Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注解

@Controller
public class TestDog3 {private Dog dog;@Autowiredpublic void setDog(Dog dog) {this.dog = dog;}public void run(){System.out.println("这是TestDog3....");dog.run();}
}

执行结果如下:

去掉 @Autowired 后,执行结果如下:

三、优缺点分析

再进行优缺点分析之前,主播先提出一个问题,不知道小伙伴们是否还记得 final 关键字修饰得变量有什么特点?我们知道被 final 修饰得变量初始化要么再最初定义变量得时候就初始化,要么就是再构造器中被初始化。当我们回想起这一点后我们再来看这三种注入方式,不难发现,只有构造方法注入可以注入 final 修饰得变量,Setter 和属性注入不可以注入 final 修饰得变量。

3.1 原因分析

首先我们来看看 spring 创建对象的流程:① 分配空白内存 → ② 默认值(0/null) → ③ 构造代码块/构造器(final 唯一合法写入点) → ④ 对象头设置 → ⑤ 返回引用。这里一旦构造器返回,final 修饰的字段就进入了“只读”模式,后续任何赋值都会编译失败。

public class User {private final String name;public User(String name){ this.name = name; } // 合法public void setName(String name){ this.name = name; } // ❌ 编译错误
}

属性注入发生的时间段在返回引用之后,也就是流程⑤之后,spring 属性注入时机(源码级)

//java源码
AbstractAutowireCapableBeanFactory#populateBean
Field field = UserController.class.getDeclaredField("userService");
field.set(controllerInstance, userServiceImpl);   // 反射 putfield
  • JVM 校验:发现对 final 字段 执行 putfield → 直接抛

    java.lang.IllegalAccessError: Update to final field

3.2 spring三种注入的时间轴

注入方式触发时刻对象状态能否再写final
属性注入第⑤步:对象引用返回对象创建完成❌ 拒绝
Setter注入第⑤步:对象引用返回对象创建完成❌ 拒绝
构造器注入第③步:构造器里对象正在创建✅ 允许

3.3 时间轴再次对照

步骤时刻final可否写入spring属性注入是否在此
类加载类装载模板❌    ❌    不参与
new构造器类✅ 唯一机会❌    尚未开始
构造器返回对象已创建❌ 锁死❌    尚未开始
populateBean反射字段赋值❌ 抛错✅ 在这里发生 →失败

3.4 优缺点总结

通过上面的分析我们可以总结出三种注入方式的优缺点

方式优点缺点
属性注入
简洁,使⽤⽅便
只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指
针异常)
不能注⼊一个Final修饰的属性
构造方法注入
可以注⼊final修饰的属性
注⼊的对象不会被修改
依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法 是在类加载阶段就会执⾏的⽅法.
通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的
注⼊多个对象时, 代码会⽐较繁琐
Setter注入
⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊
不能注⼊⼀个Final修饰的属性
注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤,就有被修改的风险

四、@Autowired存在的问题

当同一个类型的对象有多个的时候,此时又会发生什么状况呢??

@Component
public class TestUser {@Beanpublic User user1(){User user=new User();user.setName("图图");return user;  //对象1}@Beanpublic User user2(){User user=new User();user.setName("小美");return user;  //对象2}
}

错误提示:这里不只有一个User Bean对象,当同⼀类型存在多个bean时, 使⽤@Autowired会存在问题。

如何解决上述问题呢?Spring提供了以下⼏种解决⽅案: • @Primary • @Qualifier • @Resource

4.1 解决方法之 @Primary

使⽤@Primary注解:当存在多个相同类型的Bean注⼊时,加上@Primary注解,来确定默认的实现.

@Component
public class TestUser {@Primary  //指定该 Bean为默认的 Bean@Beanpublic User user1(){User user=new User();user.setName("图图");return user;}@Beanpublic User user2(){User user=new User();user.setName("小美");return user;}
}
@Controller
public class UserController {@Autowiredprivate User user; //注入成功没有报错public void desc(){user.desc();}
}

注意:@Qualifier注解不能单独使⽤,必须配合@Autowired使⽤

4.2 解决方法之 @Qualifier

使⽤ @Qualifier 注解:指定当前要注⼊的 bean 对象。 在 @Qualifier 的 value 属性中,指定注⼊的 bean 的名称。 
@Controller
public class UserController {@Qualifier("user1")  //添加注入指定Bean@Autowiredprivate User user;public void desc(){user.desc();}
}

4.3 解决方法之 @Resource

使⽤@Resource注解:是按照bean的名称进⾏注⼊。通过name属性指定要注⼊的bean的名称。

@Controller
public class UserController {@Resource(name= "user1")private User user;public void desc(){user.desc();}
}

五、@Resource 和 @Autowired 的区别

1. @Autowired 是spring框架提供的注解,⽽@Resource是JDK提供的注解
2. @Autowired 默认是按照类型注⼊,当同类型有多个实例时,也会根据名称去进行匹配,⽽@Resource是按照名称注⼊,按名称肯定也会需要类型是一致的.
3. 相⽐于 @Autowired 来说,@Resource ⽀持更多的参数设置,例如 name 设置,根据名称获取 Bean

六、@Autowired装配顺序


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98114.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98114.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/98114.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PPT中如何将设置的文本框边距设为默认

通常,在PPT中插入的文本框边距比较窄,线条和填充都为空,我们可以根据自己的需要调整文本框的边距,以及填充颜色、线条颜色和样式等,并且把这个设置为默认的文本框,然后就可以直接插入相同边距和样式的文本框…

疯狂星期四文案网第61天运营日记

网站运营第61天,点击观站: 疯狂星期四 crazy-thursday.com 全网最全的疯狂星期四文案网站 运营报告 今日访问量 今日搜索引擎收录情况 收录好像便正常了,准备加快发布频率了

开源容器管理平台Rancher

Rancher 是一个开源的 容器管理平台,用于简化 Kubernetes 和 Docker 的部署、运维和安全管理。它提供了一套用户友好的工具,帮助开发者和运维团队在企业环境中高效地管理容器化应用。核心功能Kubernetes 管理 支持多集群管理(本地、云、边缘等…

AI在目前会议直播系统中应用

AI在目前会议直播系统中有多种使用场景,以下是一些常见的例子: 会议内容实时处理 实时转写与翻译:借助AI语音识别算法,会议直播系统可实现语音的实时转写,支持多种语言和方言,转写准确率达98%以上。同时,部分系统还配备实时翻译功能,将发言语音实时翻译成多种语言字幕,…

网络安全A模块专项练习任务十解析

任务十:Linux操作系统安全配置-3任务环境说明: (Linux)系统:用户名root,密码1234561.设置账户密码有效期,密码最大有效期为30,可修改密码最小天数为5,密码长度为6,密码失效前4天通知…

WorkMagic-AI驱动的营销SaaS服务平台

本文转载自:WorkMagic-AI驱动的营销SaaS服务平台 - Hello123工具导航 ** 一、🤖 WorkMagic:跨境电商的 AI 营销自动化神器 WorkMagic 是一家专注于为跨境电商提供AI 驱动营销自动化解决方案的 SaaS 平台,成立于 2023 年。它通过…

Java 线程重点 面试笔记(线程状态,安全停止线程..)

包括线程状态、Thread.yield()、Thread.join()、线程安全停止、标志位、中断等,都是线程这块秋招的重点。1. 线程状态(Thread.State)Java 中线程有 6 种状态:状态含义进入条件NEW新建状态Thread t new Thread(...);RUNNABLE可运行…

Zigbee:Polling 终端设备的睡眠机制和功耗

一、Zigbee 设备类型与功耗基础 首先,Zigbee网络中的设备角色决定了其功耗特性。Zigbee定义了三种逻辑设备类型: 协调器 (Coordinator)​​:网络的中心,必须始终供电,不能睡眠。功耗最高。 路由器 (Router)​​:负责中继数据,扩展网络范围。通常也需持续供电,以保持网…

Python迭代协议完全指南:从基础到高并发系统实现

引言:迭代协议的核心价值在Python编程中,迭代协议是构建高效、灵活数据结构的基石。根据2024年Python开发者调查报告:92%的高级数据结构依赖迭代协议85%的数据处理框架基于迭代协议构建78%的并发系统使用自定义迭代器65%的内存优化方案通过迭…

vsan高可用:确保可访问性、全部数据迁移,两种类型权衡

目录1.如果我3台机器,其中有1台机器突然故障,那么走的是保证可用,还是全量数据迁移?这个怎么算?一、先明确:故障场景 vs 维护场景的核心差异二、3台主机故障时,vSAN的具体处理逻辑(为…

51单片机1(单片机基础,LED,数码管)

1.嵌入式嵌入式(Embedded)指的是一种专用计算机系统,它被"嵌入"或内建到一个更大的设备、产品或系统中,作为其核心控制部分,专门用于执行特定的任务或功能。通俗来讲就是以应用为中心,以计算机技…

Aerobits-用于 sUAS 和 UTM/U-Space 的微型 ADS-B 技术(收发器/接收器)和无人机跟踪应答器

Aerobits-用于 sUAS 和 UTM/U-Space 的微型 ADS-B 技术(收发器/接收器)和无人机跟踪应答器Aerobits 是一家专门为无人机 (UAV) 和无人驾驶飞机开发微型应答器和航空电子系统的公司。我们的硬件和软件解决方案基于专利技术,采用极低 SWaP 封装…

Spring Security资源服务器在高并发场景下的认证性能优化实践指南

Spring Security资源服务器在高并发场景下的认证性能优化实践指南 摘要:本文从原理与实践两个层面,深入解析Spring Security资源服务器在高并发场景下的认证性能优化策略,通过关键源码解读与实际示例,帮助开发者有效提升系统吞吐与…

SQL Server事务隔离级别

SQL Server 提供了多个事务隔离级别,用于控制并发事务如何访问和修改数据时的可见性、锁定行为以及可能遇到的并发问题(如脏读、不可重复读、幻读)。这些级别在数据一致性、并发性能和锁定开销之间进行权衡。 以下是 SQL Server 支持的主要隔…

DeepSeek R1大模型微调实战-llama-factory的安装与使用

文章目录概要1.安装必要的环境2.安装 PyTorch3.安装 Transformers 和 Datasets4.克隆 LLaMA Factory 仓库和安装LLaMA Factory5.准备数据和模型配置6.运行 LLaMA Factory7.监控和调整8.后续步骤概要 LLaMA Factory 是一个简单易用且高效的大型语言模型训练与微调平台。通过它&…

IDE mac M芯片安装报错:如何解决“InsCode.app 已损坏”,无法打开

IDE mac M芯片安装报错:如何解决“InsCode.app 已损坏”,无法打开 摘要 在 macOS 上安装并运行 InsCode IDE 时,不少开发者会遇到这样的报错: “InsCode.app 已损坏,无法打开。您应该将它移到废纸篓。” 这种情况在 …

EasyExcel:阿里开源的高效 Excel 处理工具,轻松解决 POI 内存溢出问题

在日常开发中,Excel 文件的导入导出是非常常见的需求。无论是数据批量导入、报表生成还是数据备份,我们都离不开对 Excel 的操作。但传统的 POI 框架在处理大数据量 Excel 时,常常会遇到内存溢出的问题,让开发者头疼不已。 今天给…

软件启动时加配置文件 vs 不加配置文件

一、基本概念不加配置文件启动直接执行启动命令,使用软件自带的默认参数。方便、快速,适合测试环境。缺点:灵活性差、配置不可控、不安全。redis-server zookeeper-server-start.sh kafka-server-start.sh指定配置文件启动启动时加载外部配置…

[ubuntu][C++]onnxruntime安装cpu版本后测试代码

下载官方预编译包后,怎么用呢。可以参考这个源码跑测试环境:ubuntu22.04onnxruntime1.18.0测试代码:CMakeLists.txtcmake_minimum_required(VERSION 3.12) project(onnx_test)# 设置C标准 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD…

栈:有效的括号

题目描述:给定一个只包含‘[’,{,(,),},]的字符串,判断该字符串是否括号有效。 括号有效的要求是: 每个左括号都有对应的右括号。每个右括号都有对应的左括号。左括号必须以正确的顺序闭合。 示例 1: 输入:s "…