Spring 框架备忘清单

本备忘单旨在快速梳理 Spring 框架的核心概念,从 IoC/DI 基础到 Bean 的高级管理,提供关键配置、代码示例和原理剖析,以便快速参考。

Spring 核心思想

设计原则

Spring 框架的设计深受两大设计原则的影响,其共同目标是 解耦

  • 开闭原则 (OCP): 软件实体(类、模块等)应该对扩展开放,对修改关闭。新增功能应通过添加新代码实现,而非修改旧代码。
  • 依赖倒置原则 (DIP): 倡导面向抽象(接口)编程,而不是面向具体实现编程。高层模块不应依赖底层模块,二者都应依赖于抽象。

控制反转 (IoC)

控制反转 (Inversion of Control) 是一种核心设计思想,旨在降低代码耦合度。

  • 核心理念: 将对象的 创建权 和对象之间关系的 管理权 从业务代码中移交出去,由一个独立的第三方容器来负责。
  • 实现方式: 依赖注入 (DI) 是实现 IoC 最重要的方式。

依赖注入 (DI)

依赖注入 (Dependency Injection) 是 IoC 思想的具体实现。容器会主动将某个对象所依赖的其他对象(或值)“注入”到该对象中。

  • Setter 注入: 通过调用 setXxx() 方法注入依赖,最常用、最灵活。
  • 构造器注入: 在实例化时通过构造方法注入依赖,能保证依赖在对象创建时就绪。

框架概述

Spring 6.x 核心模块

模块 (Module)核心功能描述
Spring Core(框架基石) 实现了控制反转 (IoC),是所有功能的基础。
Spring AOP(切面编程) 提供完整的面向切面编程支持,用于解耦业务与横切关注点。
Spring DAO(数据访问) 对原生 JDBC 进行了抽象和封装,简化了数据访问代码。
Spring ORM(对象关系映射) 提供对 MyBatis、Hibernate 等主流 ORM 框架的集成支持。
Spring Context(应用上下文) 提供国际化、事件传播、JNDI 等企业级服务,是框架功能的延伸。
Spring Web MVC(Web 框架) Spring 自带的、成熟的 MVC 框架,用于开发传统的 Servlet Web 应用。
Spring Webflux(响应式 Web) 完全异步、非阻塞的响应式 Web 框架,适用于高并发场景。
Spring Web(集成支持) 用于集成 Struts 等早期的第三方 Web 框架(较少使用)。

核心特性

核心特性说明
轻量无论是 JAR 包大小还是运行时资源开销都非常小。
非侵入式业务代码不依赖于 Spring 的特定 API,易于复用和测试。
控制反转 (IoC)Spring 的灵魂,通过 IoC 技术促进松耦合。
面向切面 (AOP)允许将业务逻辑与系统服务(如事务、日志)优雅地分离。
容器负责管理对象的配置、创建、装配及其完整的生命周期。
框架将简单的组件通过声明式(XML 或注解)的方式组合成复杂的应用。

入门实践 (XML)

1. 引入核心依赖

对于基础 IoC,仅需引入 spring-context 依赖。

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.2.9</version>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.13.4</version>
    <scope>test</scope>
</dependency>

2. 定义 Bean

Bean 是由 Spring IoC 容器管理的对象,本质上是 POJO。

// src/main/java/com/example/spring6/bean/User.java
public class User {
    public User() {
        // Spring 默认通过无参构造器实例化
        System.out.println("User 的无参数构造方法执行。");
    }
}

3. 创建 Spring 配置文件

XML 文件用于告知 Spring 容器需要管理哪些 Bean。

<!-- src/main/resources/beans.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">

    <bean id="userBean" class="com.example.spring6.bean.User"/>

</beans>

4. 启动容器并获取 Bean

通过 ApplicationContext 启动 Spring 容器并获取 Bean 实例。

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

// 1. 创建 Spring 容器对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

// 2. 从容器中获取 Bean
// 方式一:返回 Object,需要强转
Object userBean = context.getBean("userBean");
// 方式二:无需强转 (推荐)
User user = context.getBean("userBean", User.class);

IoC 工作机制要点

  • 唯一 ID: 同一个 XML 配置文件中,<bean>id 必须唯一。
  • 实例化: Spring 默认通过 反射机制 调用类的 无参构造函数 来创建对象。
  • Bean 存储: 容器内部维护一个类似 Map<String, Object> 的结构(单例池)来存放单例 Bean。
  • BeanFactory vs ApplicationContext: BeanFactory 是 IoC 容器的顶级接口,定义了基础功能。ApplicationContext 是其子接口,功能更强大,增加了对国际化、事件发布、AOP 等企业级特性的支持,是首选。

依赖注入 (DI)

Setter 注入

通过调用 setXxx() 方法注入依赖。

注入其他 Bean (ref)

<bean id="userDaoBean" class="com.example.spring6.bean.UserDao"/>

<bean id="userServiceBean" class="com.example.spring6.bean.UserService">
    <!-- `name` 对应属性名 `userDao` -->
    <!-- `ref` 引用另一个 bean 的 id -->
    <property name="userDao" ref="userDaoBean"/>
</bean>

注入字面量 (value)

<bean id="userBean" class="com.example.spring6.bean.User">
    <property name="name" value="Prorise"/>
    <property name="age" value="25"/>
</bean>

注入 null 和特殊字符

<property name="name"><null/></property>

<property name="expression">
    <value><![CDATA[ a < b && b > c ]]></value>
</property>

构造器注入

通过构造方法在实例化时注入依赖。

<bean id="userDaoBean" class="com.example.spring6.bean.UserDao"/>

<bean id="userServiceBean" class="com.example.spring6.bean.UserService">
    <constructor-arg name="userDao" ref="userDaoBean"/>
</bean>

注入集合类型

注入 List 和 Set

<property name="interests">
    <list>
        <value>编程</value>
        <value>游戏</value>
    </list>
</property>

<property name="phones">
    <set>
        <value>13888888888</value>
    </set>
</property>

注入 Map 和 Properties

<property name="family">
    <map>
        <entry key="father" value="老王"/>
        <entry key="mother" value="老李"/>
    </map>
</property>

<property name="dbConfig">
    <props>
        <prop key="driver">com.mysql.cj.jdbc.Driver</prop>
    </props>
</property>

外部化配置

将易变配置(如数据库连接)移至 .properties 文件。

  1. 创建 jdbc.properties
    db.driver=com.mysql.cj.jdbc.Driver
    db.url=jdbc:mysql://localhost:3306/testdb
    db.username=root
    db.password=123456
    
  2. 在 XML 中加载并使用
    <!-- 引入 context 命名空间 -->
    <beans xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="...
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 1. 加载属性文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    
        <!-- 2. 使用 ${key} 占位符引用值 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${db.driver}"/>
            <property name="url" value="${db.url}"/>
            <property name="username" value="${db.username}"/>
            <property name="password" value="${db.password}"/>
        </bean>
    </beans>
    

p/c 命名空间 (了解即可)

用于简化 XML 配置,现代开发多用注解代替。

<!-- 引入 p 和 c 命名空间 -->
<beans xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c" ...>

    <!-- p 命名空间简化 setter 注入 -->
    <bean id="user" class="...User" p:name="Prorise" p:age="25"/>

    <!-- c 命名空间简化构造器注入 (按名称) -->
    <bean id="service" class="...UserService" c:userDao-ref="userDaoBean"/>
</beans>

Bean 的高级管理

Bean 的作用域 (Scope)

scope 属性决定了 Spring 如何创建 Bean 的实例。

作用域描述创建时机
singleton (默认)在整个 IoC 容器中,只有一个共享实例。容器启动时创建。
prototype每次请求(getBean)都会创建一个新实例。请求时创建(懒加载)。

XML 配置:

<bean id="sb" class="...SpringBean" scope="prototype"/>

注解配置 (推荐):

@Component
@Scope("prototype")
public class SpringBean { ... }

Bean 的实例化方式

方式描述XML 配置
构造方法 (默认)通过调用类的无参构造器创建。<bean class="...User"/>
静态工厂方法调用一个类的静态方法创建。<bean class="...Factory" factory-method="getInstance"/>
实例工厂方法调用一个已存在 Bean 实例的方法创建。<bean factory-bean="factoryId" factory-method="create"/>
FactoryBean 接口实现 FactoryBean 接口,封装复杂创建逻辑。<bean id="product" class="...ProductFactoryBean"/>

FactoryBean 示例: FactoryBean 是一个特殊的 Bean,它的作用是生产另一个 Bean。当从容器获取它时,得到的是其 getObject() 方法的返回值。

public class MyFactoryBean implements FactoryBean<Product> {
    @Override
    public Product getObject() throws Exception {
        // ... 复杂的创建逻辑
        return new Product();
    }
    // ...
}
<!-- 获取 "myProduct" 时,实际调用的是 MyFactoryBean 的 getObject() 方法 -->
<bean id="myProduct" class="...MyFactoryBean"/>

Bean 的生命周期

一个 Bean 从创建到销毁会经历一系列预定义的阶段。

核心生命周期(七步)

  1. 实例化: 通过构造方法创建 Bean 的实例。
  2. 属性赋值: Spring 通过 DI 为 Bean 的属性注入值。
  3. 后置处理器前置处理: 调用 BeanPostProcessorpostProcessBeforeInitialization 方法。
  4. 初始化: 调用开发者自定义的初始化方法(如 init-method@PostConstruct)。
  5. 后置处理器后置处理: 调用 BeanPostProcessorpostProcessAfterInitialization 方法。
  6. 使用 Bean: Bean 处于可用状态。
  7. 销毁: 容器关闭时,调用自定义的销毁方法(如 destroy-method@PreDestroy)。

生命周期回调配置

方式描述示例
XML init/destroy-method<bean> 中指定初始化和销毁方法名。<bean ... init-method="init" destroy-method="destroy"/>
@PostConstruct / @PreDestroy (推荐)JSR-250 标准注解,无代码侵入。@PostConstruct public void init() {}
BeanPostProcessor对容器中所有 Bean 的初始化前后进行统一处理。class MyBPP implements BeanPostProcessor { ... }

原理剖析

循环依赖

指两个或多个 Bean 相互持有对方的引用,形成闭环 (A -> B -> A)。

解决能力总结

注入方式作用域是否能解决
Setter 注入singleton可以
Setter 注入prototype不可以
构造器注入singleton不可以

解决原理:三级缓存

Spring 仅能解决单例 Bean 的 Setter 注入循环依赖,其核心是三级缓存机制:

  • 一级缓存 singletonObjects: 存放完全初始化好的 Bean 成品。
  • 二级缓存 earlySingletonObjects: 存放已实例化但未填充属性的 Bean 半成品。
  • 三级缓存 singletonFactories: 存放能生产半成品 Bean 的工厂 (ObjectFactory)。

解决流程:

  1. 创建 A,实例化后将其工厂放入三级缓存。
  2. 填充 A 的属性,发现依赖 B,去创建 B。
  3. 创建 B,实例化后将其工厂也放入三级缓存。
  4. 填充 B 的属性,发现依赖 A,从三级缓存找到 A 的工厂,生成半成品 A 并放入二级缓存。
  5. B 成功注入半成品 A,完成初始化,成为成品放入一级缓存。
  6. A 成功注入成品 B,完成初始化,也成为成品放入一级缓存。问题解决。

工厂设计模式

BeanFactory vs FactoryBean

这是一个经典面试题,两者名称相似但维度完全不同。

  • BeanFactory (是工厂):

    • Spring IoC 容器的顶级接口,是 Spring 框架的“心脏”。
    • 它是管理所有 Bean 的“总工厂”,定义了获取和管理 Bean 的规范。
    • ApplicationContext 是它的一个功能更强大的子接口。
  • FactoryBean (是 Bean):

    • 它是一个特殊的 Bean,被 BeanFactory 管理。
    • 它的作用是作为一个“零件工厂”,去生产另一个 Bean。
    • 获取 id="myBean"FactoryBean 时,得到的是其 getObject() 的返回值。
    • 要获取 FactoryBean 本身,需使用 getBean("&myBean")

现代 Spring 开发 (注解)

现代 Spring/Spring Boot 开发全面拥抱基于注解的配置,替代了繁琐的 XML。

核心注解

注解作用
@Component通用的 Bean 声明注解。
@Service用于标注业务逻辑层的组件。
@Repository用于标注数据访问层的组件。
@Controller用于标注 Web 控制器层的组件。
@Configuration声明一个类为配置类,替代 XML 文件。
@Bean@Configuration 类的方法上使用,将方法返回值注册为一个 Bean。
@Autowired按类型自动注入依赖。
@Qualifier("beanId")@Autowired 配合,按名称指定要注入的 Bean。
@Value("${key}")注入外部配置文件中的值。
@Scope("prototype")指定 Bean 的作用域。
@Lazysingleton Bean 进行懒加载。
@PostConstruct声明初始化回调方法。
@PreDestroy声明销毁回调方法。

Java 配置示例

使用 @Configuration@Bean 完全替代 XML,实现类型安全、易于重构的配置。

@Configuration // 声明这是一个配置类
public class AppConfig {

    @Bean // 方法名 "userDao" 就是 bean 的 id
    public UserDao userDao() {
        return new UserDaoImpl();
    }

    @Bean("userService") // 也可以自定义 bean 的 id
    public UserService userService(UserDao userDao) { // Spring 会自动注入上面定义的 userDao
        UserServiceImpl service = new UserServiceImpl();
        service.setUserDao(userDao); // 编程方式进行依赖注入
        return service;
    }
}

另见