欢迎光临
我们一直在努力

一文吃透 Spring 生态核心:IoC/DI 原理、Bean 管理与实战指南

目录

前言

一、Spring、Spring MVC 与 Spring Boot:关系与区别

1. 核心定位与功能

2. 三者的依赖关系

3. 实战场景举例(图书管理系统)

二、IoC(控制反转):Spring 核心思想

1. 什么是 IoC?

2. 传统开发 vs IoC 开发:耦合度对比

(1)传统开发:耦合度极高

(2)IoC 开发:彻底解耦

3. IoC 的核心优势

三、DI(依赖注入):IoC 的实现方式

1. 什么是 DI?

2. IoC 与 DI 的关系

3. DI 的实战体现

四、Spring 中 Bean 的存储与获取

1. Bean 的存储:两种注解方式

(1)类注解:快速存储 Bean

(2)方法注解 @Bean:灵活存储 Bean

2. Bean 的获取:三种方式

(1)通过类型获取(推荐,简洁)

(2)通过名称获取(需强转)

(3)通过类型 + 名称获取(解决同类型多个 Bean 冲突)

3. 上下文对比:ApplicationContext vs BeanFactory

五、依赖注入的三种方式:优缺点与实战

1. 属性注入(最常用,简洁)

2. 构造方法注入(Spring 4.X 推荐)

3. Setter 注入(Spring 3.X 推荐)

4. 三种注入方式对比表

六、解决多个同类型 Bean 的冲突

1. @Primary:指定默认 Bean

2. @Qualifier:指定 Bean 名称

3. @Resource:按名称注入(JDK 提供)

七、Bean 的命名规则

1. 类注解(@Controller、@Service 等)的 Bean 命名

2. 方法注解(@Bean)的 Bean 命名

八、Spring 扫描路径:确保 Bean 被正确识别

1. 默认扫描路径

2. 自定义扫描路径

九、常见面试题总结(高频考点)

1. 请谈谈 IoC 与 DI 的关系

2. 三种依赖注入方式的优缺点

3. @Autowired 与 @Resource 的区别

4. Spring、Spring MVC、Spring Boot 的关系

5. BeanFactory 与 ApplicationContext 的区别

十、总结


前言

在 Java 后端开发中,Spring 生态无疑是绕不开的核心技术栈。无论是初入职场的新手,还是经验丰富的开发者,理清 Spring、Spring MVC、Spring Boot 的关系,掌握 IoC/DI 核心思想,熟练运用 Bean 管理与依赖注入,都是进阶路上的必备技能。本文将结合实战案例,从概念到实操,全面拆解 Spring 生态的核心知识点,帮你构建完整的知识体系。

一、Spring、Spring MVC 与 Spring Boot:关系与区别

很多开发者刚接触 Spring 生态时,都会被这三个框架的关系搞晕。其实可以用一个通俗的比喻理解:Spring 是一个 “大家族”,Spring MVC 是家族中负责 “Web 业务” 的成员,而 Spring Boot 是家族的 “快速启动工具”。

1. 核心定位与功能

框架核心定位核心功能
Spring 轻量级、一站式企业级应用开发框架 核心是 IoC 容器(管理对象及依赖)、AOP(面向切面编程)、事务管理、数据访问支持
Spring MVC 基于 Spring 的 Web 框架 处理 HTTP 请求、URL 路由映射、参数接收、视图渲染、接口开发(RESTful)
Spring Boot Spring 生态的 “脚手架”,简化 Spring 应用开发 自动配置、 Starter 依赖整合、内嵌服务器(无需单独部署 Tomcat)、简化配置文件

2. 三者的依赖关系

  • Spring 是基础:Spring MVC 和 Spring Boot 都基于 Spring 核心(IoC 容器、AOP 等)实现,无法脱离 Spring 独立存在。
  • Spring MVC 是 Spring 的子模块:专门解决 Web 开发场景的问题,相当于 Spring 生态中的 “Web 模块”。
  • Spring Boot 是 Spring 的 “封装增强”:不提供新功能,而是通过 “自动配置” 和 “依赖整合”,简化 Spring + Spring MVC 项目的搭建和配置。

3. 实战场景举例(图书管理系统)

// Spring Boot:搭建项目框架(自动配置 Tomcat、Spring 容器)
@SpringBootApplication
public class BookApplication {
public static void main(String[] args) {
SpringApplication.run(BookApplication.class, args);
}
}

// Spring MVC:处理 Web 请求(URL 映射、接口响应)
@RestController
@RequestMapping("/book")
public class BookController {
// Spring:IoC 容器注入依赖(BookService 对象由 Spring 管理)
@Autowired
private BookService bookService;

// Spring MVC:处理 GET 请求,返回 JSON 数据
@RequestMapping("/getList")
public List<BookInfo> getList() {
return bookService.getBookList(); // 调用业务逻辑
}
}

// Spring:管理业务层 Bean,处理核心逻辑
@Service
public class BookService {
@Autowired
private BookDao bookDao; // 依赖注入数据访问层对象

public List<BookInfo> getBookList() {
List<BookInfo> books = bookDao.mockData();
// 业务处理:转换状态为中文
for (BookInfo book : books) {
book.setStatusCN(book.getStatus() == 1 ? "可借阅" : "不可借阅");
}
return books;
}
}

从代码中可以清晰看到:

  • Spring Boot 负责项目启动和自动配置;
  • Spring MVC 负责 Web 层的请求处理;
  • Spring 核心(IoC/DI)负责管理 Bean 及其依赖关系。

二、IoC(控制反转):Spring 核心思想

1. 什么是 IoC?

IoC(Inversion of Control,控制反转)是 Spring 的核心思想,本质是将对象的创建、管理权限从程序代码转移到 IoC 容器,程序不再直接通过 new 关键字创建对象,而是从容器中获取所需对象。

简单说:传统开发是 “程序自己造对象”,IoC 开发是 “容器送对象”。

2. 传统开发 vs IoC 开发:耦合度对比

以 “造车” 为例,直观感受两者的差异。

(1)传统开发:耦合度极高

传统开发中,对象的创建是 “自上而下” 的,上层对象依赖下层对象的创建,修改底层会导致整个调用链修改:

// 传统造车:Car 依赖 Framework,Framework 依赖 Bottom,Bottom 依赖 Tire
public class TraditionalCarDemo {
static class Car {
private Framework framework;
// Car 自己创建 Framework
public Car() {
this.framework = new Framework();
System.out.println("Car init…");
}
}

static class Framework {
private Bottom bottom;
// Framework 自己创建 Bottom
public Framework() {
this.bottom = new Bottom();
System.out.println("Framework init…");
}
}

static class Bottom {
private Tire tire;
// Bottom 自己创建 Tire
public Bottom() {
this.tire = new Tire();
System.out.println("Bottom init…");
}
}

static class Tire {
private int size = 17;
public Tire() {
System.out.println("Tire init: size=" + size);
}
}

public static void main(String[] args) {
// 只需要创建 Car,后续对象会被递归创建
new Car();
}
}

问题:如果需要修改轮胎尺寸(比如从 17 改为 20),需要修改 Tire、Bottom、Framework、Car 四个类的构造函数,耦合度极高,可维护性差。

(2)IoC 开发:彻底解耦

IoC 开发中,对象的创建是 “自下而上” 的,上层对象通过 “注入” 获取下层对象,不再自己创建:

// IoC 造车:对象通过构造函数注入,不再自己创建依赖
public class IocCarDemo {
static class Car {
private Framework framework;
// 注入 Framework,而非自己创建
public Car(Framework framework) {
this.framework = framework;
System.out.println("Car init…");
}
}

static class Framework {
private Bottom bottom;
// 注入 Bottom
public Framework(Bottom bottom) {
this.bottom = bottom;
System.out.println("Framework init…");
}
}

static class Bottom {
private Tire tire;
// 注入 Tire
public Bottom(Tire tire) {
this.tire = tire;
System.out.println("Bottom init…");
}
}

static class Tire {
private int size;
public Tire(int size) {
this.size = size;
System.out.println("Tire init: size=" + size);
}
}

public static void main(String[] args) {
// 先创建底层对象,再注入到上层
Tire tire = new Tire(20);
Bottom bottom = new Bottom(tire);
Framework framework = new Framework(bottom);
Car car = new Car(framework);
}
}

优势:修改轮胎尺寸时,只需修改 Tire 的创建参数,其他类无需改动,彻底解耦。

3. IoC 的核心优势

  • 解耦:消除对象间的直接依赖,修改一个类不会影响其他类;
  • 可维护性:对象的创建、初始化统一由容器管理,便于统一配置和修改;
  • 可扩展性:新增功能时,只需新增 Bean 并配置依赖,无需修改现有代码;
  • 资源集中管理:容器统一管理对象的生命周期(创建、销毁),避免资源泄漏。
  • 三、DI(依赖注入):IoC 的实现方式

    1. 什么是 DI?

    DI(Dependency Injection,依赖注入)是 IoC 思想的具体实现:容器在创建 Bean 时,自动将其依赖的其他 Bean 注入到当前 Bean 中,无需程序手动获取。

    简单说:IoC 是 “让容器管对象” 的思想,DI 是 “容器给对象送依赖” 的具体操作。

    2. IoC 与 DI 的关系

    • IoC 是 “目标”:核心是反转对象的控制权;
    • DI 是 “手段”:通过注入依赖的方式,实现控制权反转。

    比如:“吃一顿好的” 是思想(IoC),“吃海底捞” 是具体实现(DI)。

    3. DI 的实战体现

    在 Spring 中,通过 @Autowired 等注解实现 DI,例如:

    @Service
    public class BookService {
    // 容器自动注入 BookDao 对象,无需手动 new
    @Autowired
    private BookDao bookDao;

    public List<BookInfo> getBookList() {
    return bookDao.mockData(); // 直接使用注入的依赖
    }
    }

    四、Spring 中 Bean 的存储与获取

    Bean 是 Spring 容器管理的核心对象(如 Service、Dao、Controller 等)。要让 Spring 管理 Bean,需先 “存储” Bean,再根据需求 “获取” Bean。

    1. Bean 的存储:两种注解方式

    (1)类注解:快速存储 Bean

    适用于自定义类,直接在类上添加注解,Spring 会自动扫描并创建 Bean。Spring 提供 5 种类注解,对应不同的应用分层:

    注解适用场景作用
    @Controller 控制层(Web 层) 处理 HTTP 请求,返回响应,标识为 Web 层 Bean
    @Service 业务逻辑层 处理核心业务逻辑,标识为业务层 Bean
    @Repository 数据访问层(Dao 层) 操作数据库(或数据存储),标识为数据层 Bean,还会自动处理数据库异常
    @Component 通用组件层 无明确分层的通用 Bean(如工具类),是其他类注解的 “父注解”
    @Configuration 配置层 项目配置类(如数据源配置、Bean 配置),标识为配置类 Bean

    使用示例:

    // 控制层 Bean
    @Controller
    public class UserController {
    public void sayHi() {
    System.out.println("Hi, UserController!");
    }
    }

    // 业务层 Bean
    @Service
    public class UserService {
    public void sayHi() {
    System.out.println("Hi, UserService!");
    }
    }

    // 数据层 Bean
    @Repository
    public class UserDao {
    public void sayHi() {
    System.out.println("Hi, UserDao!");
    }
    }

    // 配置类 Bean
    @Configuration
    public class AppConfig {
    public void config() {
    System.out.println("App config…");
    }
    }

    (2)方法注解 @Bean:灵活存储 Bean

    适用于第三方类(如 User、DataSource)或需要多个实例的场景,通过方法返回 Bean 对象,需配合类注解使用(否则 Spring 无法扫描到)。

    使用示例:

    // 配合 @Component 类注解,让 @Bean 生效
    @Component
    public class BeanConfig {
    // 存储 User 实例,Bean 名称默认是方法名 "user1"
    @Bean
    public User user1() {
    User user = new User();
    user.setName("张三");
    user.setAge(18);
    return user;
    }

    // 存储多个 User 实例,通过 name 属性自定义 Bean 名称
    @Bean(name = {"user2", "lisi"})
    public User user2() {
    User user = new User();
    user.setName("李四");
    user.setAge(19);
    return user;
    }
    }

    注意:

    • @Bean 必须配合类注解(如 @Component、@Configuration)使用,否则 Spring 无法识别;
    • 若未指定 name 属性,Bean 名称默认是方法名;
    • 支持多个名称(如 name = {"user2", "lisi"}),用逗号或数组分隔。

    2. Bean 的获取:三种方式

    获取 Bean 需先获取 Spring 上下文(ApplicationContext),再通过上下文获取 Bean。常用三种方式:

    (1)通过类型获取(推荐,简洁)

    @SpringBootApplication
    public class SpringIocDemo {
    public static void main(String[] args) {
    // 1. 获取 Spring 上下文
    ApplicationContext context = SpringApplication.run(SpringIocDemo.class, args);

    // 2. 通过类型获取 Bean
    UserController userController = context.getBean(UserController.class);

    // 3. 使用 Bean
    userController.sayHi(); // 输出:Hi, UserController!
    }
    }

    (2)通过名称获取(需强转)

    // 通过 Bean 名称获取(名称规则见下文)
    UserController userController = (UserController) context.getBean("userController");

    (3)通过类型 + 名称获取(解决同类型多个 Bean 冲突)

    // 明确指定 Bean 名称和类型,无需强转
    User user = context.getBean("user2", User.class);
    System.out.println(user); // 输出:User(name=李四, age=19)

    3. 上下文对比:ApplicationContext vs BeanFactory

    Spring 容器有两个核心接口,面试常考:

    特性ApplicationContext(推荐)BeanFactory(轻量)
    继承关系 继承 BeanFactory,功能更全 顶层接口,提供基础功能
    初始化方式 启动时一次性创建所有 Bean(空间换时间) 按需创建 Bean(用时才创建,时间换空间)
    额外功能 支持国际化、资源访问、事件传播 仅提供 Bean 存储和获取功能
    适用场景 大部分应用(Web 应用、Spring Boot) 资源受限场景(如嵌入式设备)

    五、依赖注入的三种方式:优缺点与实战

    Spring 提供三种依赖注入方式,适用于不同场景,需根据需求选择:

    1. 属性注入(最常用,简洁)

    通过 @Autowired 直接标注字段,容器自动注入依赖。

    示例:

    @Controller
    public class UserController {
    // 直接注入 UserService
    @Autowired
    private UserService userService;

    public void sayHi() {
    userService.sayHi(); // 输出:Hi, UserService!
    }
    }

    优缺点:

    • 优点:代码简洁,无需写构造函数或 Setter 方法;
    • 缺点:
      • 无法注入 final 修饰的字段(final 字段需在构造时初始化);
      • 仅支持 IoC 容器,非 IoC 环境(如单元测试)无法使用,容易出现空指针异常;
      • 不符合 Java 规范(字段私有化,应通过构造或 Setter 访问)。

    2. 构造方法注入(Spring 4.X 推荐)

    通过构造函数注入依赖,@Autowired 可省略(若只有一个构造函数)。

    示例:

    @Controller
    public class UserController {
    private final UserService userService; // 支持 final 字段

    // 构造方法注入,@Autowired 可省略
    public UserController(UserService userService) {
    this.userService = userService;
    }

    public void sayHi() {
    userService.sayHi();
    }
    }

    优缺点:

    • 优点:
      • 支持 final 字段,确保依赖不可修改;
      • 依赖在对象创建时初始化,避免空指针异常;
      • 通用性强,符合 Java 规范,换框架也能使用;
    • 缺点:
      • 依赖较多时,构造函数参数过长,代码繁琐。

    3. Setter 注入(Spring 3.X 推荐)

    通过 Setter 方法注入依赖,需在方法上添加 @Autowired。

    示例:

    @Controller
    public class UserController {
    private UserService userService;

    // Setter 方法注入
    @Autowired
    public void setUserService(UserService userService) {
    this.userService = userService;
    }

    public void sayHi() {
    userService.sayHi();
    }
    }

    优缺点:

    • 优点:
      • 灵活性高,可在对象创建后重新注入依赖;
      • 符合 JavaBean 规范;
    • 缺点:
      • 无法注入 final 字段;
      • 依赖可能被多次修改(Setter 方法可被重复调用);
      • 对象创建时依赖可能未初始化,存在空指针风险。

    4. 三种注入方式对比表

    注入方式支持 final通用性代码简洁度依赖安全性
    属性注入 不支持
    构造方法注入 支持
    Setter 注入 不支持

    推荐用法:

    • 依赖较少(1-2 个):属性注入(简洁);
    • 依赖较多(3 个以上):构造方法注入(安全);
    • 需要动态修改依赖:Setter 注入(灵活)。

    六、解决多个同类型 Bean 的冲突

    当 Spring 容器中存在多个同类型 Bean 时,使用 @Autowired 会报错(无法确定注入哪个),需通过以下三种方式解决:

    1. @Primary:指定默认 Bean

    在多个同类型 Bean 中,用 @Primary 标注默认注入的 Bean。

    示例:

    @Component
    public class BeanConfig {
    // 标注为默认 Bean
    @Primary
    @Bean("user1")
    public User user1() {
    User user = new User();
    user.setName("张三");
    return user;
    }

    @Bean("user2")
    public User user2() {
    User user = new User();
    user.setName("李四");
    return user;
    }
    }

    // 注入时,默认选择 @Primary 标注的 user1
    @Controller
    public class UserController {
    @Autowired
    private User user; // 注入的是 user1(张三)
    }

    2. @Qualifier:指定 Bean 名称

    配合 @Autowired 使用,通过 value 属性指定要注入的 Bean 名称。

    示例:

    @Controller
    public class UserController {
    // 明确指定注入名称为 "user2" 的 Bean
    @Autowired
    @Qualifier("user2")
    private User user; // 注入的是 user2(李四)
    }

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

    3. @Resource:按名称注入(JDK 提供)

    @Resource 是 JDK 自带的注解(非 Spring 提供),默认按名称注入,可通过 name 属性指定 Bean 名称。

    示例:

    @Controller
    public class UserController {
    // 按名称注入 "user2"
    @Resource(name = "user2")
    private User user; // 注入的是 user2(李四)
    }

    优势:

    • 无需依赖 Spring 框架,兼容性更强;
    • 支持更多属性(如 name、type),配置更灵活。

    七、Bean 的命名规则

    Spring 对 Bean 的命名有明确约定,避免手动配置时出现错误:

    1. 类注解(@Controller、@Service 等)的 Bean 命名

    • 默认规则:类名首字母小写,其余驼峰命名(如 UserController → userController);
    • 特殊情况:类名前两个字母均为大写时,保留原类名(如 UController → UController、AManager → AManager);
    • 自定义名称:通过注解的 value 属性指定(如 @Controller(value = "myUserController") 或简写 @Controller("myUserController"))。

    2. 方法注解(@Bean)的 Bean 命名

    • 默认规则:Bean 名称 = 方法名(如 @Bean public User user1() → 名称为 user1);
    • 自定义名称:通过 name 属性指定(如 @Bean(name = {"u1", "user1"}))。

    八、Spring 扫描路径:确保 Bean 被正确识别

    Spring 不会自动扫描所有包下的 Bean,需满足扫描路径规则才能识别注解:

    1. 默认扫描路径

    Spring Boot 中,@SpringBootApplication 注解默认包含 @ComponentScan,扫描路径为启动类所在包及其子包。

    例如:启动类 BookApplication 位于 com.example.demo 包下,Spring 会自动扫描 com.example.demo 及所有子包(如 com.example.demo.controller、com.example.demo.service)。

    2. 自定义扫描路径

    若 Bean 不在默认扫描路径下,需通过 @ComponentScan 手动指定:

    // 扫描多个包:com.example.demo 和 com.example.util
    @ComponentScan({"com.example.demo", "com.example.util"})
    @SpringBootApplication
    public class BookApplication {
    public static void main(String[] args) {
    SpringApplication.run(BookApplication.class, args);
    }
    }

    推荐实践:将启动类放在项目根包下(如 com.example.demo),所有业务代码放在子包中,无需手动配置扫描路径。

    九、常见面试题总结(高频考点)

    1. 请谈谈 IoC 与 DI 的关系

    • IoC(控制反转)是核心思想:将对象的创建、管理权从程序转移到容器,解决对象耦合问题;
    • DI(依赖注入)是 IoC 的实现方式:容器在创建 Bean 时,自动注入其依赖的其他 Bean;
    • 关系:IoC 是 “目标”,DI 是 “手段”,两者描述的是同一件事(解耦),只是角度不同(IoC 从容器角度,DI 从应用程序角度)。

    2. 三种依赖注入方式的优缺点

    注入方式支持 final通用性代码简洁度依赖安全性
    属性注入 不支持
    构造方法注入 支持
    Setter 注入 不支持

    3. @Autowired 与 @Resource 的区别

    对比维度@Autowired@Resource
    来源 Spring 框架提供 JDK 自带(java.annotation)
    注入方式 默认按类型注入 默认按名称注入
    支持属性 仅支持 required(是否必须注入) 支持 name、type 等属性
    兼容性 仅支持 Spring 容器 支持所有 IoC 容器(如 Spring、JBoss)
    配合注解 需配合 @Qualifier 指定名称 直接通过 name 指定名称

    4. Spring、Spring MVC、Spring Boot 的关系

    • Spring 是基础框架,核心是 IoC 容器,提供 AOP、事务管理等核心功能;
    • Spring MVC 是 Spring 的子模块,专注于 Web 开发,处理 HTTP 请求、路由映射等;
    • Spring Boot 是 Spring 的 “脚手架”,通过自动配置、Starter 依赖简化项目搭建,底层依赖 Spring 和 Spring MVC;
    • 总结:Spring 是 “地基”,Spring MVC 是 “Web 楼层”,Spring Boot 是 “快速盖楼工具”。

    5. BeanFactory 与 ApplicationContext 的区别

    特性ApplicationContext(推荐)BeanFactory(轻量)
    继承关系 继承 BeanFactory,功能更全 顶层接口,提供基础功能
    初始化方式 启动时一次性创建所有 Bean(空间换时间) 按需创建 Bean(用时才创建,时间换空间)
    额外功能 支持国际化、资源访问、事件传播 仅提供 Bean 存储和获取功能
    适用场景 大部分应用(Web 应用、Spring Boot) 资源受限场景(如嵌入式设备)

    十、总结

    Spring 生态的核心是 IoC/DI,通过容器管理 Bean 及其依赖,实现代码解耦。Spring MVC 专注于 Web 开发,Spring Boot 简化配置,三者分工明确、相辅相成。

    掌握本文核心知识点:

  • 理清 Spring、Spring MVC、Spring Boot 的关系;
  • 理解 IoC/DI 的核心思想与实操;
  • 熟练使用 Bean 的存储(类注解、@Bean)与获取方式;
  • 选择合适的依赖注入方式;
  • 解决 Bean 冲突与扫描路径问题。
  • 赞(0)
    未经允许不得转载:171主机测评 » 一文吃透 Spring 生态核心:IoC/DI 原理、Bean 管理与实战指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址