spring6-IOC/DI的引入与开发环境的准备

Spring 6 是一个轻量级开源框架,基于 JDK 17 和 Jakarta EE 9+,以 IoC/DI 和 AOP 为核心,管理 Bean 生命周期和依赖关系,提供事务、AOP 等模块。本篇聚焦 IoC/DI,介绍其概念、优势及开发环境搭建。

Posted by Hilda on June 12, 2025

前一节介绍了一些spring相关的引入,例如spring的优势,spring的发展历程(Spring6最重要的就是基线更新,JDK17以及命名空间变成Jakarta),Spring的模块介绍(比如核心容器实现Bean对象生命周期和依赖关系的管理,实现IOC/DI,还有事务等等…)

现在聚焦于Spring的IOC/DI的学习,本篇笔记专注于:理解IOC/DI是什么,以及为了实现IOC/DI的练习,提前准备好开发的环境。


1.IOC/DI的引入- 概念的解释辨析

很多视频/书籍都喜欢把IOC和DI两者做区分,但是其实IoC(Inversion of Control,控制反转)和 DI(Dependency Injection,依赖注入)是一个东西,他们是描述同一个东西的不同的名称而已。

首先:

IOC:控制反转指的是创建对象的权利,或者说对对象的控制权,从开发者编写的 Java 代码中转移到了 Spring 容器。 传统上,当一个对象需要使用另一个对象时,开发者会手动使用 new 关键字来创建这个被依赖的对象。而在 IoC 的思想下,开发者不再需要这样做。Spring 容器会负责对象的创建、配置和生命周期管理。

直白来说:【对象的创建、依赖关系的组装及其生命周期的管理权】这个控制权被反转了,转移到由容器自动化管理。

DI:由于“控制反转”这个概念相对抽象,2004 年,著名软件大师 Martin Fowler 提出了一个更具描述性的新名称:“依赖注入”。依赖注入明确描述了“被注入对象依赖 IoC 容器来配置依赖对象”。简单来说,当 Spring 容器创建好某个对象(这个对象就是“依赖对象”)后,它会将这个依赖对象自动地“注入”到那些需要它的地方(即“被注入对象”)。

——一句话理解就是:【创建对象及对象之间依赖关系的管理权,都交由 Spring 容器来完成。

开发者无需关心对象的创建过程,只需声明它们之间的依赖关系,Spring 容器就会在运行时自动满足这些依赖。

那么这一点是怎么做的呢?

工作原理: Spring 容器在启动时,会读取开发者提供的配置信息(例如 XML 配置或注解),然后使用【反射】机制创建好相应的对象,并将这些对象存储在一个内部的集合(通常可以理解为一个 Map 集合,数据结构就是键值对)中。当应用程序代码需要某个对象时,它不再通过 new 来创建,而是向 Spring 容器“索取”(通过 ID 或类型),容器就会将已经创建并管理好的对象提供给它。


除了这些,再解释2个概念:

【1】容器Container:在 Spring 框架中,容器是放置所有被管理对象的“场所”或“载体”。 它是 IoC/DI 机制的实现者,负责管理应用程序中组件的生命周期和依赖关系。

  • Spring 容器的本质是一个复杂的软件组件,其核心是一个全局的 Map 对象(或类似的内部数据结构)。这个 Map 中存储了所有被 Spring 容器管理的对象实例。
  • 在 Spring 中,容器通常指的是 ApplicationContext 接口及其子接口或实现类ApplicationContext 提供了更丰富的功能,如国际化、事件传播、资源加载等,是访问和管理 Spring Bean 的主要入口点。早期的 Spring 容器还有 BeanFactory,但 ApplicationContextBeanFactory 的超集,提供了更多企业级功能。

【2】Bean:在 Spring 容器中,所有被 Spring 容器管理的对象都被称为 Bean。如果特指其中的一个对象,则称为“一个 Bean”。

  • Bean 的创建、配置、生命周期(初始化、使用、销毁)都由 Spring 容器管理
  • Bean 通常代表应用程序中的一个功能模块或组件,例如一个 Service 类、一个 Dao 类、一个 Controller 类等。
  • Bean 可以通过 XML 文件、Java 配置类(@Configuration@Bean 注解)或组件扫描(@Component@Service@Repository@Controller 等注解)来定义和注册到 Spring 容器中。

可以这样理解这三者之间的关系:开发者将需要 Spring 管理的对象(Bean)的定义告知给 Spring 容器,然后容器负责反转控制(IoC),即自行创建和管理这些 Bean,并在 Bean 之间需要协作时,自动地将一个 Bean 注入(DI) 到另一个 Bean 中,从而实现了松耦合的应用程序架构。

2.使用IOC/DI的好处

Spring IoC/DI使用后可以管理项目中相关对象,让对象管理的事情和业务分离(解耦)。同时程序员也不管理对象的依赖关系,所有的依赖关系交给Spring容器进行管理。

没有使用spring之前:

使用spring-IoC/DI以后:

以后如果改变实现类,只需要修改配置文件即可,不需要去java代码中操作了。

要注意我们改都是改实现类,不会改接口,接口是规范

Spring IoC/DI 的使用使得项目中对象管理(对象的创建、组装、生命周期)的职责与业务逻辑完全分离。在没有 Spring 之前,开发者需要在业务代码中手动 new 对象并管理它们的依赖关系,这导致业务代码与对象管理细节紧密耦合。引入 Spring 后,程序员不再需要亲自管理对象的依赖关系,所有的依赖关系都交由 Spring 容器进行管理

如果通过代码举例,可以看下面的例子:

没有使用 Spring 之前:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 假设 Service 层需要 Dao 层对象
public class PeopleServiceImpl {
    private PeopleDao peopleDao;

    public PeopleServiceImpl() {
        // 需要手动创建 Dao 对象,耦合性高
        this.peopleDao = new PeopleDaoImpl();
    }

    public void doSomething() {
        peopleDao.queryData();
    }
}

这种方式的缺点是,如果 PeopleDaoImpl 改变为 PeopleDaoHibernateImpl,那么 PeopleServiceImpl 的代码也必须修改。

使用 Spring-IoC/DI 以后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Spring 会自动注入 Dao 对象
public class PeopleServiceImpl {
    // 只需要声明依赖,Spring 会在运行时注入具体的实现
    private PeopleDao peopleDao;

    // 通过构造器、Setter 方法或字段注解实现依赖注入
    public PeopleServiceImpl(PeopleDao peopleDao) {
        this.peopleDao = peopleDao;
    }

    // 或者使用 @Autowired 注解
    // @Autowired
    // private PeopleDao peopleDao;

    public void doSomething() {
        peopleDao.queryData();
    }
}

通过 Spring IoC/DI,开发者只需在配置文件(或通过注解)中声明 PeopleServiceImpl 依赖于 PeopleDao 接口的具体实现,Spring 容器就会在运行时自动将 PeopleDaoImpl(或任何其他 PeopleDao 的实现)注入到 PeopleServiceImpl 中。

以后如果需要改变 PeopleDao 的具体实现类,例如从 PeopleDaoImpl 切换到 PeopleDaoMybatisImpl,只需要修改 Spring 的配置文件或 Java 配置类即可,而无需去改动业务 Java 代码。这大大降低了系统变更的风险和成本。

3.IOC/DI的具体使用场景

IoC/DI 的主要作用就是管理对象的实例化和对象之间依赖关系。项目中以前需要自己实例化的各种层对象、以及需要自己实例化的框架或工具包的入口类,都可以交给 Spring 容器进行管理。

  • 层对象: 比如 PeopleMapper 接口的代理对象(通常由 Mybatis 生成并与 Spring 集成)、PeopleDaoImpl(数据访问层实现类)、PeopleServiceImpl(业务逻辑层实现类)、PeopleController(控制层类)等这些不同层中的普通对象都可以交给 Spring 容器进行管理。这些放入到容器中的对象可以相互之间直接声明依赖,由 Spring 负责注入。
  • 框架入口类:SqlSessionFactory(Mybatis 的核心工厂类)、Hibernate SessionFactory(Hibernate 的核心工厂类)、DataSource(数据库连接池)等这些第三方框架或工具包的入口类,它们通常需要复杂的初始化过程,也都可以交给 Spring 容器进行统一管理。

需要注意的特例: 需要注意的是,并非所有对象都能被 Spring 容器管理。例如,Servlet 只能被 Tomcat 或其他 Web 容器管理(由 Tomcat 帮助实例化和创建的),所以 Spring 容器是无法直接管理 Servlet 的生命周期的。然而,Servlet 可以在其内部通过 Spring 的 API 从 Spring 容器中取出它所需要的对象(即 Bean),从而实现业务逻辑的解耦。这体现了 Spring 容器的强大集成能力和灵活性。

4.开发环境的搭建

注意:JDK17,maven起码3.6+

目前spring版本(最新的GA版本):6.2.7

image-20250612222754936

实现案例:创建Person类,让Spring帮我们管理对象。

在`pom.xml中添加spring的依赖。

Spring项目的最基本依赖包含:

  • spring-context.jar。spring上下文依赖,它依赖了下面的四个jar。
  • spring-core.jar。Spring 核心jar包。它依赖了spring-jcl.jar
  • spring-aop.jar。Spring AOP面向切面编程的基本支持。
  • spring-expression.jar。Spring的表达式语言支持。
  • spring-beans.jar。Spring容器的bean管理,创建对象非常重要的包。
  • spring-jcl.jar。内置的日志包,Spring 4版本时使用的还是common-logging.jar呢,但是从5开始Spring自己对日志进行了封装。

但是在Maven项目中想要使用Spring框架只需要在项目中导入spring-context就可以了,其他的jar包根据Maven依赖传递性都可以导入进来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.kirsten</groupId>
  <artifactId>IOCProject01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>IOCProject01</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>6.2.7</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

image-20250612223042027