Java框架快速入门: 揭开 Spring 核心奥秘(IoC 与 DI)

本文纲要

  1. Spring 到底是什么?为什么要学它?
    1.1 Spring 的地位与优势
    1.2 Spring 家族与学习重点
    1.3 Spring Framework 的系统架构
  2. 两个核心概念:IoCDI
    2.1 传统开发中的耦合问题
    2.2 IoC:控制反转
    2.3 DI:依赖注入
  3. IoC 入门案例:让 Spring 管理对象
    3.1 项目结构一览
    3.2 第一步:导入 Spring 坐标
    3.3 第二步:创建 Spring 配置文件并声明 Bean
    3.4 第三步:获取 IoC 容器并取出 Bean
    3.5 完整代码展示与运行
  4. DI 入门案例:解耦依赖关系
    4.1 案例思路分析
    4.2 改造 Service 实现类
    4.3 在配置中绑定依赖关系
    4.4 完整代码展示与运行
  5. 总结

Spring 到底是什么?为什么要学它?

1 ) Spring 的地位与优势

在 Java 企业级开发领域,Spring 已经是事实上的标准。超过 90% 的项目都会采用 Spring 技术栈,如果你从事 Java 后端开发,Spring 几乎是一项必备技能。

它之所以如此流行,是因为它解决了两个根本性问题:

优势说明
简化开发降低企业级应用开发的复杂度,让开发者用更少的代码完成更多工作
框架整合能够无缝整合市面上主流的框架(如 MyBatis、Struts、MyBatis-Plus 等),避免因框架堆砌造成的“组合爆炸”

Spring 既能让开发变得更简单、高效,又能充当整个项目技术栈的“粘合剂”,这正是它不可替代的原因。

2 ) Spring 家族与学习重点

Spring 并非一个单一的技术,而是一个庞大的家族——常被称为 Spring 全家桶。官网将所有项目罗列在 spring.io/projects 中,其中最核心的三个项目是:

Spring 全家桶

Spring Framework

Spring Boot

Spring Cloud

底层设计框架

快速构建工具

分布式/微服务

  • Spring Framework:最早、最基础的项目,本课程的核心,提供 IoC、AOP、事务等基石能力。
  • Spring Boot:在 Spring Framework 之上进一步简化配置,用于快速搭建应用。
  • Spring Cloud:面向分布式、微服务架构的一站式解决方案。

本文的重点就是 Spring Framework,它是其它所有项目的底层支撑。

3 ) Spring Framework 的系统架构

Spring Framework 的架构经过多年演进,已非常稳定。其核心模块如下:

核心容器 Core Container

AOP 面向切面编程

数据访问与集成 Data Access/Integration

Web 层 Web

测试 Test

各模块职责简述:

模块作用
Core Container(核心容器)管理对象(Bean)的创建、初始化及生命周期,是整个框架的基础
AOP面向切面编程,在不修改原代码的情况下增强功能;整合了 AspectJ
Data Access / Integration数据层技术,包含 JDBC、事务(Transaction)以及与 MyBatis 等框架的集成
Web提供 Web 开发能力(后续 Spring MVC 课程会深入)
Test方便进行单元测试与集成测试

4 )学习路线

依据以上架构,我们推荐的掌握顺序是:

  1. 核心容器 → 掌握 IoC 和 DI 的概念与使用
  2. 数据访问与集成 → 整合 MyBatis
  3. AOP → 掌握面向切面编程
  4. 事务处理 → 基于 AOP 的声明式事务
  5. 测试 & Web 后续再聊

今天我们先扎进最核心的部分:IoCDI

两个核心概念:IoC 与 DI

1 ) 传统开发中的耦合问题

看一段普通的业务层代码:

public class BookServiceImpl implements BookService {
    private BookDao bookDao = new BookDaoImpl(); // 直接 new 具体实现 
 
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

这种做法存在明显的 高耦合 问题:如果以后想替换 BookDaoImpl 为另一个实现,就必须修改源代码,然后重新编译、测试、部署,代价很高。根本原因就是我们自己在类中 主动创建了依赖对象。

2 ) IoC:控制反转

为了解决上述问题,我们提出一个思路:对象不要由使用方主动 new,而是交给一个“外部”来提供。这种将对象创建的控制权从程序内部转移到外部的思想,就是 IoC(Inverse of Control,控制反转)。

高耦合

解耦

传统方式: 程序自己 new 对象

使用方

IoC方式: 外部提供对象

Spring 对 IoC 进行了落地实现:它提供一个 IoC 容器(又称 Spring 容器),充当那个“外部”。
容器负责创建、初始化对象,并将这些对象称为 Bean

IoC / IoC 容器 / Bean 三者的关系:

  • IoC 是一种思想
  • IoC 容器是 Spring 提供的具体实现
  • Bean 就是容器管理的对象

3 ) DI:依赖注入

仅仅管理 Bean 还不够。BookServiceImpl 在运行时仍然需要 BookDao 对象,否则会报 NullPointerException。IoC 容器既然已经管理了 BookServiceImplBookDao 两个 Bean,就可以 自动把后者注入到前者中。这种在容器中绑定 Bean 之间依赖关系的行为,称为 DI(Dependency Injection,依赖注入)。

管理

管理

DI 注入

需要

IoC 容器

BookServiceImpl Bean

BookDao Bean

依赖关系

IoC + DI 协同工作:容器不仅帮你创建对象,还会把对象需要的依赖自动注入,实现 充分解耦。

IoC 入门案例:让 Spring 管理对象

下面我们用一个简单的 BookDaoBookService 案例,演示 IoC 的具体使用。

1 ) 项目结构一览

spring01quickstart 
├── pom.xml 
└── src 
    ├── main 
    │   ├── java/com/wb 
    │   │   ├── App.java 
    │   │   ├── App2.java 
    │   │   ├── dao 
    │   │   │   ├── BookDao.java 
    │   │   │   └── impl 
    │   │   │       └── BookDaoImpl.java 
    │   │   └── service 
    │   │       ├── BookService.java 
    │   │       └── impl 
    │   │           └── BookServiceImpl.java 
    │   └── resources 
    │       └── applicationContext.xml 
    └── test 

2 ) 第一步:导入 Spring 坐标

pom.xml 中添加 spring-context 依赖:

<!-- pom.xml -->
<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>

只有导入了 Spring 相关依赖,IDEA 等工具才会提供 Spring 配置文件的模板。

3 ) 第二步:创建 Spring 配置文件并声明 Bean

src/main/resources 下新建 applicationContext.xml,编写如下内容:

<?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">
 
    <!-- 配置 Dao Bean -->
    <bean id="bookDao" class="com.wb.dao.impl.BookDaoImpl"/>
 
    <!-- 配置 Service Bean -->
    <bean id="bookService" class="com.wb.service.impl.BookServiceImpl"/>
</beans>
  • <bean> 标签:声明一个受 Spring 管理的 Bean
  • id 属性:给 Bean 起一个唯一标识,用于后续获取
  • class 属性:指定实现类的全限定名(不能是接口)

4 ) 第三步:获取 IoC 容器并取出 Bean

编写 App2.java 来测试:

package com.wb;
 
import com.wb.dao.BookDao;
import com.wb.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class App2 {
    public static void main(String[] args) {
        // 1. 获取 IoC 容器:加载配置文件 
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
 
        // 2. 获取 Bean(根据 id 获取)
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
 
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

此时运行,你会发现 BookDaosave() 正常执行,但 BookService 会抛出 NullPointerException,因为 Service 里的 bookDao 还没有注入。

5 ) 运行结果

book dao save ...
Exception in thread "main" java.lang.NullPointerException 

仅 IoC 还不够,必须配合 DI 才能让 Service 真正工作。

DI 入门案例:解耦依赖关系

1 ) 案例思路分析

要让 BookServiceImpl 能用上 BookDao 的对象,需要完成三件事:

  1. 删除 Service 实现类中 new BookDaoImpl() 的硬编码;
  2. 在 Service 实现类中 提供 setter 方法,让容器能把 Dao 对象传进来;
  3. 在配置文件中 描述 Service 与 Dao 的依赖关系。

2 ) 改造 Service 实现类

package com.wb.service.impl;
 
import com.wb.dao.BookDao;
import com.wb.service.BookService;
 
public class BookServiceImpl implements BookService {
    // 不再 new,只声明接口 
    private BookDao bookDao;
 
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
 
    // 提供 setter 方法,供容器注入 
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

3 ) 在配置中绑定依赖关系

修改 applicationContext.xml,在 Service 的 <bean> 标签内添加 <property>

<bean id="bookDao" class="com.wb.dao.impl.BookDaoImpl"/>
 
<bean id="bookService" class="com.wb.service.impl.BookServiceImpl">
    <!-- 注入依赖:name 对应属性名,ref 引用容器中的另一个 Bean -->
    <property name="bookDao" ref="bookDao"/>
</bean>
  • name:Service 中定义的属性名(bookDao
  • ref:指向已经配置的 Bean 的 id(同样是 bookDao,但含义不同)
  • Spring 容器会自动调用 setBookDao() 方法,将对应的 Bean 传入。

4 ) 完整代码展示与运行

再次运行 App2

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    BookService bookService = (BookService) ctx.getBean("bookService");
    bookService.save();
}

运行结果:

book service save ...
book dao save ...

到此,Service 与 Dao 实现了完全解耦,所有对象都由 IoC 容器管理,依赖关系由容器注入

总结

通过本篇文章,我们完成了 Spring 中最基础、最核心的 IoCDI 入门:

  • IoC(控制反转):将对象的创建权交给容器,而不是由程序自身 new。
  • DI(依赖注入):容器自动为 Bean 注入它所需要的其他 Bean。
  • 入门实践:
    1. 导入 spring-context 依赖;
    2. applicationContext.xml 中声明 Bean;
    3. 通过 ApplicationContext 获取容器,再通过 getBean() 拿到对象;
    4. 使用 <property> + setter 方法实现依赖注入。

掌握这两个概念之后,后续学习 AOP、事务、整合 MyBatis 等内容都会更加顺畅。
Spring 的世界刚刚打开,下一站我们将深入 Bean 配置与生命周期。欢迎持续关注!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值