前言

在 2026 年的 Java 开发世界里,Spring Boot 已经成为了事实上的标准。我们铺天盖地的项目、微服务、中间件,底层都离不开 Spring 的支撑。为了不“浮沙筑高台”,我们非常有必要深入底层,把 Spring Framework 这个地基打牢。

今天我们开启 Spring 全家桶学习的第一站:Spring Framework。先整体看看它的架构,再深入理解它最核心的两个思想:IOC(控制反转)DI(依赖注入),最后手写代码配置出第一个 Spring 容器。

💬 课前摸底:你在平时写代码时,是不是经常遇到“改了一个类名,结果关联的地方要改一堆”的情况?有没有想过如何让程序“解耦”?在评论区说说你对“耦合”的痛恨程度!

一、什么是 Spring?一张图看懂它的家族

目前我们要掌握的 Spring 全家桶主要包含三部分:

  1. Spring Framework:底层核心框架(我们今天学这个)。
  2. Spring Boot:对 Spring 的封装简化,开箱即用。
  3. Spring Cloud:分布式微服务解决方案。

Spring Framework 的五层架构

从 Spring 4 开始,整体架构大致分为五层,从底向上依次是:

层级 模块 核心作用
1. 核心层 (Core) Core Container Spring 最底层核心,其他所有层都依赖它。
2. AOP 层 AOP, Aspects 面向切面编程,在不改变原代码前提下增强功能。
3. 数据层 (Data) Data Access, Integration, Transactions 负责数据库访问、事务管理。
4. Web 层 Web, Servlet (Spring MVC) 处理 Web 请求,MVC 模式实现(后续重点)。
5. 测试层 (Test) Test 整合 Junit,支持单元测试、集成测试。

🧠 AOP 小提示:OOP 是面向对象,AOP 则是面向方法(切面)。目前只需要知道它是用来“加功能”的即可,后面会细讲。

二、Spring 核心概念:别再自己 new 对象了!

在没有框架的时候,我们写 Java Web 项目,业务层(Service)要调数据层(Dao),是不是只能 private BookDao bookDao = new BookDaoImpl();

这会导致一个巨麻烦的问题:高耦合。一旦 BookDaoImpl 改了名或换了实现类,所有引用它的 Service 你都得改一遍代码。我们追求的“高内聚、低耦合”瞬间崩塌。

为了解决这个痛点,Spring 提出了 IOC 思想,并且用 IOC 容器 把这个思想实现了。

2.1 IOC(控制反转)

思想定义:把对象的创建控制权从程序内部,反转给外部容器来管理。

  • 以前:你自己做饭吃(主动 new 对象)。
  • 现在:你点外卖(向 IOC 容器要对象)。

简单来说:你的代码里不再需要 new 对象,需要什么找容器拿就行。

2.2 IOC 容器 与 Bean

Spring 实现 IOC 的做法是搞一个“大池子”,专门负责创建和管理对象。

  • IOC 容器:这个管理对象的大池子。
  • Bean:容器里存放的每一个对象,就叫做 Bean。

2.3 DI(依赖注入)

容器里既有 BookService 的 Bean,也有 BookDao 的 Bean。但是,BookService 想要调用 BookDao,容器怎么知道它们俩有关系呢?

这时候就需要 DI(依赖注入) 了。它的作用就是建立 Bean 与 Bean 之间的绑定关系

  • 以前:Service 自己找 Dao。
  • 现在:Service 跟容器喊一声:“喂,给我注入一个 Dao 用用!”,容器就把 Dao 的 Bean 塞给 Service,这就是依赖注入。

✍️ 动笔理一理:IOC 是思想(控制权反转),DI 是手段(注入依赖关系)。容器是 IOC 思想的产物,Bean 是容器里的物件。这几个概念的关系你厘清了吗?

三、手把手:从底层玩转 Spring IOC

别看 Spring Boot 里写个 @Autowired 就搞定了,底层 Spring Framework 其实是通过配置文件完成的。跟着我做,体会一把“从底抄”的感觉。

3.1 第一步:创建 Maven 项目,加依赖

pom.xml 加入核心依赖 spring-context 和测试依赖 junit

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

3.2 第二步:准备基础代码

分别创建数据层(Dao)和业务层(Service)的接口及实现类。

// 数据层接口
public interface BookDao {
    void save();
}

// 数据层实现类
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}

// 业务层接口
public interface BookService {
    void save();
}

// 业务层实现类(注意这里依然有 new,我们马上解决它)
public class BookServiceImpl implements BookService {
    // 目前还是手动 new,耦合度高,第三步会优化
    private BookDao bookDao = new BookDaoImpl();
    
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

3.3 第三步:配置 Spring 配置文件(核心)

resources 目录下新建 applicationContext.xml,把刚才那些类“托付”给 Spring 的 IOC 容器,让容器帮我们管理。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    <!-- bean标签:把一个类声明为受Spring管理的Bean -->
    <!-- id:必须唯一,是这个Bean在容器里的唯一标识 -->
    <!-- class:你这个类的全限定名 -->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"/>
</beans>

⚠️ 切记:在同一个配置文件中,Bean 的 id 不能重复,必须唯一。

3.4 第四步:启动容器,获取对象

在主类中,通过 ClassPathXmlApplicationContext 加载配置文件,获取 IOC 容器,然后从容器里拿 Bean 来用。

public class App {
    public static void main(String[] args) {
        // 1. 加载配置文件,初始化IOC容器
        ApplicationContext ctx = 
            new ClassPathXmlApplicationContext("applicationContext.xml");
            
        // 2. 从容器中根据id获取Bean
        BookService bookService = (BookService) ctx.getBean("bookService");
        
        // 3. 调用方法
        bookService.save();
    }
}

运行一下,成功输出!但你会发现,此时业务层的代码里依然用 new 创建了 Dao 对象,耦合问题并没有彻底解决。

四、进阶:用 DI 彻底消灭 new

光把对象放进 IOC 容器不够,我们还要让容器帮我们把对象“组装”起来。

操作步骤:

  1. 清空:删除 BookServiceImpl 中的 private BookDao bookDao = new BookDaoImpl();
  2. 留门:给 BookServiceImpl 提供 BookDaosetter 方法。
  3. 配置:在 XML 中告诉 Spring,bookService 里的 bookDao,帮它注入对应的 Bean。

修改后的 BookServiceImpl

public class BookServiceImpl implements BookService {
    // 不再自己new,只定义属性
    private BookDao bookDao;
    
    // 提供setter方法,Spring靠这个注入
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

修改后的 applicationContext.xml 配置(DI 注入部分):

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>

<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    <!-- 配置依赖注入 -->
    <!-- property:给当前Bean的某个属性赋值 -->
    <!-- name:填属性的名字(比如 bookDao),Spring会自动找到 setBookDao() 方法 -->
    <!-- ref:填注入哪个 Bean 的 id -->
    <property name="bookDao" ref="bookDao"/>
</bean>

知识点辨析:

  • property 中的 name:对应 BookServiceImpl 里的变量名 bookDao。Spring 拿到这个名字后,会首字母大写+补 set,变成 setBookDao,然后去调用这个方法给你注入。
  • property 中的 ref:指向 <bean id="bookDao" .../> 那个 Bean 的名字。

🎯 动手挑战:把你的 applicationContext.xml 里的 <property name="bookDao" ref="bookDao"/>,不看笔记自己敲一遍。然后试着把 ref 随便改个不存在的名字(比如 bookDao2),再运行看看会报什么错?把报错信息发在评论区,我们一起分析!

五、总结

今天我们初探了 Spring Framework,需要重点掌握:

  1. Spring 架构:五层结构,核心层是根基。
  2. IOC(控制反转):把 new 对象的权利交给 Spring 容器。
  3. DI(依赖注入):通过 setter 方法 + XML 配置,把容器中 Bean 的依赖关系组装起来。
  4. Bean 配置三要素id(唯一标识)、class(全限定类名)、property(依赖注入)。

今天我们依然保留了 Service 中的 setter 注入,这比直接 new 前进了一大步,但代码写起来还是有点繁琐。下一期,我们会继续深入 Spring 的 DI,讲讲除了 setter 注入以外,还有哪些更便捷的玩法!

📝 课后作业:不看代码,能说出 IOC 和 DI 分别解决了什么问题吗?用你自己的话把它们的核心区别写下来,加深理解。在评论区打卡,我会挑出最通俗易懂的解释置顶!


Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐