JavaEE——Spring IoC与DI核心揭秘:从入门到精通

目录

[一、 核心概念通俗解读](#一、 核心概念通俗解读)

[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 就像一个全自动管家:

  1. 扫描: 发现你需要一个 Computer

  2. 寻找: 在它的仓库里找找有没有 MacComputer

  3. 注入: 偷偷把 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) ------ 最强推荐 🏆
  • 比喻: 进门安检强制发放。

    基地的安检门规定:"钢铁侠,如果你手里没有拿反应堆,就不许进这个门(不许被创建)!"

  • 优点: 绝对安全

    1. 只要钢铁侠这个对象存在,他一定有反应堆(不会空指针)。

    2. 一旦装上,就焊死了(可以加 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 收快递 灵活,可选,但装备容易丢。
构造器 死规定 最推荐,必须有装备才能出生,安全可靠。

这样是不是清晰多了?下次写代码时,想象一下你是基地的管理员,正在给英雄发装备,你就知道该选哪种了!

相关推荐
Mr.朱鹏9 小时前
RocketMQ安装与部署指南
java·数据库·spring·oracle·maven·rocketmq·seata
雨中飘荡的记忆9 小时前
Spring表达式详解:SpEL从入门到实战
java·spring
w***375110 小时前
Spring 核心技术解析【纯干货版】- Ⅶ:Spring 切面编程模块 Spring-Instrument 模块精讲
前端·数据库·spring
l***914711 小时前
SpringCloud 整合 Dubbo
spring·spring cloud·dubbo
Dolphin_Home11 小时前
Spring 事务避坑笔记:从入门到解决自调用陷阱
数据库·笔记·spring
yaoxin52112312 小时前
为什么 IRIS SQL 会比 Spring JDBC 更快?
数据库·sql·spring
Croa-vo12 小时前
Tesla Spring 2026 Co-op 面经:CodeSignal真题解析与通关攻略
java·后端·spring
n***786812 小时前
SpringCloud-持久层框架MyBatis Plus的使用与原理详解
spring·spring cloud·mybatis
C***u17612 小时前
【springboot】Spring 官方抛弃了 Java 8!新idea如何创建java8项目
java·spring boot·spring