目录
[一、 核心概念通俗解读](#一、 核心概念通俗解读)
[1. Bean 是什么?](#1. Bean 是什么?)
[2. IoC (Inversion of Control - 控制反转)](#2. IoC (Inversion of Control - 控制反转))
[3. DI (Dependency Injection - 依赖注入)](#3. DI (Dependency Injection - 依赖注入))
[二、 完整 Java 代码演示](#二、 完整 Java 代码演示)
[1. 定义标准 (接口)](#1. 定义标准 (接口))
[2. 定义 Bean (实现类)](#2. 定义 Bean (实现类))
[3. 依赖注入 (使用类)](#3. 依赖注入 (使用类))
[4. 启动容器 (模拟 Main 方法)](#4. 启动容器 (模拟 Main 方法))
[三、 知识点详细解析(结合图片)](#三、 知识点详细解析(结合图片))
[1. 为什么叫"控制反转" (IoC)?](#1. 为什么叫“控制反转” (IoC)?)
[2. 依赖注入 (DI) 的本质](#2. 依赖注入 (DI) 的本质)
[四、 扩展知识讲解 (进阶必读)](#四、 扩展知识讲解 (进阶必读))
[1. 构造器注入 vs 字段注入](#1. 构造器注入 vs 字段注入)
[2. 如果有两个电脑怎么办?(@Qualifier)](#2. 如果有两个电脑怎么办?(@Qualifier))
[3. Bean 的生命周期](#3. Bean 的生命周期)
[总结Bean 的三种注册方式以及依赖注入的方法。举例说明能让我记忆深刻。](#总结Bean 的三种注册方式以及依赖注入的方法。举例说明能让我记忆深刻。)
[第一部分:Bean 的三种注册方式](#第一部分:Bean 的三种注册方式)
[1. XML 配置文件(上古卷轴)](#1. XML 配置文件(上古卷轴))
[2. @Component 及其衍生注解(自动扫描)](#2. @Component 及其衍生注解(自动扫描))
[3. @Configuration + @Bean(Java 配置类)](#3. @Configuration + @Bean(Java 配置类))
[第二部分:依赖注入 (DI) 的三种方式](#第二部分:依赖注入 (DI) 的三种方式)
[1. 字段注入 (Field Injection) ------ @Autowired 放在变量上](#1. 字段注入 (Field Injection) —— @Autowired 放在变量上)
[2. Setter 注入 ------ @Autowired 放在 set 方法上](#2. Setter 注入 —— @Autowired 放在 set 方法上)
[3. 构造器注入 (Constructor Injection) ------ 最强推荐 🏆](#3. 构造器注入 (Constructor Injection) —— 最强推荐 🏆)
这张图片上的文字 "Bean 的管理 (IoC) 与依赖注入 (DI)" 只有短短几个字,但它是整个 Spring 框架的灵魂。如果不理解这句话,就没有真正学会 Spring。
为了让你彻底搞懂,我将通过 "传统代码 vs Spring 代码" 的对比,配合全注释的 Java 代码来为你拆解。
一、 核心概念通俗解读
1. Bean 是什么?
在 Java 中,我们把每一个被 Spring 容器管理的对象,都叫 Bean。
-
以前: 你自己
new User(),这个 User 是你私有的。 -
现在: 你把 User 交给 Spring 管,这个 User 就变成了 Bean。
2. IoC (Inversion of Control - 控制反转)
这是权力的转移。
-
以前 (正向控制): 你想吃苹果,必须自己去买 (在代码里写
new Apple())。 -
现在 (控制反转): 你想吃苹果,就在桌子上放个盘子喊一声。Spring (容器) 会自动把苹果买好放到你盘子里。创建对象的控制权,从你手里交给了 Spring。
3. DI (Dependency Injection - 依赖注入)
这是 IoC 的具体实现方式。
- Spring 怎么把苹果给你?它把苹果塞进(Inject)你的盘子(变量)里。这就叫依赖注入。
二、 完整 Java 代码演示
为了演示这套机制,我们模拟一个场景:一名程序员 (Programmer) 需要一台电脑 (Computer) 才能工作。
1. 定义标准 (接口)
首先,定义电脑的标准。
Java
java
// 这是一个接口,代表电脑的标准
public interface Computer {
void runCode(); // 电脑必须能运行代码
}
2. 定义 Bean (实现类)
我们要告诉 Spring,这里有一台具体的电脑(比如 Mac),请把它管理起来。
Java
java
import org.springframework.stereotype.Component;
/**
* @Component: 这个注解就像在脑门上贴个标签:"我是 Spring 的 Bean"。
* Spring 扫描到它时,就会自动执行:Computer macComputer = new MacComputer();
* 并把它放入 IoC 容器(大仓库)里。
*/
@Component
public class MacComputer implements Computer {
@Override
public void runCode() {
System.out.println("Mac电脑正在飞速编译代码...");
}
}
3. 依赖注入 (使用类)
现在有一个程序员类,他需要电脑。注意看他从来没有 new 电脑。
Java
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Component: 程序员也是一个 Bean,也交给 Spring 管理。
*/
@Component
public class Programmer {
/**
* @Autowired: 这就是 DI(依赖注入)的核心!
* 意思:Spring,请去你的仓库(IoC容器)里帮我找一个实现了 Computer 接口的 Bean。
* 找到后,自动赋值给这个变量。
* * 如果没有这个注解,computer 变量就是 null,调用会报错。
*/
@Autowired
private Computer computer;
public void work() {
System.out.println("程序员准备开始工作...");
// 直接使用 computer,完全不需要自己 new!
computer.runCode();
}
}
4. 启动容器 (模拟 Main 方法)
最后,我们需要启动 Spring 容器,让这一切发生。
Java
java
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @Configuration: 告诉 Spring 这是一个配置类。
* @ComponentScan: 告诉 Spring 去扫描当前包及其子包,找找有没有加 @Component 的类。
*/
@Configuration
@ComponentScan(basePackages = "com.example.demo")
public class MainApp {
public static void main(String[] args) {
// 1. 启动 IoC 容器
// 这一步发生时,Spring 会自动创建 MacComputer 和 Programmer 的实例。
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MainApp.class);
System.out.println("容器启动完毕!");
// 2. 从容器里拿出"程序员" Bean
// 我们不需要 new Programmer(),而是向容器要。
Programmer programmer = context.getBean(Programmer.class);
// 3. 让程序员工作
// 此时,programmer 内部的 computer 已经被 Spring 自动注入好了。
programmer.work();
// 关闭容器
context.close();
}
}
三、 知识点详细解析(结合图片)
1. 为什么叫"控制反转" (IoC)?
-
控制权: 指的是"谁负责创建对象"和"谁负责维护对象之间的关系"。
-
正向(传统):
Java
java// 这种写法,Programmer 必须知道 MacComputer 的存在,耦合度极高。 // 如果以后想换成 Windows,必须修改 Programmer 的代码。 Computer computer = new MacComputer(); -
反转(Spring):
Java
java// Programmer 根本不知道 computer 是 Mac 还是 Windows。 // 只要 Spring 给它塞进去什么,它就用什么。 @Autowired private Computer computer;控制权从"程序员"手里反转到了"Spring 容器"手里。
2. 依赖注入 (DI) 的本质
DI 是 IoC 的手段。IoC 是思想,DI 是做法。
Spring 就像一个全自动管家:
-
扫描: 发现你需要一个
Computer。 -
寻找: 在它的仓库里找找有没有
MacComputer。 -
注入: 偷偷把
MacComputer赋值给你的变量。
四、 扩展知识讲解 (进阶必读)
1. 构造器注入 vs 字段注入
上面的例子用的是字段注入(直接在变量上加 @Autowired),这最简单,但不被推荐用于生产环境的复杂业务。
推荐写法(构造器注入):
Java
java
@Component
public class Programmer {
private final Computer computer; // 可以声明为 final,更安全
// Spring 4.3+ 以后,如果只有一个构造方法,@Autowired 可以省略
// 好处:这就强迫你必须给 Programmer 一台电脑,否则连对象都造不出来,避免了空指针风险。
public Programmer(Computer computer) {
this.computer = computer;
}
}
2. 如果有两个电脑怎么办?(@Qualifier)
如果你的仓库里既有 MacComputer 也有 WindowsComputer(都加了 @Component),Spring 会懵圈:"你要哪一个?"
这时会报错:NoUniqueBeanDefinitionException。
解决办法:指定名字
Java
java
@Autowired
@Qualifier("macComputer") // 指定 Bean 的名字(默认是类名首字母小写)
private Computer computer;
3. Bean 的生命周期
你把对象交给 Spring 管理后,Spring 不仅仅是帮你 new 一下那么简单。它还会负责:
-
初始化前/后: 执行
@PostConstruct注解的方法。 -
销毁时: 执行
@PreDestroy注解的方法(比如关闭数据库连接)。
总结
图片中的那行字,用一句话概括就是:
不要自己 new 对象,在类上加 @Component 交给 Spring (IoC),在需要用的属性上加 @Autowired 让 Spring 填给你 (DI)。
总结Bean 的三种注册方式以及依赖注入的方法。举例说明能让我记忆深刻。
这是一个完美的总结时刻!为了让你不仅记住代码,还能记住原理,我们用一个**"组建超级英雄联盟"**的故事来比喻。
第一部分:Bean 的三种注册方式
(潜台词:如何把英雄招募进联盟?)
Spring 容器就是"英雄联盟基地"。我们需要把各种英雄(对象)注册进去。
1. XML 配置文件(上古卷轴)
-
比喻: 纸质花名册。
在几十年前,没有电脑,神盾局要把英雄名字写在一个厚厚的本子上。
-
特点: 古老、繁琐、要把类和配置分开写。现在除了维护老古董系统,基本没人用了。
-
代码长相:
XML
java<bean id="ironMan" class="com.marvel.IronMan" />
2. @Component 及其衍生注解(自动扫描)
-
比喻: 自带工牌,刷脸进门。
英雄们(类)自己在脑门上贴个二维码(@Component / @Service / @Controller)。基地的监控系统(Spring Boot)一扫,自动识别:"哦,你是自己人,进来吧!"
-
适用场景: 你自己写的代码。
-
代码长相:
Java
java@Component // <--- 脑门贴条:我是英雄 public class IronMan { ... }
3. @Configuration + @Bean(Java 配置类)
-
比喻: 外包合同 / 进口采购。
有些装备(比如"雷神之锤"),它不是人,也没法自己在脑门贴条(因为它是第三方库里的类,你改不了它的源码)。
这时候你需要一个专门的"采购员"(配置类),手动把它造出来,送进基地。
-
适用场景: 引入第三方库(如数据库连接池、Gson、Redis客户端)。
-
代码长相:
Java
java@Configuration // <--- 我是采购部 public class WeaponConfig { @Bean // <--- 这是采购回来的宝贝,也要入库 public Hammer mjolnir() { return new Hammer("雷神之锤"); } }
第二部分:依赖注入 (DI) 的三种方式
(潜台词:英雄进门了,怎么把武器塞给他是最稳的?)
假设:钢铁侠 (IronMan) 必须要有 反应堆 (Reactor) 才能飞。
1. 字段注入 (Field Injection) ------ @Autowired 放在变量上
-
比喻: 隔空变物(魔法)。
钢铁侠两手空空走进基地,Spring 会魔法,直接把反应堆"变"进他的胸口里。
-
缺点: 太依赖魔法。如果脱离了 Spring 环境(比如你自己写单元测试),这招魔法就失效了,钢铁侠胸口是空的,直接空指针异常。
-
推荐度: ⭐⭐(写起来最爽,但官方不推荐,容易造成循环依赖和测试困难)。
Java
java@Component public class IronMan { @Autowired // <--- 魔法注入 private Reactor reactor; }
2. Setter 注入 ------ @Autowired 放在 set 方法上
-
比喻: 快递补给。
钢铁侠先裸身进来,然后有人给他送个快递:"嘿,你的反应堆到了,装上吧。"
-
特点: 装备可拆卸。钢铁侠可以一会装这个反应堆,一会换那个。
-
适用场景: 可选依赖(比如这个装备不是必须的,没有也能活)。
Java
java@Component public class IronMan { private Reactor reactor; @Autowired // <--- 什么时候想用,什么时候装 public void setReactor(Reactor reactor) { this.reactor = reactor; } }
3. 构造器注入 (Constructor Injection) ------ 最强推荐 🏆
-
比喻: 进门安检强制发放。
基地的安检门规定:"钢铁侠,如果你手里没有拿反应堆,就不许进这个门(不许被创建)!"
-
优点: 绝对安全。
-
只要钢铁侠这个对象存在,他一定有反应堆(不会空指针)。
-
一旦装上,就焊死了(可以加
final),谁也别想偷偷换掉(不可变性)。
-
-
推荐度: ⭐⭐⭐⭐⭐(Spring 官方唯一指定最佳实践)。
Java
java@Component public class IronMan { private final Reactor reactor; // 加上 final,焊死! // 构造方法:想出生?先拿装备! // (Spring 4.3以后,如果只有一个构造方法,@Autowired 甚至可以省略) public IronMan(Reactor reactor) { this.reactor = reactor; } }
一张表总结(截图保存)
| 核心问题 | 方式 | 记忆口诀 | 适用/优缺点 |
|---|---|---|---|
| 注册 Bean | XML | 老账本 | 过时,繁琐。 |
| (怎么进门?) | @Component | 刷脸卡 | 最常用,用于自己写的类。 |
| @Bean | 搞外包 | 用于第三方类(改不了源码时)。 | |
| 依赖注入 | 字段注入 | 变魔术 | 写着快,测着坑(容易空指针)。 |
| (怎么给装备?) | Setter | 收快递 | 灵活,可选,但装备容易丢。 |
| 构造器 | 死规定 | 最推荐,必须有装备才能出生,安全可靠。 |
这样是不是清晰多了?下次写代码时,想象一下你是基地的管理员,正在给英雄发装备,你就知道该选哪种了!