一、起源:Spring 解决什么问题

Spring 诞生于 EJB 2.x 主导的 Java EE 时代。当时的企业级开发存在四个根本矛盾:

矛盾 EJB 2.x 的实现方式 实际痛点
业务代码的侵入性 业务类必须实现 EJB 接口(SessionBeanEntityBean),继承容器基类 业务代码与容器强绑定,脱离 EJB 容器无法独立运行,单元测试需要启动完整容器,耗时可达数十分钟
依赖关系的管理 依赖通过 JNDI 查找写在业务方法内部,如 ctx.lookup("jdbc/MyDB") 类之间的依赖关系散落在代码各处,耦合关系不可见,修改依赖需要改动业务代码并重新编译
横切逻辑的重复 事务开启提交、安全校验、日志记录等逻辑以模板代码方式写入每个方法 同一横切关注点散布在数百个业务方法中,新增一个监控维度需要批量修改代码
配置与运行的复杂度 ejb-jar.xml 配合各厂商专有描述符,类加载层级深 新手入门周期长,排障需要理解容器内部机制,小团队难以承受维护成本

Spring 针对上述矛盾给出的对应解法是两个核心思想:

  • 控制反转(IoC)依赖注入(DI):将对象的创建权与依赖关系从业务代码剥离,交给容器管理,使业务对象回归纯粹的 POJO(Plain Old Java Object),不依赖任何框架 API。
  • 面向切面编程(AOP):将散布在业务方法中的横切逻辑提取为独立模块,以声明方式织入目标方法,业务方法不再感知切面的存在。

二、控制反转与依赖注入

2.1 IoC 的本质

IoC(Inversion of Control)不是一种具体技术,而是一种设计思想:对象不再主动创建自己的依赖,而是声明所需依赖的抽象,由外部实体把依赖注入进来。控制权从业务代码侧转移到了容器侧,故称"控制反转"。

传统方式与 IoC 方式的对比:

传统方式(对象自己找依赖):
  UserService(调用方)
    └── new UserDao()  ──→ 直接创建具体实现,耦合 UserDao

IoC 方式(容器送来依赖):
  容器(持有所有对象实例)
    ├── UserService(仅声明:我需要一个 UserDao)
    └── UserDao(容器先创建它,再注入给 UserService)

2.2 DI 的三种注入方式

DI(Dependency Injection)是 IoC 思想的具体实现手段。注入方式分三种:

注入方式 执行机制 适用定位
构造器注入 依赖通过构造方法参数传入,对象实例化时依赖即完整 强制依赖与不可变依赖,Spring 官方推荐优先采用
Setter 注入 依赖通过 setter 方法赋值,对象创建完成后可重新设置 可选依赖、需要运行时动态变更的依赖
字段注入 通过反射直接对字段赋值,典型注解为 @Autowired 快速开发场景;不利于编写纯单元测试(需要容器或反射手段才能注入)

构造器注入能够保证对象完全初始化后才能被使用,且依赖字段可声明为 final,天然具备不可变性。从 Spring 4.3 起,单构造器的类即使不加 @Autowired 也会被容器自动识别注入。

2.3 与依赖倒置原则的关系

DI 的理论根源是面向对象七大原则中的 依赖倒置原则(DIP)

  1. 高层模块不应依赖低层模块,两者都应依赖抽象
  2. 抽象不应依赖细节,细节应当依赖抽象

DI 通过容器把具体实现注入给面向抽象的消费方,将代码间的硬编码依赖替换为抽象接口的关系。业务代码只知道"我需要一个 UserDao 接口",具体实现是 JdbcUserDaoHibernateUserDao 还是 MyBatisUserMapper,由容器在装配阶段决定。

依赖倒置原则的详细说明见 面向对象编程 中 DIP 章节。


三、面向切面编程

3.1 横切关注点

在一个典型的业务系统中,以下逻辑通常横跨大量业务方法:

  • 事务管理(方法前开事务、结束提交、异常回滚)
  • 安全校验(方法前校验当前用户权限)
  • 日志记录(方法前后记录入参、耗时、异常)
  • 性能监控(上报执行耗时)
  • 异常兜底(捕获异常后返回降级结果)
  • 幂等校验(方法前校验请求是否重复)

这类逻辑在业务层面与核心功能无关,但在技术层面又必须执行,称之为 横切关注点(cross-cutting concern)。如果直接写进每个业务方法,会造成代码重复和耦合——调整某个横切逻辑需要修改全部涉及的方法。

AOP(Aspect-Oriented Programming)的目标就是将横切逻辑从业务方法中剥离,集中封装为独立模块,再以声明方式织入需要被干预的方法集合。业务代码本身不感知切面存在。

3.2 核心术语体系

术语 含义 举例
Aspect(切面) 封装横切逻辑的独立模块 事务切面、日志切面、权限切面
JoinPoint(连接点) 切面可介入的执行位置 方法调用、字段访问、构造调用、异常抛出。Spring AOP 仅支持"方法执行"一种连接点
Pointcut(切入点) 一组 JoinPoint 的匹配规则,决定切面作用于哪些位置 execution(* com.example.service.*.*(..)) 匹配 service 包下所有方法
Advice(通知) 在 JoinPoint 执行的具体逻辑,以及执行时机 Before(前置)、AfterReturning(成功返回)、AfterThrowing(抛出异常)、Around(环绕,可控制是否执行原方法)、After(无论成功失败都执行)
Weaving(织入) 将切面逻辑融入目标对象生命周期的过程 编译期(AspectJ 编译器)、类加载期(AspectJ Agent)、运行期(Spring AOP 的动态代理)
Introduction(引介) 在不修改原有类代码的前提下,为其在运行时增加新方法或接口 为任意 Bean 动态添加一个 Monitorable 接口,暴露监控方法

3.3 动态代理与内部调用失效

Spring AOP 采用运行期织入,通过为目标对象生成代理类实现增强。代理分两种:

代理方式 原理 触发条件
JDK 动态代理 基于 java.lang.reflect.Proxy,为目标类实现的接口生成代理类 目标类至少实现一个接口
CGLIB 代理 基于 ASM 字节码框架,为目标类生成子类并重写非 final 的 public/protected 方法 目标类本身非 final,被代理方法非 final、非 static。Spring Boot 2.x 起默认采用 CGLIB

两种代理存在共通的约束:目标类内部的 this 调用不会经过代理。例如 A 类的 method1() 调用自身的 method2()method2() 上的 @Transactional@Async 不会生效,原因是调用链直接走目标对象自身,跳过了代理对象的拦截层。

常见的处理方式有三种:自注入本类 Bean、通过 ApplicationContext.getBean() 获取代理再调用、使用 AopContext.currentProxy() 取得当前代理对象。

反射、动态代理如何支撑起 Spring IoC 与 AOP 的运行时基础,见 反射


四、Bean 生命周期契约

Spring 容器对 Bean 的管理遵循完整的生命周期契约。开发者可以通过接口、注解或配置在各个阶段插入自定义逻辑。Bean 从创建到销毁的完整流程如下:

启动容器,加载 BeanDefinition
  ↓
  Bean 实例化阶段
    ├── InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
    │     (有机会在实例化前返回代理对象,短路后续流程)
    └── 调用构造器或工厂方法,得到原始 Bean 实例
  ↓
  属性填充阶段
    ├── InstantiationAwareBeanPostProcessor.postProcessPropertyValues
    │     (在此阶段可修改属性值,如 @Value/@Autowired 解析)
    └── 依赖注入:setter/字段注入完成
  ↓
  感知回调阶段
    ├── BeanNameAware.setBeanName
    ├── BeanClassLoaderAware.setBeanClassLoader
    ├── BeanFactoryAware.setBeanFactory
    ├── EnvironmentAware / EmbeddedValueResolverAware
    ├── ResourceLoaderAware / ApplicationEventPublisherAware
    ├── MessageSourceAware / ApplicationContextAware
    └── ServletContextAware(Web 环境)
  ↓
  初始化前处理
    └── BeanPostProcessor.postProcessBeforeInitialization
        (@PostConstruct、@Autowired、@Value 等注解的处理在此之前已完成,
         此阶段可对 Bean 做进一步包装)
  ↓
  初始化回调
    ├── @PostConstruct(CommonAnnotationBeanPostProcessor 处理)
    ├── InitializingBean.afterPropertiesSet
    └── init-method(XML 或 @Bean(initMethod) 指定)
  ↓
  初始化后处理(AOP 代理在此阶段生成)
    └── BeanPostProcessor.postProcessAfterInitialization
        (AnnotationAwareAspectJAutoProxyCreator 判断 Bean 是否需要被切,
         需要则返回代理对象替代原始 Bean)
  ↓
Bean 就绪,可被使用
  ↓
容器关闭
  ↓
  销毁回调
    ├── @PreDestroy
    ├── DisposableBean.destroy
    └── destroy-method(XML 或 @Bean(destroyMethod) 指定)

生命周期中各回调方式的执行顺序遵循"注解优先、接口其次、配置最后"的原则。@PostConstructInitializingBean 先执行,@PreDestroyDisposableBean 先执行。


五、IoC 容器分层

Spring 的容器从下至上分为两层,分别对应不同的使用场景。

5.1 BeanFactory

BeanFactory 是最底层的容器接口,核心职责仅围绕 Bean 的定义、创建、依赖注入和生命周期管理:

  • 典型实现为 DefaultListableBeanFactory,同时实现了 BeanDefinitionRegistry 接口,具备 BeanDefinition 的注册能力
  • 懒加载策略:仅在调用 getBean() 时才真正实例化 Bean,启动速度快、内存占用低
  • 不具备企业级能力:不自动注册 BeanPostProcessor、不支持国际化、没有事件机制
  • 常见于资源受限的嵌入式场景,或作为构建上层容器的内部组件

5.2 ApplicationContext

ApplicationContext 在 BeanFactory 的基础上扩展了企业级特性,是日常开发中主要使用的容器形态:

扩展能力 说明
后处理器自动识别 自动识别并注册 BeanFactoryPostProcessor 和 BeanPostProcessor 类型的 Bean
预实例化 容器启动时即完成所有非懒加载单例 Bean 的创建,启动时即可暴露配置错误
国际化 通过 MessageSource 接口提供多语言消息解析
事件发布 基于 ApplicationEventPublisher 的观察者模式事件机制
资源抽象 统一的资源加载接口,支持 classpath、文件系统、URL 等多种来源
环境抽象 Environment 统一管理 profile 与属性源(properties/yml/环境变量/命令行参数)

常用的 ApplicationContext 实现:

实现类 配置来源 典型场景
ClassPathXmlApplicationContext classpath 下的 XML 文件 传统 XML 配置的遗留系统
FileSystemXmlApplicationContext 文件系统路径下的 XML 文件 外部配置化加载
AnnotationConfigApplicationContext @Configuration 注解类 + 包扫描 Spring 注解驱动的纯 Java 配置应用
AnnotationConfigServletWebServerApplicationContext Spring Boot Web 应用 内嵌 Servlet Web 容器,Spring Boot Web 默认上下文
AnnotationConfigReactiveWebServerApplicationContext Spring Boot WebFlux 应用 内嵌响应式 Web 服务器

六、BeanDefinition:Bean 的配方

BeanDefinition 是 IoC 容器的核心抽象,代表一个 Bean 的"配方"。容器在真正实例化 Bean 之前,首先将各种来源的配置解析为 BeanDefinition 对象集合,然后统一基于配方创建 Bean。

BeanDefinition 中包含的关键元信息:

属性 含义
beanClass Bean 的全限定类名,运行时通过反射创建实例
scope 作用域(singleton / prototype / request / session 等)
constructorArgumentValues 构造器参数列表,用于构造器注入
propertyValues 属性值列表,用于 setter 注入
initMethodName / destroyMethodName 自定义初始化与销毁方法名
lazyInit 是否懒加载(仅对单例 Bean 生效)
dependsOn 创建该 Bean 前必须先完成初始化的其他 Bean
autowireMode 自动装配模式(按名称 / 按类型 / 构造器)
factoryBeanName / factoryMethodName 工厂 Bean 名称与工厂方法名(用于非直接构造的 Bean)

这一设计实现了"配置来源"与"创建逻辑"的彻底解耦:XML 解析、注解扫描、@Bean 方法处理、Groovy DSL 等只是不同的 BeanDefinition 读取器,最终产出的 BeanDefinition 结构完全一致,底层创建逻辑无需关心配置来源。


七、作用域体系

Spring 为 Bean 定义了多种作用域,决定 Bean 实例的存在范围与创建时机:

作用域 实例数量与生命周期 典型使用场景
singleton(默认) 整个容器内一个 Bean 定义对应一个实例 无状态服务、工具类、配置对象、数据源
prototype 每次获取 Bean 都创建新实例 有状态对象、每次调用需要独立上下文的组件
request 一次 HTTP 请求内一个实例 Web 应用中与单次请求绑定的上下文对象
session 一个 HTTP Session 内一个实例 用户级购物车、会话级临时缓存
application 整个 ServletContext 生命周期内一个实例 Web 应用级全局共享对象
websocket 一条 WebSocket 连接生命周期内一个实例 WebSocket 会话级上下文

自定义作用域通过实现 Scope 接口并注册到容器中完成。Spring Cloud 基于自定义作用域实现了 refresh(配置刷新作用域)和 request 的扩展版本。


八、两大扩展点:BeanFactoryPostProcessor 与 BeanPostProcessor

这两个后处理器接口是 Spring 具备高扩展性的根本原因。它们分别作用于 BeanDefinition 阶段和 Bean 实例阶段。

8.1 BeanFactoryPostProcessor

作用时机:BeanDefinition 全部加载完成后、任何 Bean 实例化开始之前。

作用对象:BeanDefinition 的元信息,可以修改、新增、删除 BeanDefinition。

典型应用:

后处理器 功能
PropertySourcesPlaceholderConfigurer 将 BeanDefinition 中 ${key} 占位符替换为属性源中的实际值
ConfigurationClassPostProcessor 解析 @Configuration@Bean@ComponentScan@Import,产出对应 BeanDefinition
MapperScannerConfigurer 扫描指定包下的 MyBatis Mapper 接口,注册为 MapperFactoryBean 类型的 BeanDefinition

8.2 BeanPostProcessor

作用时机:Bean 实例化完成并填充属性后,在初始化回调前后各执行一次。

作用对象:已经实例化的 Bean 实例,可以做包装、修改、甚至替换为代理。

典型应用:

后处理器 功能
AutowiredAnnotationBeanPostProcessor 处理 @Autowired@Value,完成依赖注入
CommonAnnotationBeanPostProcessor 处理 @Resource@PostConstruct@PreDestroy
AnnotationAwareAspectJAutoProxyCreator 匹配 @Aspect 切面,为目标 Bean 生成 AOP 代理对象
AsyncAnnotationBeanPostProcessor @Async 标注的方法生成异步执行代理

九、声明式事务

9.1 事务抽象

Spring 的事务体系建立在 PlatformTransactionManager 抽象之上,不同底层技术对应不同实现:

底层技术 事务管理器实现
JDBC / MyBatis DataSourceTransactionManager
Hibernate 5 HibernateTransactionManager
JPA JpaTransactionManager
JTA 分布式事务 JtaTransactionManager(配合 Atomikos、Bitronix 等)

业务代码通过声明式注解使用事务,不直接依赖具体实现。

9.2 传播行为

传播行为定义了当一个事务方法被另一个事务方法调用时,事务边界如何组合。@Transactional(propagation = ...) 支持七种传播行为:

传播行为 行为描述 典型场景
REQUIRED(默认) 已有事务则加入,没有则新开事务 绝大多数 CRUD 操作
REQUIRES_NEW 始终新开独立事务,外层事务挂起 审计日志、积分记录等必须成功的子操作,外层回滚不影响它
SUPPORTS 有事务则加入,没有则非事务执行 查询类方法
NOT_SUPPORTED 总是以非事务方式执行,外层事务挂起 发消息、写日志等无需事务参与的操作
MANDATORY 必须在已有事务中调用,否则抛出异常 必须作为大事务一部分的内部子方法
NEVER 必须在非事务环境调用,否则抛出异常 DDL 语句、纯非事务操作
NESTED 有外层事务则开启保存点嵌套,没有则等效 REQUIRED 子操作失败仅回滚子操作,不影响外层主事务(需要 JDBC 驱动支持 Savepoint)

9.3 回滚规则与注意事项

  • 默认回滚条件:方法抛出 RuntimeExceptionError 时回滚;受检异常(Exception 及其子类,非 RuntimeException)不回滚
  • 自定义回滚:rollbackFor = Exception.class 可扩大回滚范围;noRollbackFor 可指定某些异常不回滚
  • @Transactional 仅在 public 方法上生效(动态代理只能拦截 public 方法)
  • 同类内部方法调用不触发事务(同 AOP 内部调用失效的原因)
  • 多数据源场景需要显式指定对应的 transactionManager,否则使用默认主数据源

十、事件机制

Spring 的事件机制基于观察者模式,是模块间解耦通知的轻量级手段。

10.1 核心角色

角色 接口/类 作用
事件 ApplicationEvent 抽象类 自定义事件的基类,携带事件源与业务数据
发布者 ApplicationEventPublisher 接口 发布事件,ApplicationContext 本身即为发布者
监听者 ApplicationListener<E> 接口或 @EventListener 注解 接收并处理匹配类型的事件

10.2 使用流程

1. 自定义事件继承 ApplicationEvent,携带业务数据
2. 需要时调用 applicationEventPublisher.publishEvent(customEvent)
3. 监听方通过 @EventListener 标注方法,参数类型决定接收的事件
4. 若监听方法需要处理后续事件的发布顺序,可使用 @Order 控制执行顺序

默认情况下事件同步发布,发布线程会阻塞直到所有监听器执行完成。如需异步处理,可在监听方法上追加 @Async 注解(需启用异步支持),或为 SimpleApplicationEventMulticaster 配置线程池。


十一、Spring 生态全景

Spring Framework 本身是 IoC、AOP、上下文与基础设施的集合。围绕该核心,Spring 生态以子项目形式覆盖了企业开发的各类场景:

子项目 定位 核心能力
Spring Core / Beans 核心容器 Bean 工厂、依赖注入、资源加载、工具类
Spring Context 上下文层 注解驱动、国际化、事件机制、属性解析
Spring AOP 切面基础设施 JDK/CGLIB 代理、切入点表达式、AspectJ 集成
Spring JDBC / DAO 数据访问基础 JdbcTemplate 模板方法、异常转译、DataSource 管理
Spring ORM ORM 集成 Hibernate / JPA 集成,统一事务管理
Spring Transaction 事务抽象 声明式事务、传播行为、多数据源事务管理
Spring Web 基础 Web 支持层 WebApplicationContext、Multipart 解析、远程调用封装
Spring MVC 传统 Web MVC DispatcherServlet、注解驱动 Controller、视图解析、参数绑定
Spring WebFlux 响应式 Web 非阻塞 IO、Reactor 编程模型、Netty/Servlet 3.1 支持
Spring Security 安全框架 认证授权、会话管理、CSRF/XSS 防御、OAuth2
Spring Data 数据访问统一 统一 Repository 抽象,适配 JPA/MongoDB/Redis/ES/Cassandra 等
Spring Integration 企业集成 EIP 模式实现、消息通道、端点适配器、外部系统连通
Spring Batch 离线批处理 Job/Step 管道、读-处理-写三步模型、重启与跳过策略
Spring AMQP / Kafka 消息集成 与 RabbitMQ / Apache Kafka 的生产消费封装
Spring for GraphQL GraphQL 支持 服务端 Query/Mutation 处理、订阅、Schema 解析
Spring Boot 开发脚手架 自动装配、内嵌容器、Starter 依赖、约定优于配置
Spring Cloud 分布式治理 注册发现、配置中心、网关、熔断器、链路追踪、分布式事务基础

Spring Boot 作为脚手架层的整体架构、自动装配原理、Fat JAR 结构与启动时序,见 Spring Boot 主脉络;具体的 Starter 选型、配置系统与环境隔离,见 Spring Boot Starter 汇总application.yml 核心配置Profiles 环境隔离

Spring MVC 与 WebFlux 在 Java Web 体系中的位置见 Java Web 技术体系


十二、总结

Spring Framework 的出现重新定义了 Java 企业级开发的范式。在它之前,业务代码被容器规范侵入、依赖关系隐藏在 JNDI 查找中、横切逻辑散落在各处。Spring 通过 IoC 与 AOP 两项核心思想,让业务对象回归 POJO,让依赖关系从不可见变为显式声明,让横切逻辑从分散变为集中管理。

支撑这一设计落地的是层层递进的机制:BeanDefinition 将配置来源与创建逻辑解耦,BeanFactory 与 ApplicationContext 提供了从简到全的两档容器能力,BeanFactoryPostProcessor 与 BeanPostProcessor 两个扩展点构成了 Spring 极高扩展性的骨架,生命周期契约为开发者提供了可插入的标准钩子。

在这个核心之上,Spring 生态以子项目的形式向外延伸,从数据访问、事务、Web 到安全、集成、批处理,再到 Spring Boot 的脚手架化与 Spring Cloud 的分布式治理。每一层子项目都建立在同一套容器抽象之上,共享一致的配置模型与扩展规范,使得 Spring 成为 Java 领域覆盖面最广的开发框架。