SpringIoC & DI (4)DI详解(三种注入方式)
文章目录
- [SpringIoC & DI (4)DI详解(三种注入方式)](#SpringIoC & DI (4)DI详解(三种注入方式))
- [1. 什么是 DI?](#1. 什么是 DI?)
- [2. DI 的三种方式](#2. DI 的三种方式)
-
- [2.1 第一种:属性注入(@Autowired)](#2.1 第一种:属性注入(@Autowired))
-
- [去掉 @Autowired:](#去掉 @Autowired:)
- [去掉 @Service:](#去掉 @Service:)
- [注意:一个 @Autowired 只能注入一个对象](#注意:一个 @Autowired 只能注入一个对象)
- [2.2 第二种:构造方法注入](#2.2 第二种:构造方法注入)
-
- [2.2.1 第一种情况:只有一个构造方法:](#2.2.1 第一种情况:只有一个构造方法:)
- [2.2.2 第二种情况:加入无参的构造方法:(两个构造方法)(空指针异常)](#2.2.2 第二种情况:加入无参的构造方法:(两个构造方法)(空指针异常))
- [2.2.3 第三种情况:加入另一个属性,再加入一个构造方法(三个构造方法)(空指针异常)](#2.2.3 第三种情况:加入另一个属性,再加入一个构造方法(三个构造方法)(空指针异常))
- [2.2.4 第四种情况:不使用无参的构造方法,使用另外两个:(两个构造方法)(Bean构造失败异常)](#2.2.4 第四种情况:不使用无参的构造方法,使用另外两个:(两个构造方法)(Bean构造失败异常))
- [2.2.5 原因分析(重点):](#2.2.5 原因分析(重点):)
- [2.2.6 手动指定采用的构造方法:](#2.2.6 手动指定采用的构造方法:)
- [2.2.7 构造方法注入总结:](#2.2.7 构造方法注入总结:)
- [2.3 第三种:Setter 方法注入](#2.3 第三种:Setter 方法注入)
- [2.4 总结:](#2.4 总结:)
- [3. 三种注入方式优缺点分析(一道面试题,频率不高):](#3. 三种注入方式优缺点分析(一道面试题,频率不高):)
- [4. 总结:](#4. 总结:)
观前提醒
这篇博客的代码,是比较简单的。
你自己可以创建类,使用注解,调用对象,就能够看见效果。
或者,你获取我的源码:
https://gitee.com/mrbgvhbhjv/java-ee-course/tree/master/后端代码/SpringIoC_demo_20251025
1. 什么是 DI?
DI: Dependency Injection,叫做 依赖注入。
依赖注入是一个过程,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象。
传统开发模式中需要自己通过 new 创建对象, 现在不需要再进行创建, 把创建对象的任务交给容器, 程序中只需要依赖注入 (Dependency Injection,DI)就可以了。
什么叫做依赖?
依赖,就是完成这项工作的必须品,是完成这个工作不可或缺的东西。
IoC ,是存储 Bean 的方式。
DI ,是获取 Bean 的方式。
2. DI 的三种方式
DI(依赖注入),有三种方式:
- 属性注入(Field Injection)
- 构造方法注入(Constructor Injection)
- Setter 注入(Setter Injection)
2.1 第一种:属性注入(@Autowired)
@Autowired的作用:从 Spring 容器当中,根据类型获取对象,并赋值。

代码:
java
package org.example.springioc_demo_20251024.controller;
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
// 属性注入
@Autowired
private UserService userService;
public void print() {
System.out.println("do Controller");
userService.print();
}
}
-----------------------------------------------------------
package org.example.springioc_demo_20251024.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void print(){
System.out.println("do Service");
}
}
-----------------------------------------------------------
package org.example.springioc_demo_20251024;
import org.example.springioc_demo_20251024.controller.HelloController;
import org.example.springioc_demo_20251024.controller.UserController;
import org.example.springioc_demo_20251024.model.Student;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class SpringIoCDemo20251024Application {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(SpringIoCDemo20251024Application.class, args);
UserController bean1 = context.getBean(UserController.class);
bean1.print();
}
}
如果注入生效,控制器,会将 userService 这个对象的 print()方法 一起执行。
效果:

去掉 @Autowired:

去掉 @Service:
想要从 IoC容器 中获取 UserService 对象,首先容器中得有这个对象,如果我们去掉 @Service 注解,没有将 UserService类 的对象,交给 Spring 进行管理 :

注意:一个 @Autowired 只能注入一个对象



2.2 第二种:构造方法注入
首先,我们的 UserServicer类 交给了Spring进行管理,Spring就会构造这个类的 对象。
构造类的对象,就需要使用到 构造方法。
2.2.1 第一种情况:只有一个构造方法:
代码:
java
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
// 构造注入:
public UserController(UserService userService) {
this.userService = userService;
}
public void print() {
System.out.println("do Controller");
userService.print();
}
}
运行结果:

2.2.2 第二种情况:加入无参的构造方法:(两个构造方法)(空指针异常)
当我们手动添加了构造方法,JDK就不会给我们提供默认的构造方法了,我们手动加上:
代码:
java
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
public UserController() {
}
// 构造注入:
public UserController(UserService userService) {
this.userService = userService;
}
public void print() {
System.out.println("do Controller");
userService.print();
}
}

2.2.3 第三种情况:加入另一个属性,再加入一个构造方法(三个构造方法)(空指针异常)
代码:
java
import org.example.springioc_demo_20251024.component.StudentComponent;
import org.example.springioc_demo_20251024.component.UserComponent;
import org.example.springioc_demo_20251024.model.Student;
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
private UserComponent userComponent;
public UserController() {
}
// 构造注入:
public UserController(UserService userService) {
this.userService = userService;
}
public UserController(UserService userService, UserComponent userComponent) {
this.userService = userService;
this.userComponent = userComponent;
}
public void print() {
System.out.println("do Controller");
userService.print();
userComponent.print();
}
}
运行结果:

2.2.4 第四种情况:不使用无参的构造方法,使用另外两个:(两个构造方法)(Bean构造失败异常)
代码:
java
import org.example.springioc_demo_20251024.component.StudentComponent;
import org.example.springioc_demo_20251024.component.UserComponent;
import org.example.springioc_demo_20251024.model.Student;
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
private UserComponent userComponent;
// public UserController() {
// }
// 构造注入:
public UserController(UserService userService) {
this.userService = userService;
}
public UserController(UserService userService, UserComponent userComponent) {
this.userService = userService;
this.userComponent = userComponent;
}
public void print() {
System.out.println("do Controller");
userService.print();
userComponent.print();
}
}
运行结果:

2.2.5 原因分析(重点):
直接说结论:
- Spring管理对象,构造 Bean(对象),是依靠反射
- Spring管理对象,构造 Bean(对象)会采用默认的构造方法:无参的构造方法
针对上述情况分析:
第一种情况 :只有一个构造方法 ,Spring没得选,就使用那个有参的。
第二种情况 :有 两 个构造方法 (包含无参构造方法)的时候,Spring 就会采用 无参的构造方法 ,userService 没有被赋值,为 NULL
第三种情况 :有 三 个构造方法 (包含无参构造方法)的时候,Spring 就会采用 无参的构造方法 ,userService 没有被赋值,为 NULL
第四种情况 :有多个构造方法,但是没有 无参的构造方法 的时候,Spring 不知道使用哪一个,就会构造 Bean 失败!!!
我们构造方法注入,就是当有多个构造方法,但是没有 无参的构造方法 的时候,添加@Autowired,手动设置,Spring构造 UserServicer类 对象的时候,使用哪种构造方法。
2.2.6 手动指定采用的构造方法:
我们需要在我们想要 Spring构建对象 采用的构造方法前,添加 @Autowired注解,就能够指定 Spring构建对象 采用的构造方法:

代码:
java
import org.example.springioc_demo_20251024.component.UserComponent;
import org.example.springioc_demo_20251024.model.Student;
import org.example.springioc_demo_20251024.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
private UserComponent userComponent;
public UserController() {
}
// 构造注入:
public UserController(UserService userService) {
this.userService = userService;
}
@Autowired
// 指定使用这个构造方法
public UserController(UserService userService, UserComponent userComponent) {
this.userService = userService;
this.userComponent = userComponent;
}
public void print() {
System.out.println("do Controller");
userService.print();
userComponent.print();
}
}
运行结果:

2.2.7 构造方法注入总结:
-
如果没有提供构造方法,Spring 采用无参的构造方法,构建对象
-
如果只有一个构造方法(不是无参的),@Autowired可以省略,Spring 只能采用这个
-
如果存在多个构造方法:
3.1 包含无参 的构造方法:使用 无参 的构造方法,对象不会赋值(可能引发 空指针 异常)
3.2 不包含无参 的构造方法:Bean(对象)构造失败,丢出 BeanCreationException异常
-
通过 @Autowired 指定 Spring构建对象时,默认的构造方法
2.3 第三种:Setter 方法注入
这种方法比较简单,和属性注入差不多,想注入哪个对象,就在哪个对象的set方法上,添加@Autowired即可。
代码:
java
import org.example.springioc_demo_20251025.component.UserComponent;
import org.example.springioc_demo_20251025.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
// set方法注入
private UserService userService;
private UserComponent userComponent;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
@Autowired
public void setUserComponent(UserComponent userComponent) {
this.userComponent = userComponent;
}
public void print(){
System.out.println("UserController");
// System.out.println(userService.getNum());
userService.print();
userComponent.print();
}
}
运行结果:

2.4 总结:
这三种注入方式,程序员更推荐使用第一种,方便简单。
这三种注入方式,当然也有区别。
3. 三种注入方式优缺点分析(一道面试题,频率不高):
- 属性注入
- 优点: 简洁,使用方便;
- 缺点:
- 只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)
- 不能注入⼀个Final修饰的属性
- 构造函数注入(Spring 4.X推荐)
- 优点:
- 可以注入final修饰的属性
- 注入的对象不会被修改
- 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法.
- 通用性好, 构造方法是JDK支持的, 所以更换任何框架,他都是适用的
- 缺点:
- 注入多个对象时, 代码会比较繁琐
- 优点:
- Setter注入(Spring 3.X推荐)
- 优点: 方便在类实例之后, 重新对该对象进行配置或者注入
- 缺点:
- 不能注入⼀个Final修饰的属性
- 注入对象可能会被改变, 因为setter方法可能会被多次调用, 就有被修改的风险.
4. 总结:
DI: Dependency Injection,叫做 依赖注入。
DI(依赖注入),有三种方式:
- 属性注入(Field Injection) (更推荐程序员使用这个)
- 构造方法注入(Constructor Injection)
- Setter 注入(Setter Injection)
一道面试题:三种注入方式优缺点分析。
最后,如果这篇博客能帮到你的,请你点点赞,有写错了,写的不好的,欢迎评论指出,谢谢!
下一篇博客:SpringIoC & DI(5):DI详解