Spring DI 入门详解:三种依赖注入方式 +@Autowired 歧义问题解决
目录
一、什么是依赖注入(DI)?
DI(Dependency Injection)直译为“依赖注入”,依赖注⼊是⼀个过程,是指IoC容器在创建Bean时,去提供运⾏时所依赖的资源,⽽资源指的就是对象
说定义可能过于抽象这边也举个例子,比如说你想喝奶茶了:
- 没有 DI:你得自己买原料、煮茶、做奶盖(自己创建依赖)
- 有 DI:你直接点外卖,奶茶店(IOC容器)做好奶茶(依赖)送到你手上(注入)
简单来说,就是把对象取出来放到某个类的属性中
DI的作用和价值
上述买奶茶的例子中,不需要你自己费心去制作奶茶,直接就有奶茶店给你制造好了,而相对应的意思就是,DI 通过让组件的依赖由外部提供,而非组件自身创建,实现组件与依赖解耦、提升代码可测试性并简化依赖维护
在⼀些⽂章中,依赖注⼊也被称之为"对象注⼊",“属性装配”,具体含义需要结合⽂章的上下⽂来理解
二、DI 的 3 种核心实现方式
关于依赖注⼊,Spring也给我们提供了三种⽅式:
- 属性注⼊(FieldInjection)
- 构造⽅法注⼊(ConstructorInjection)
- Setter 注⼊(SetterInjection)
1. 属性注⼊(FieldInjection)
直接在类的成员变量(属性)上标注注入注解@Autowired,由Spring框架自动为属性赋值
本章的代码演示我们都按照实际开发中的模式,将Service类注⼊到Controller类中
Service 类的实现代码如下:

Controller 类的实现代码如下:

获取Controller中的print⽅法:
测试结果:
去掉@Autowired后
你去掉了@Autowired,Spring就不会帮你把UserService的实例“填”到HelloController的userService属性里了,所以这个属性还是默认的null,调用null的print()方法就会报错
2. 构造⽅法注⼊(ConstructorInjection)
通过类的构造方法,将依赖作为参数传递给当前类,在对象创建的同时完成依赖赋值
Service 类的实现代码如下:
Controller 类的实现代码如下:
获取Controller中的print⽅法:
测试结果:
构造方法注入的其他情况
如果类只有⼀个构造⽅法,那么@Autowired 注解可以省略;如果类中有多个构造⽅法,那么需要添加上@Autowired 来明确指定到底使⽤哪个构造⽅法
情况代码演示
Service 类的实现代码如下:

Controller 类的实现代码如下:
测试结果:
3. Setter 注⼊(SetterInjection)
通过类的 setter 方法(即属性的 “设置方法”)传入依赖,Setter 注⼊和属性的Setter⽅法实现类似,只不过在设置set⽅法的时候需要加上@Autowired 注
解
Service 类的实现代码如下:
Controller 类的实现代码如下:
测试结果:
三、三种注⼊优缺点分析(了解即可)
- 属性注入
- 优点:简洁,使⽤⽅便
- 缺点:
- 只能⽤于IoC容器,如果是⾮IoC容器不可⽤,并且只有在使⽤的时候才会出现NPE(空指针异常)
- 不能注⼊⼀个Final修饰的属性
- 构造方法注入(Spring4.X推荐)
- 优点:
- 可以注⼊final修饰的属性
- 注⼊的对象不会被修改
- 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅法是在类加载阶段就会执⾏的⽅法
- 通⽤性好,构造⽅法是JDK⽀持的,所以更换任何框架,他都是适⽤的
- 缺点: 注⼊多个对象时,代码会⽐较繁琐
- 优点:
- setter注入(Spring3.X推荐)
- 优点:⽅便在类实例之后,重新对该对象进⾏配置或者注⼊
- 缺点:
- 不能注⼊⼀个Final修饰的属性
- 注⼊对象可能会被改变,因为setter⽅法可能会被多次调⽤,就有被修改的⻛险
四、@Autowired存在问题
因为@Autowired是根据类型获取对象,当同⼀类型存在多个bean时,使用@Autowired会存在问题
代码演示
Controller 类的实现代码如下:
这里IDEA已经提醒出现错误了
Component 类的实现代码如下:
Student 类的实现代码如下:
测试结果:
解决@Autowired会存在问题的方法
上述报错的原因是,⾮唯⼀的Bean对象,为了解决这个问题,Spring提供了以下⼏三种解决⽅案:@Primary,@Qualifier,@Resource
1. @Primary
使⽤@Primary注解:当存在多个相同类型的Bean注⼊时,加上@Primary注解,来确定默认的实现
测试结果:
2. @Qualifier
按 Bean 名称精准锁定目标 Bean,但必须与 @Autowired搭配使用,可弥补 @Autowired 先按类型匹配的模糊性,实现类型筛选 + 名称精准匹配的注入逻辑


测试运行结果:
优先级
@Qualifier 优先高于@Primary,直接覆盖 @Primary 的默认选择
测试一下同时使用@Qualifier和@Primary

测试运行结果:
从上述测试结果可证明即使同时使用@Qualifier和@Primary,,@Qualifier优先级高于@Primary,直接将@Primary覆盖
3. @Resource
@Resource 是 JDK 原生的依赖注入注解,实现 Bean 的注入,其注入逻辑是也是先按名称匹配,再按类型匹配

测试运行结果:
注意,@Qualifier需要搭配@Autowired一起使用,@Resource不需要,且需要通过name属性指定要注⼊的bean的名称
五 、@Autowird与@Resource的区别
- @Autowired是spring框架提供的注解,⽽@Resource是JDK提供的注解
- @Autowired默认是按照类型注⼊,⽽@Resource是按照名称注⼊.相⽐于@Autowired 来说,@Resource ⽀持更多的参数设置,例如name设置,根据名称获取Bean
六 、最后说一下
结合上篇的IOC文章,我们可以对IOC是 Spring 的核心思想,DI是其具体实现手段这句话应该会有了更深的理解,也就是容器接管 Bean 的创建与生命周期,DI 负责依赖注入,二者共同实现组件解耦.
至此spring IOC和DI内容就告一段落

更多推荐

所有评论(0)