Spring Framework 7.0.9 深度剖析-01-整体架构与设计哲学
1. 本章导读
Spring Framework 7.0.9 是一个历经二十余年演进的成熟框架。很多开发者每天都在用 @SpringBootApplication 启动应用,却很少有人回答清楚这样几个问题:为什么 spring-beans 只能依赖 spring-core?为什么 ApplicationContext 要同时"继承"六个接口?为什么单例注册表在 7.0 里把 synchronized 换成了 ReentrantLock?DispatcherServlet 收到一个 HTTP 请求后,凭什么知道该调用哪个 Controller、用哪个视图渲染?
本章不急于钻进某一个具体机制的细节,而是站在仓库全景的高度,回答"骨架"问题:
- 项目概貌实证 :用脚本实测 27 个 Gradle 子模块的
src/main/java规模(.java文件数与代码行数),用真实数字说明哪些模块是"重量级核心"、哪些是"薄适配层"。 - 模块分层与依赖规则 :依据
settings.gradle的 include 清单与各模块build.gradle的dependencies块,画出分层 ASCII 图,讲清为什么spring-beans只许依赖spring-core、为什么spring-web可以在没有spring-context的情况下独立使用。 - 核心抽象接口链 :逐个读真实接口文件,讲清
BeanFactory → ListableBeanFactory → HierarchicalBeanFactory → ConfigurableBeanFactory → ConfigurableListableBeanFactory这条链上每一层"多暴露了什么",以及ApplicationContext为什么要多继承MessageSource、ApplicationEventPublisher、ResourcePatternResolver等六个角色。 - 设计哲学落到源码 :模板方法(
AbstractApplicationContext.refresh、AbstractAutowireCapableBeanFactory.doCreateBean、AbstractMessageSource)、策略(HandlerMapping/HandlerAdapter/ViewResolver、TaskExecutor)、观察者(事件多播器)、组合与适配器(Resource体系、ProxyFactory)、约定优于配置(@Configuration/@ComponentScan)、扩展点优先(BeanPostProcessor/BeanFactoryPostProcessor),每一种都贴真实源码并逐段讲解。 - 7.0.9 版本痕迹 :JDK 工具链要求(构建用 JDK 25、字节码基线 release 17)、
VirtualThreadTaskExecutor是否存在、DefaultSingletonBeanRegistry的singletonLock替换旧synchronized、日志桥接从 spring-jcl 迁到org.springframework.core.log等------只写仓库里有证据的差异。 - 阅读源码路线图:给出建议的阅读顺序与理由。
本章涉及的主要模块为 spring-core、spring-beans、spring-context、spring-aop、spring-expression、spring-web、spring-webmvc、spring-webflux、spring-tx、spring-test,以及构建脚本 settings.gradle、build.gradle、buildSrc/。前置知识:熟悉 Java 接口继承、设计模式基本概念、Gradle 的 api/implementation/optional 依赖配置语义。读完本章,读者应能在不看任何文档的情况下,仅凭 build.gradle 推断出任意两个模块之间的允许依赖方向,并能说清 ApplicationContext.refresh() 十二个步骤各自的职责。
本章所有路径均相对于仓库根目录 spring-framework-7.0.9,格式为 模块名/src/main/java/包路径/类名.java。所有数字均为脚本实测,所有代码片段均从仓库实际读取,与 7.0.9 一致。
2. 项目概貌实证:27 个模块的真实规模
2.1 统计口径与方法
settings.gradle 第 5-31 行 include 了 27 个项目,根项目名为 spring:
groovy
include "spring-aop"
include "spring-aspects"
include "spring-beans"
include "spring-context"
include "spring-context-indexer"
include "spring-context-support"
include "spring-core"
include "spring-core-test"
include "spring-expression"
include "spring-instrument"
include "spring-jdbc"
include "spring-jms"
include "spring-messaging"
include "spring-orm"
include "spring-oxm"
include "spring-r2dbc"
include "spring-test"
include "spring-tx"
include "spring-web"
include "spring-webflux"
include "spring-webmvc"
include "spring-websocket"
include "framework-api"
include "framework-bom"
include "framework-docs"
include "framework-platform"
include "integration-tests"
rootProject.name = "spring"
注意一个 7.0 版本的事实:清单里没有 spring-jcl 。在 Spring 6.x 及更早版本中,spring-jcl 是一个独立模块,负责把 commons-logging API 桥接到 Log4j/JUL 等真实日志实现;而在本仓库中,Glob **/jcl/** 与 **/spring-jcl/**/*.java 均无结果,日志抽象被合并进了 spring-core 的 org.springframework.core.log 包(该包下有 LogAccessor、LogDelegateFactory、CompositeLog、LogMessage 四个类)。这是 7.0 相对旧版的一个结构性变化,后文版本痕迹一节会再展开。
统计方法:对每个含 src/main/java 的模块目录,递归统计 *.java 文件个数,并用 Get-Content | Measure-Object -Line 累加每个文件的行数。实测结果如下表(按代码行数降序):
| 模块 | .java 文件数 | main 代码行数 | 一句话定位 |
|---|---|---|---|
| spring-core | 730 | 112,101 | 最底层基础:IoC 基础设施、资源、转换、环境、任务执行 |
| spring-web | 781 | 111,097 | Web 通用层:HTTP 客户端/服务端抽象、消息转换、响应式 HTTP |
| spring-context | 625 | 74,677 | 应用上下文:IoC 容器的"高级形态"、事件、注解配置 |
| spring-webmvc | 363 | 64,750 | Servlet 栈 MVC:DispatcherServlet、注解 Controller |
| spring-test | 459 | 62,201 | TestContext 框架:集成测试支持 |
| spring-beans | 336 | 55,492 | Bean 工厂与 BeanDefinition:IoC 的"引擎层" |
| spring-webflux | 275 | 40,266 | 响应式栈:RouterFunction、WebClient |
| spring-jdbc | 253 | 33,935 | JDBC 访问:JdbcTemplate、事务资源管理 |
| spring-messaging | 245 | 29,787 | 消息抽象:WebSocket、STOMP、消息通道 |
| spring-aop | 226 | 23,798 | AOP 核心:代理工厂、切入点、通知模型 |
| spring-jms | 122 | 20,158 | JMS 集成 |
| spring-websocket | 170 | 18,644 | WebSocket 服务端实现 |
| spring-expression | 123 | 18,965 | SpEL 表达式语言 |
| spring-tx | 173 | 19,506 | 事务抽象:PlatformTransactionManager、@Transactional |
| spring-context-support | 87 | 10,461 | 上下文扩展:缓存、调度、FreeMarker/Quartz 集成 |
| spring-orm | 65 | 12,825 | ORM 集成:Hibernate/JPA 支持 |
| spring-r2dbc | 65 | 7,180 | R2DBC 响应式数据访问 |
| spring-oxm | 28 | 3,406 | XML 对象映射(JAXB/XStream) |
| spring-core-test | 45 | 4,533 | core 模块的测试 fixture |
| spring-aspects | 14 | 414 | @AspectJ 注解方面的运行时织入 |
| spring-context-indexer | 13 | 869 | 类路径候选组件索引生成器 |
| spring-instrument | 2 | 64 | JVM agent(类加载时织入) |
把 22 个 spring-* 模块的数字加总:约 5,200 个 .java 文件、约 72.5 万行 main 代码 (不含 src/test/java)。这还不包括 buildSrc(19 文件、1,455 行,Gradle 构建逻辑本身)与 framework-docs(217 文件、7,290 行,参考文档与 Schema)。
2.2 从数字读出来的三个判断
判断一:spring-core 与 spring-web 是两个"等量级巨兽",但角色完全不同。 spring-core 以 730 文件/11.2 万行位居代码行数第一,spring-web 以 781 文件/11.1 万行紧随其后。spring-core 大是因为它是所有模块的地基:资源抽象 org.springframework.core.io、类型转换 org.springframework.core.convert、环境抽象 org.springframework.core.env、任务执行 org.springframework.core.task、注解工具 org.springframework.core.annotation、异常体系 org.springframework.core、以及重新打包的 ASM/CGLIB/javapoet/objenesis(构建时通过 ShadowJar relocate 到 org.springframework.asm、org.springframework.cglib、org.springframework.javapoet、org.springframework.objenesis 包下)。spring-web 大则是因为它承载了从 RestClient/RestTemplate 到 WebClient、从 HttpMessageConverter 到 Codec 的整条 HTTP 抽象链,且对 Servlet API、Reactor Netty、Jetty、Tomcat、Jackson、Gson 等数十种第三方库做了可选适配。
判断二:spring-beans(5.5 万行)与 spring-context(7.5 万行)构成 IoC 的"双层引擎"。 很多初学者以为 IoC 容器就是一个类,实测数据说明它是两层:spring-beans 提供最底层的 BeanFactory、BeanDefinition、属性编辑、类型转换接入,它不依赖任何上层模块 ;spring-context 在其之上叠加 ApplicationContext、事件发布、注解配置解析、环境抽象接入,代码量比 spring-beans 多 35%。这个"beans 是引擎、context 是整车"的数字关系,与下一节要讲的依赖方向完全吻合。
判断三:数据访问与 Web 栈是"薄适配层",核心机制不在这里。 spring-jdbc(3.4 万行)、spring-tx(2.0 万行)、spring-orm(1.3 万行)、spring-r2dbc(0.7 万行)都只依赖 spring-beans+spring-core,它们是"在 IoC 引擎之上按特定技术栈写的适配代码";同理,spring-webmvc(6.5 万行)与 spring-webflux(4.0 万行)都强依赖 spring-web,而 spring-web 本身不强依赖 spring-context------这意味着 Web 层可以脱离 IoC 容器独立存在,这正是 RestClient 这类"非容器化 HTTP 客户端"能成立的架构前提。
3. 模块全景与分层:从 build.gradle 实证依赖方向
3.1 构建文件的特殊命名
读 7.0.9 的构建脚本前要先注意一个细节:settings.gradle 第 34-36 行设置了
groovy
rootProject.children.each {project ->
project.buildFileName = "${project.name}.gradle"
}
也就是说,spring-core 模块的构建脚本不在 Gradle 默认位置 spring-core/build.gradle,而在 spring-core/spring-core.gradle。这是 Spring 构建长期沿用的约定,目的是让模块构建文件与模块同名、便于在 IDE 里识别。下面所有引用均按此命名。
3.2 关键模块的 dependencies 块实证
逐个读取十个核心模块的 dependencies 块,只摘录 api(project(":...")) 这类内部模块硬依赖 (optional(...) 是可选适配,testImplementation(...) 不影响运行时架构):
spring-core(spring-core/spring-core.gradle 第 73-117 行) :没有任何 api(project(":..."))。它的外部 api 依赖只有两行:
groovy
dependencies {
javapoet("com.palantir.javapoet:javapoet:${javapoetVersion}@jar")
objenesis("org.objenesis:objenesis:${objenesisVersion}@jar")
api(files(javapoetRepackJar))
api(files(objenesisRepackJar))
api("commons-logging:commons-logging")
api("org.jspecify:jspecify")
......
}
api(files(javapoetRepackJar))与api(files(objenesisRepackJar))是 ShadowJar 任务重新打包后的产物,分别把com.palantir.javapoetrelocate 成org.springframework.javapoet、把org.objenesisrelocate 成org.springframework.objenesis(见同文件第 30-57 行的javapoetRepackJar/objenesisRepackJar任务)。这就是"把第三方库内嵌进自家包名"的做法------objenesis用于绕过 JDK 构造器实例化类(CGLIB 代理创建时无默认构造器也能实例化),javapoet用于 AOT 代码生成。api("commons-logging:commons-logging")说明 7.0.9 仍然"兼容"commons-logging API(作为桥接的入口),但不再有独立 spring-jcl 模块。api("org.jspecify:jspecify")是 7.0 的新变化:可空注解从老的org.springframework.lang.Nullable迁到了 JSpecify 的org.jspecify.annotations.Nullable。前文提到BeanFactory.java第 19 行的import org.jspecify.annotations.Nullable;正是这一迁移的实证;buildSrc/.../ArchitectureRules.java第 49-59 行的 ArchUnit 规则甚至禁止 业务类再依赖老的org.springframework.lang.NonNull/Nullable,把迁移变成了构建期强制约束。
spring-beans(spring-beans/spring-beans.gradle 第 5-18 行):
groovy
dependencies {
api(project(":spring-core"))
optional("jakarta.inject:jakarta.inject-api")
optional("org.apache.groovy:groovy-xml")
optional("org.jetbrains.kotlin:kotlin-reflect")
optional("org.jetbrains.kotlin:kotlin-stdlib")
optional("org.reactivestreams:reactive-streams")
optional("org.yaml:snakeyaml")
......
}
只有一行内部依赖 api(project(":spring-core"))。这就是"beans 只依赖 core"的铁证。没有 aop、没有 context、没有 web------IoC 引擎层刻意与一切业务抽象解耦。
spring-expression(spring-expression/spring-expression.gradle 第 5-12 行) :同样只有 api(project(":spring-core"))。SpEL 作为表达式语言,被设计成可以独立于 IoC 使用(例如在 spring-data 或其他产品里单独嵌入 SpEL)。
spring-aop(spring-aop/spring-aop.gradle 第 5-18 行):
groovy
dependencies {
api(project(":spring-beans"))
api(project(":spring-core"))
......
}
AOP 同时依赖 beans 与 core。注意顺序上 beans 在前------因为 AOP 要生成 Advisor、Pointcut 类型的 bean,必须复用 beans 模块的 BeanDefinition 体系,但它不依赖 context。
spring-context(spring-context/spring-context.gradle 第 9-59 行):
groovy
dependencies {
api(project(":spring-aop"))
api(project(":spring-beans"))
api(project(":spring-core"))
api(project(":spring-expression"))
api("io.micrometer:micrometer-observation")
optional(project(":spring-instrument"))
......
}
context 一次性 api 依赖 aop、beans、core、expression 四个模块。这说明 context 是"集成者":它把 AOP 织入能力(@EnableAspectJAutoProxy)、SpEL(@Value 解析)、bean 引擎全部组合成一个完整应用容器。
spring-web(spring-web/spring-web.gradle 第 6-106 行):
groovy
dependencies {
api(project(":spring-beans"))
api(project(":spring-core"))
api("io.micrometer:micrometer-observation")
optional(project(":spring-aop"))
optional(project(":spring-context"))
optional(project(":spring-oxm"))
......
}
关键事实:web 对 context 是 optional 而非 api 。optional 是 buildSrc 里 OptionalDependenciesPlugin 定义的配置(见 buildSrc/src/main/java/org/springframework/build/optional/OptionalDependenciesPlugin.java),它在编译期存在但不传递给下游、运行期按需出现。这意味着 spring-web 的核心类(如 RestClient、HttpMessageConverter)不引用 ApplicationContext,Web 层可以脱离 IoC 容器独立使用。
spring-webmvc(spring-webmvc/spring-webmvc.gradle 第 6-84 行):
groovy
dependencies {
api(project(":spring-aop"))
api(project(":spring-beans"))
api(project(":spring-context"))
api(project(":spring-core"))
api(project(":spring-expression"))
api(project(":spring-web"))
......
}
webmvc 把 aop/beans/context/core/expression/web 六个模块全部 api 化------它是 Servlet 栈最顶层,天然需要完整容器。
spring-webflux(spring-webflux/spring-webflux.gradle 第 6-65 行):
groovy
dependencies {
api(project(":spring-beans"))
api(project(":spring-core"))
api(project(":spring-web"))
api("io.projectreactor:reactor-core")
optional(project(":spring-context"))
......
}
webflux 硬依赖 beans/core/web,但对 context 同样是 optional。这与 webmvc 形成对比:webflux 的注解式栈(@Controller + 容器)需要 context,但 WebClient/RouterFunctions 这套编程式 API 可以脱离容器。
spring-tx(spring-tx/spring-tx.gradle 第 5-28 行):
groovy
dependencies {
api(project(":spring-beans"))
api(project(":spring-core"))
optional(project(":spring-aop"))
optional(project(":spring-context")) // for JCA, @EnableTransactionManagement
......
}
tx 的核心抽象(PlatformTransactionManager、TransactionDefinition、TransactionStatus)只依赖 beans+core;@EnableTransactionManagement 这种注解驱动配置才需要 context,因此是 optional。
spring-test(spring-test/spring-test.gradle 第 7-18 行):
groovy
dependencies {
api(project(":spring-core"))
optional(project(":spring-aop"))
optional(project(":spring-beans"))
optional(project(":spring-context"))
optional(project(":spring-jdbc"))
optional(project(":spring-orm"))
optional(project(":spring-tx"))
optional(project(":spring-web"))
optional(project(":spring-webflux"))
optional(project(":spring-webmvc"))
optional(project(":spring-websocket"))
......
}
test 只 api 依赖 core,其余全部 optional。这是"测试框架适配一切被测技术栈"的设计:你用什么栈,就把对应模块 optional 进来,TestContext 框架本身不绑架任何业务模块。
3.3 分层 ASCII 图与依赖规则
把上述实证汇总成一张分层图(箭头方向表示"依赖于",即上层 → 下层;实线为 api 硬依赖,虚线为 optional 可选依赖):
scss
┌─────────────────────────────────────┐
构建发布层 │ framework-api / framework-bom / │
│ framework-platform / integration-tests│
└─────────────────────────────────────┘
▲(仅构建期,不参与运行时)
┌─────────────────────────── Web/响应式栈 ───────────────────────────┐
│ spring-webmvc ──api──▶ spring-web ──api──▶ spring-beans ──┐ │
│ │ │ ▲ optional(context) │ │
│ └─api─▶ spring-context─┤ │ │ │
│ │ │ │ │
│ spring-webflux ─api─▶ spring-web ──┘ optional(context) │ │
│ │ │ │
│ spring-websocket / spring-messaging(依赖 web 与 context) │ │
└─────────────────────────────────────────────────────────────┼──────┘
┌─────────────────────────── 数据访问/事务层 ──────────────────────┐
│ spring-jdbc / spring-orm / spring-r2dbc ─api─▶ spring-beans ─┤ │
│ spring-tx ─api─▶ spring-beans ──optional──▶ spring-aop │ │
│ spring-jms / spring-oxm(同上,beans+core 之上的薄适配) │ │
└────────────────────────────────────────────────────────────────┼────┘
┌─────────────────────────── IoC 与 AOP 核心层 ─────────────────────┐
│ spring-context ─api─▶ spring-aop ─api─▶ spring-beans ──────────┤ │
│ │ │ ▲ │ │
│ └──api──▶ spring-expression ──api──▶ │ │ │
│ │ │ │
│ spring-context-support(context 的可选集成扩展) │ │
└────────────────────────────────────────────────────────────────┼───┘
┌─────────────────────────── 基础设施层 ─────────────────────────┼───┐
│ ▼ │
│ spring-core(无任何内部模块依赖) │
│ io / env / convert / task / annotation / core / lang │
│ 内嵌 shaded:org.springframework.asm / cglib / javapoet / │
│ objenesis;日志:org.springframework.core.log │
└────────────────────────────────────────────────────────────────────┘
▲
spring-test ──api──▶ spring-core
(其余模块全部 optional,适配被测栈)
从这张图可以读出五条依赖规则,每条都有 build.gradle 实证:
- core 是唯一的"零内部依赖"模块 。它不 import 任何其他 spring 模块的类(
buildSrc/.../ArchitectureRules.java还通过 ArchUnitSlicesRuleDefinition.slices()...beFreeOfCycles()强制整个框架包结构无环)。 - beans 只许依赖 core,绝不反向依赖 context。这是 IoC 引擎可被"单独拿去嵌入其他容器"的前提。
- aop 依赖 beans,但不依赖 context;context 反过来依赖 aop------方向是 context → aop → beans,绝不允许 aop → context 的反向引用。
- web 层对 context 是 optional:web/webflux 可以脱离 IoC 容器做纯 HTTP 编程;webmvc 因为要接管 Servlet 生命周期与注解映射,才硬依赖 context。
- 数据访问层(jdbc/orm/r2dbc/tx)统一站在 beans+core 之上 ,对 context/aop 都是 optional------
@Transactional的注解驱动配置(@EnableTransactionManagement)才把 context 拉进来。
这五条规则不是"风格建议",而是被 Gradle 配置 + ArchUnit 架构检查双重锁死的工程约束。理解了它们,读者以后看任何一个新模块(比如未来新增的 spring-xxx),都能立刻判断它应该放在哪一层、允许依赖谁。
4. 核心概念:顶层抽象接口的职责与设计意图
本节逐个读真实接口文件,讲清 Spring 最顶层的几组抽象。这些接口是整个框架的"契约层"------后续所有实现类(DefaultListableBeanFactory、AnnotationConfigApplicationContext 等)都是在履行这些契约。
4.1 BeanFactory 继承链:五层接口的职责递进
BeanFactory 位于 spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java,是整个 IoC 体系的根接口。它的类注释第 26 行开宗明义:"The root interface for accessing a Spring bean container."(访问 Spring bean 容器的根接口)。
第一层:BeanFactory 本身。它只定义"客户端视图"------拿 bean、问 bean 状态,不暴露任何配置能力。关键方法签名(见该文件第 155-334 行):
java
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, @Nullable Object @Nullable ... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, @Nullable Object @Nullable ... args) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
以及一个常量:
java
String FACTORY_BEAN_PREFIX = "&";
char FACTORY_BEAN_PREFIX_CHAR = '&'; // @since 6.2.6
逐段解读:
- 五个
getBean重载形成"按名字 / 按类型 / 带显式构造参数"的组合矩阵。按类型的getBean(Class)是注解注入时代的主力------容器要根据依赖类型反查 bean;带args的重载用于"原型作用域或手动创建时临时指定构造参数"。 FACTORY_BEAN_PREFIX = "&"是一个极富设计感的约定:当一个 bean 本身是FactoryBean(工厂 bean)时,getBean("myFactory")返回的是工厂生产的对象 ,而getBean("&myFactory")才返回工厂本身。用一个字符前缀区分两种语义,避免了设计两个独立方法。- 类注释第 63-67 行明确写了一条重要语义:本接口的所有操作"都会向父工厂查找",但本工厂的同名 bean 会覆盖父工厂的同名 bean------这就是父子容器的优先级规则。
- 类注释第 70-91 行还以列表形式完整声明了 bean 生命周期回调顺序:
BeanNameAware.setBeanName→BeanClassLoaderAware→BeanFactoryAware→EnvironmentAware→EmbeddedValueResolverAware→ResourceLoaderAware→ApplicationEventPublisherAware→MessageSourceAware→ApplicationContextAware→ServletContextAware→BeanPostProcessor.postProcessBeforeInitialization→InitializingBean.afterPropertiesSet→ 自定义 init-method →BeanPostProcessor.postProcessAfterInitialization。这张顺序表是框架级契约,后文initializeBean方法逐行落实它。
第二层:HierarchicalBeanFactory (spring-beans/.../factory/HierarchicalBeanFactory.java 第 34-52 行):
java
public interface HierarchicalBeanFactory extends BeanFactory {
@Nullable BeanFactory getParentBeanFactory();
boolean containsLocalBean(String name);
}
它在 BeanFactory 之上只加了两个方法:getParentBeanFactory() 返回父工厂;containsLocalBean(String) 只查本地 、忽略祖先。设计意图是:BeanFactory 的 containsBean 会沿父链向上找,而 containsLocalBean 提供"我自己到底有没有定义这个 bean"的精确判断------Spring Boot 的父子容器、Web 应用的根容器+Servlet 容器分层都靠这两个方法区分"本地定义"与"继承而来"。
第三层:ListableBeanFactory (spring-beans/.../factory/ListableBeanFactory.java 第 60 行起):
java
public interface ListableBeanFactory extends BeanFactory {
int getBeanDefinitionCount();
String[] getBeanNamesForType(ResolvableType type);
String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
// ......
}
"Listable"意为"可列举"。BeanFactory 只能按名字或按类型取一个 bean,而 ListableBeanFactory 增加"枚举整个容器"的能力:数一下容器里有多少 bean 定义、按类型列出所有 bean 名、把某类型的所有 bean 收成一个 Map。getBeanNamesForType 带 ResolvableType 参数的重载说明它能处理泛型(如 List<String>),而不只是 raw class。这一层是"自动装配"与"按类型注入"的基础------容器要能根据注入点类型反查所有候选 bean。
第四层:ConfigurableBeanFactory (spring-beans/.../factory/config/ConfigurableBeanFactory.java 第 53 行):
java
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
void registerScope(String scopeName, Scope scope);
// ......
}
注意它同时 extends HierarchicalBeanFactory, SingletonBeanRegistry------这是 Java 接口多继承的典型用法:ConfigurableBeanFactory 把"层级能力"与"单例注册表能力"组合成一个可配置接口。方法名以 set/add/register 开头,与前三层的"只读查询"形成对比:这一层面向容器初始化者,允许设置父工厂、注册 BeanPostProcessor、注册自定义作用域(如 web 环境的 request/session)。
第五层:ConfigurableListableBeanFactory (spring-beans/.../factory/config/ConfigurableListableBeanFactory.java 第 43-44 行):
java
public interface ConfigurableListableBeanFactory
extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
void ignoreDependencyType(Class<?> type);
void ignoreDependencyInterface(Class<?> ifc);
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
void freezeConfiguration();
void preInstantiateSingletons() throws BeansException;
// ......
}
它是这条链的"汇合点":同时继承 ListableBeanFactory(可列举)、AutowireCapableBeanFactory(可自动装配/外部创建 bean)、ConfigurableBeanFactory(可配置)。新增方法各有职责:
ignoreDependencyType/ignoreDependencyInterface:告诉自动装配"遇到这些类型别尝试注入"。注释里举例:BeanFactory本身通过BeanFactoryAware回调注入,而不是按类型自动装配,所以默认要把BeanFactory接口加入忽略名单------否则自动装配会试图"注入一个 BeanFactory",语义混乱。getBeanDefinition(String):直接拿 bean 定义元数据,这是"可列举"到"可内省"的一步------上层不仅能枚举 bean,还能读每个 bean 的定义。freezeConfiguration():冻结配置,冻结后容器假设 bean 定义不再变化,可做缓存优化。preInstantiateSingletons():实例化所有非懒加载单例。这正是refresh()最后阶段调用的入口------容器启动完成前,一次性把所有单例 bean 创建好。
这条五层链的设计哲学可以概括为:用接口继承把"客户端视图"与"容器管理者视图"分离 。普通业务代码只接触到 BeanFactory/ListableBeanFactory(只读),框架自己才用 Configurable* 系列(可写)。这种"能力随继承层级递增"的接口拆分,让依赖方向天然可控------你没法拿一个 BeanFactory 引用去调 addBeanPostProcessor,因为编译期类型上就没这个方法。
4.2 ApplicationContext 继承链:一个接口组合六种角色
ApplicationContext(spring-context/src/main/java/org/springframework/context/ApplicationContext.java 第 59-60 行)是上面那条 BeanFactory 链的"上层封装":
java
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
一行 extends 列出六个父接口,把六种角色组合到一个接口里:
ListableBeanFactory+HierarchicalBeanFactory:容器能力本身(可列举、可分层)。注意它没有继承ConfigurableBeanFactory------ApplicationContext的客户端视图同样不暴露配置方法。EnvironmentCapable:提供getEnvironment(),接入Environment抽象(profile、properties)。MessageSource:国际化消息解析(getMessage(code, args, locale))。ApplicationEventPublisher:事件发布(publishEvent)。ResourcePatternResolver:按 ant 风格路径模式批量加载Resource(如classpath*:META-INF/spring.factories)。
也就是说,拿到一个 ApplicationContext 引用,你就同时拥有了"bean 容器 + 环境 + 国际化 + 事件总线 + 资源加载器"五种能力。这种"宽接口组合"是 Spring 门面模式的体现:业务代码只需注入 ApplicationContext 一个对象,不必分别持有五个协作组件。
下一层是 ConfigurableApplicationContext(spring-context/.../context/ConfigurableApplicationContext.java 第 45 行):
java
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
void setEnvironment(ConfigurableEnvironment environment);
void addApplicationListener(ApplicationListener<?> listener);
void refresh() throws BeansException, IllegalStateException;
void close();
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
// ......
}
它在 ApplicationContext 之上加了 Lifecycle(start/stop)、Closeable(close)两个生命周期接口,以及关键的 refresh()/close()/getBeanFactory()。注意 getBeanFactory() 返回的是 ConfigurableListableBeanFactory------即"容器内部那一层可配置的 bean 工厂",这正好印证了上文的五层链:ApplicationContext 对外是只读门面,对内通过 getBeanFactory() 把可配置的引擎层暴露给框架自身。
类继承关系(后续源码剖析会反复用到):
java
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext // spring-context/.../support/AbstractApplicationContext.java:144
public class GenericApplicationContext extends AbstractApplicationContext
implements BeanDefinitionRegistry // spring-context/.../support/GenericApplicationContext.java:106
public class AnnotationConfigApplicationContext extends GenericApplicationContext
implements AnnotationConfigRegistry // spring-context/.../annotation/AnnotationConfigApplicationContext.java:57
AbstractApplicationContext 继承 DefaultResourceLoader------这意味着上下文本身就是一个 ResourceLoader,@Autowired ResourceLoader loader 注入进来的往往就是上下文自己。AnnotationConfigApplicationContext 提供 register(Class<?>...) 与 scan(String...) 两个方法(见该文件第 169、186 行),这就是注解驱动容器的两个入口:注册配置类、扫描包。
4.3 Resource 与 ResourceLoader:资源访问的统一抽象
Java 标准库的资源访问一直是割裂的:ClassLoader.getResourceAsStream() 只能读 classpath,new FileSystemResource/FileInputStream 只能读文件系统,URL 能读 http/file 但 API 别扭。Spring 在 spring-core/src/main/java/org/springframework/core/io/ 下用一组接口把它们统一了。
根接口 Resource(Resource.java 第 58 行):
java
public interface Resource extends InputStreamSource {
boolean exists();
URL getURL() throws IOException;
File getFile() throws IOException;
long contentLength() throws IOException;
// isReadable, openStream, getFilename, createRelative ......
}
它继承 InputStreamSource(只声明 InputStream getInputStream()),并增加了"存在性判断、URL/File 定位、内容长度、文件名"等元信息。spring-core/src/main/java/org/springframework/core/io/ 目录下的实现类清单(Glob 实证)包括:ClassPathResource、FileSystemResource、UrlResource、FileUrlResource、ByteArrayResource、InputStreamResource、PathResource、VfsResource(JBoss 虚拟文件系统)、DescriptiveResource、WritableResource(扩展了可写能力)、AbstractResource(公共骨架)。这套体系是组合+适配器模式 的体现:每种底层资源源都被适配成统一的 Resource 接口,上层代码(如 XmlBeanDefinitionReader 读 XML、PropertySourceLoader 读 yml)只面向 Resource 编程,不关心资源到底来自 classpath、文件系统还是 URL。
ResourceLoader 接口(ResourceLoader.java 第 43-68 行)则是"根据位置字符串选 Resource 实现"的工厂:
java
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
Resource getResource(String location);
}
其默认实现 DefaultResourceLoader.getResource(String)(DefaultResourceLoader.java 第 145-165 行)是一段经典的"按前缀分发"策略逻辑:
java
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = ResourceUtils.toURL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
// ......兜底:按 classpath 资源处理
}
}
逐段解读:
- 先过
ProtocolResolver扩展链 。protocolResolvers是一个LinkedHashSet(第 54 行),用户可通过addProtocolResolver注册自定义协议解析器(例如支持myprotocol:xxx这种自定义 scheme)。这是"策略链"扩展点------内置规则处理不了的位置,先问外部注册的策略。 - 按
/开头判断走getResourceByPath。这是一个 protected 钩子方法,FileSystemXmlApplicationContext之类的子类可以重写它,把/xxx解释成文件系统路径而非 classpath 路径------模板方法钩子。 classpath:前缀走ClassPathResource。- 其余一律先尝试当 URL 解析 :是 file: URL 就用
FileUrlResource(可写),否则用UrlResource;URL 解析失败再兜底成 classpath 资源。
这段代码的设计价值在于:同一套 getResource("...") 调用,根据位置字符串自动选出最合适的 Resource 实现 。业务代码写 getResource("classpath:config/app.yml")、getResource("file:/etc/app.yml")、getResource("https://example.com/app.yml") 拿到的都是 Resource,后续处理代码完全一致。
ApplicationContext 之所以能继承 ResourcePatternResolver(ResourceLoader 的子接口,支持 ant 通配符如 classpath*:**/*.xml),正是因为 AbstractApplicationContext 本身继承了 DefaultResourceLoader------资源加载能力是上下文的祖传技能。
4.4 ConversionService:类型转换的策略总线
spring-core/src/main/java/org/springframework/core/convert/ConversionService.java 第 29 行:
java
public interface ConversionService {
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
default @Nullable Object convert(@Nullable Object source, TypeDescriptor targetType) { ...... }
@Nullable Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
}
它把"对象从类型 A 转成类型 B"抽象成一个服务。两个 canConvert 重载(Class 版与 TypeDescriptor 版)对应"粗粒度类型判断"与"带字段上下文的精确判断"(比如泛型 List<Integer> vs List<String>)。BeanFactory 在做属性填充时,遇到 String 类型的配置值要注入 int/boolean/Date 属性,走的就是这条转换服务链;其背后由 Converter<S,T> SPI 树支撑(converter 包下有 ConverterRegistry、GenericConversionService、DefaultConversionService)。设计意图:把"类型系统"从 bean 装配逻辑里抽出来,成为一个可独立替换、可注册自定义 converter 的策略总线。
4.5 MessageSource:国际化消息解析
spring-context/src/main/java/org/springframework/context/MessageSource.java 第 40-93 行:
java
public interface MessageSource {
@Nullable String getMessage(String code, Object @Nullable [] args, @Nullable String defaultMessage, @Nullable Locale locale);
String getMessage(String code, Object @Nullable [] args, @Nullable Locale locale) throws NoSuchMessageException;
String getMessage(MessageSourceResolvable resolvable, @Nullable Locale locale) throws NoSuchMessageException;
}
三个 getMessage 重载分别处理"带默认文案"、"不带默认文案(找不到抛异常)"、"把 code+args+default 封装成 MessageSourceResolvable 对象"三种场景。它的默认抽象实现 AbstractMessageSource 在后文模板方法一节会专门剖析。
4.6 Environment 与 PropertyResolver:配置环境
spring-core/src/main/java/org/springframework/core/env/Environment.java 第 72 行:
java
public interface Environment extends PropertyResolver {
String[] getActiveProfiles();
String[] getDefaultProfiles();
boolean acceptsProfiles(String... profiles);
boolean acceptsProfiles(Profiles profiles);
// ......
}
它继承 PropertyResolver(PropertyResolver.java 第 30 行):
java
public interface PropertyResolver {
boolean containsProperty(String key);
@Nullable String getProperty(String key);
String getProperty(String key, String defaultValue);
// <T> T getProperty(String key, Class<T> targetType) ......
}
PropertyResolver 管"键值属性查询",Environment 在其上增加"profile"维度------getActiveProfiles() 返回激活的环境标识(如 dev/prod),acceptsProfiles(...) 判断当前环境是否匹配某组 profile(@Profile("prod") 注解的判定就走这里)。这套抽象把"运行环境"从"properties 文件"里独立出来:profile 是环境,properties 是配置值,两者通过 Environment 统一对外。ApplicationContext 通过 EnvironmentCapable 父接口暴露 getEnvironment(),而 @Value("${...}") 占位符解析最终也落到 Environment/PropertySources 体系。
4.7 六组顶层抽象的共同设计模式
把 4.1-4.6 放在一起看,会发现 Spring 顶层抽象有一个一致的手法:接口只做最小契约,能力通过继承叠加,实现类通过组合复用。
BeanFactory只做最小查询;ListableBeanFactory加列举;Configurable*加配置。ApplicationContext一次组合六个角色;ConfigurableApplicationContext再加生命周期。Resource是最小资源契约;ResourceLoader是资源工厂;ResourcePatternResolver是批量模式扩展。PropertyResolver是最小属性查询;Environment加 profile;EnvironmentCapable是"谁能提供 Environment"的能力标记接口。
这种"窄接口 + 组合接口"的做法,对应到构建脚本就是第 3 节看到的依赖方向:core 只放最底层抽象(Resource、Environment、ConversionService、TaskExecutor),beans 加 BeanFactory 家族,context 才组合出 ApplicationContext。每一层接口的位置,恰好就是它所在模块的位置。
4.8 补充剖析:Environment 的内部结构与 PropertySources 优先级
4.6 节只讲了 Environment/PropertyResolver 接口,这里补它的内部结构。spring-core/src/main/java/org/springframework/core/env/ 目录(Glob 实证)有完整一族:PropertySource、EnumerablePropertySource、MapPropertySource、PropertiesPropertySource、SystemEnvironmentPropertySource、CompositePropertySource、MutablePropertySources、StandardEnvironment、AbstractEnvironment。
Environment 的运行时实现是 StandardEnvironment,它内部持有一个 MutablePropertySources------一个 PropertySource 有序列表。PropertyResolver 查询时按列表顺序从前到后找第一个命中的 key。这套设计的关键是优先级靠顺序表达:
SystemEnvironmentPropertySource(OS 环境变量)与PropertiesPropertySource(JVM 系统属性)默认在列;- 用户通过
@PropertySource或application.properties加入的排在后面; MutablePropertySources.addFirst/addLast/addBefore/addAfter可以微调顺序。
也就是说,"哪个配置生效"不是靠复杂规则,而是靠 PropertySource 在 MutablePropertySources 列表里的位置------后加的同名 key 不会覆盖前面的。acceptsProfiles(Environment.java 第 136、149 行)则用 Profiles 谓词对象判断 @Profile("prod & !test") 这种复合条件,ProfilesParser 负责解析表达式字符串。这套把"环境"拆成"profile 维度 + 有序 property 列表"两维的做法,让 @Value("${db.url}") 占位符、@Profile 条件装配、命令行参数(SimpleCommandLinePropertySource)都共享同一套解析机制。
4.9 补充剖析:ResourcePatternResolver 与 SPI 加载
4.3 节讲了 ResourceLoader.getResource 按位置字符串选单个 Resource。但 Spring 大量场景需要"按 ant 通配符批量找资源",例如 classpath*:META-INF/spring.factories。这个能力在 spring-core/src/main/java/org/springframework/core/io/support/ 目录:
ResourcePatternResolver(该目录)是ResourceLoader的子接口,加了Resource[] getResources(String locationPattern);PathMatchingResourcePatternResolver是其实现,用PathMatcher(ant 风格匹配)把classpath*:展开成 classpath 下多个Resource。
AbstractApplicationContext 实现的 ResourcePatternResolver 能力,实际就是委托给 PathMatchingResourcePatternResolver。这就是为什么 ApplicationContext.getResources("classpath*:com/acme/**/*.class") 能扫出整个包------Web 容器启动时 ClassPathBeanDefinitionScanner 也是基于这套模式解析来找候选类的。
同目录还有一个极重要的类 SpringFactoriesLoader:它负责从 classpath 所有 jar 的 META-INF/spring.factories(以及新版的 .imports 文件)加载 SPI 实现类名。Spring Boot 的自动装配、Spring 自身的 ApplicationContextInitializer 扩展点都靠它。它体现了 Spring 的"扩展点发现"机制:框架不硬编码实现列表,而是扫描 classpath 上的资源文件动态装配------这与 5.9 节的扩展点优先哲学一脉相承。
把这三节(4.7-4.9)合起来看,spring-core 的顶层抽象其实是一张网:ResourceLoader 解决"单个资源怎么读",ResourcePatternResolver 解决"一批资源怎么找",Environment/PropertySources 解决"配置值从哪读",SpringFactoriesLoader 解决"扩展实现从哪发现"。四者都在 core 层,不依赖任何业务模块,是整个框架的地基。
5. 源码剖析:设计哲学如何落到具体类
本节按设计模式分类,把框架里最典型的实现逐个拆开。注意本章只讲"骨架",具体机制(如循环依赖解析、AOP 代理细节)点到为止。
5.1 模板方法:AbstractApplicationContext.refresh() 的十二步骨架
模板方法模式的精髓是:父类固定算法骨架,子类只实现可变步骤。AbstractApplicationContext.refresh()(spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java 第 581-661 行)是整个 Spring 最著名的模板方法。贴出 7.0.9 实际源码:
java
@Override
public void refresh() throws BeansException, IllegalStateException {
this.startupShutdownLock.lock();
try {
this.startupShutdownThread = Thread.currentThread();
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (RuntimeException | Error ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Stop already started Lifecycle beans to avoid dangling resources.
if (this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning()) {
try {
this.lifecycleProcessor.stop();
}
catch (Throwable ex2) {
logger.warn("Exception thrown from LifecycleProcessor on cancelled refresh", ex2);
}
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
contextRefresh.end();
}
}
finally {
this.startupShutdownThread = null;
this.startupShutdownLock.unlock();
}
}
逐段解读十二步:
prepareRefresh()(第 667 行):刷新前预处理------校验必要属性、初始化早期事件列表、记录启动时间。obtainFreshBeanFactory()(第 719 行):告诉子类刷新内部 bean 工厂。AbstractRefreshableApplicationContext的实现会新建一个DefaultListableBeanFactory并加载 bean 定义;GenericApplicationContext则复用构造时就建好的工厂。这是模板方法留给子类的第一个可变点。prepareBeanFactory(beanFactory)(第 729 行):配置工厂------设置类加载器、表达式解析器、注册ApplicationContextAwareProcessor等内建后处理器、把Environment/ResourceLoader等依赖列为可解析。postProcessBeanFactory(beanFactory)(第 786 行):空实现的 protected 钩子,留给子类(如WebApplicationContext子类)在工厂就绪后加额外后处理器。invokeBeanFactoryPostProcessors(beanFactory)(第 794-795 行):委托PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors,执行所有BeanFactoryPostProcessor------这一步会解析@Configuration类、扫描@ComponentScan、注册 bean 定义。registerBeanPostProcessors(beanFactory)(第 811-812 行):同样委托给PostProcessorRegistrationDelegate,把所有BeanPostProcessor注册进工厂------注意此时单例 bean 还没开始实例化,后处理器必须先注册好,才能拦截后续 bean 的创建。initMessageSource():初始化国际化消息源。initApplicationEventMulticaster()(第 853 行):初始化事件多播器(观察者模式的核心)。onRefresh()(第 906 行):protected 钩子,默认空。GenericWebApplicationContext在这里初始化 Servlet 容器的主题源;Spring Boot 的ServletWebServerApplicationContext在这里启动内嵌 Web 服务器。这是模板方法最经典的扩展点------"容器就绪后、单例实例化前"这个时机。registerListeners()(第 914 行):把ApplicationListenerbean 注册到多播器。finishBeanFactoryInitialization(beanFactory)(第 942 行):调用beanFactory.preInstantiateSingletons(),一次性实例化所有非懒加载单例------容器启动的"重活"在这一步。finishRefresh()(第 1002 行):初始化LifecycleProcessor、调用onRefresh()、发布ContextRefreshedEvent。
异常处理段同样是设计的一部分:刷新失败时先停掉已启动的 Lifecycle bean、销毁已创建的单例、重置 active 标志,再把异常抛给调用者------保证失败不留半初始化容器。finally 里释放 startupShutdownLock。
7.0.9 版本差异 :旧版(5.x/6.x)这段方法开头是 synchronized (this.startupShutdownMonitor)(一个 Object 类型的监视器),7.0.9 改成了 this.startupShutdownLock.lock()。字段声明见第 214、217 行:
java
private final Lock startupShutdownLock = new ReentrantLock();
private volatile @Nullable Thread startupShutdownThread;
换成 ReentrantLock 后,close()/doClose() 路径可以用 tryLock(100, TimeUnit.MILLISECONDS)(第 1096 行)做带超时的等待,还能记录 startupShutdownThread 是哪个线程在持有启动/关闭锁------这些都是 synchronized 做不到的。
5.2 模板方法:AbstractAutowireCapableBeanFactory.doCreateBean
bean 创建的骨架在 spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java 第 556-637 行。贴关键段:
java
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object @Nullable [] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) { ...... }
mbd.markAsPostProcessed();
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) { ...... }
// ......循环依赖校验段略
}
骨架顺序固定为:实例化(createBeanInstance)→ 合并定义后处理(applyMergedBeanDefinitionPostProcessors)→ 提前暴露三级缓存(addSingletonFactory)→ 属性填充(populateBean)→ 初始化(initializeBean) 。每个步骤都是 protected 方法,子类可扩展但不可改顺序。createBeanInstance(第 1175 行)内部再分"工厂方法/构造器自动选择/简单实例化"三条支路;populateBean(第 1390 行)处理 @Autowired 与属性注入;initializeBean(第 1797 行)如下:
java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
// Skip initialization of a NullBean
if (bean.getClass() == NullBean.class) {
return bean;
}
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) { ...... }
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
invokeAwareMethods(第 1824 行)只处理三个最基础的 Aware:BeanNameAware/BeanClassLoaderAware/BeanFactoryAware;其余 Aware(如 ApplicationContextAware)由 ApplicationContextAwareProcessor 这个 BeanPostProcessor 在 applyBeanPostProcessorsBeforeInitialization 阶段处理。这正好与 BeanFactory 类注释第 70-91 行声明的生命周期顺序表一一对应:Aware 回调 → postProcessBeforeInitialization → InitializingBean.afterPropertiesSet/init-method → postProcessAfterInitialization。
5.3 模板方法:AbstractMessageSource 的 final 骨架 + abstract 钩子
spring-context/src/main/java/org/springframework/context/support/AbstractMessageSource.java 第 66 行起:
java
public abstract class AbstractMessageSource extends MessageSourceSupport implements HierarchicalMessageSource {
public final @Nullable String getMessage(String code, Object @Nullable [] args, @Nullable String defaultMessage, @Nullable Locale locale) { ...... }
public final String getMessage(String code, Object @Nullable [] args, @Nullable Locale locale) throws NoSuchMessageException { ...... }
protected @Nullable String resolveCodeWithoutArguments(String code, Locale locale) {
MessageFormat messageFormat = resolveCode(code, locale);
......
}
protected abstract @Nullable MessageFormat resolveCode(String code, Locale locale);
}
注意两个 getMessage 都是 final------父类锁死了"查 code、取不到走父 MessageSource、格式化参数"的骨架;子类只实现一个抽象方法 resolveCode(String, Locale):根据 code 与 locale 返回一个 MessageFormat。ResourceBundleMessageSource 实现"从 ResourceBundle 按 locale 取条目",ReloadableResourceBundleMessageSource 实现"可定时重载的 properties 文件",两者共享同一套骨架。这是模板方法最教科书式的用法:final 方法定流程,abstract 方法定差异点。
5.4 策略模式:DispatcherServlet 的三个策略族
Spring MVC 最核心的设计决策是:DispatcherServlet 自己不处理"哪个方法该接这个请求、这个方法返回后用哪个模板渲染",而是把它们拆成三个策略接口,每个接口有多个可插拔实现。
策略族一:HandlerMapping (spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java 第 58、179 行):
java
public interface HandlerMapping {
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
@Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
职责:根据 request 选出一个"处理器执行链"(HandlerExecutionChain,包含处理器对象 + 拦截器列表)。实现有 RequestMappingHandlerMapping(处理 @RequestMapping)、SimpleUrlHandlerMapping(URL 路径映射)、BeanNameUrlHandlerMapping 等。
策略族二:HandlerAdapter (同包 HandlerAdapter.java 第 49-76 行):
java
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
职责:因为 HandlerMapping 选出的"处理器"可能是任意类型(Controller 对象、Servlet、HttpRequestHandler......),DispatcherServlet 不直接调用它,而是找一个"支持该类型"的 Adapter 来调。supports(Object) 是策略选择的关键------容器遍历所有 Adapter,谁 supports 返回 true 谁上场。
策略族三:ViewResolver (同包 ViewResolver.java 第 38-55 行):
java
public interface ViewResolver {
@Nullable View resolveViewName(String viewName, Locale locale) throws Exception;
}
职责:把逻辑视图名(字符串)解析成 View 实现。实现有 InternalResourceViewResolver(JSP)、FreeMarkerViewResolver、ContentNegotiatingViewResolver 等。
DispatcherServlet.doDispatch(spring-webmvc/.../servlet/DispatcherServlet.java 第 935-1004 行)是这三个策略的调度现场,贴关键段:
java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Determine handler adapter and invoke the handler.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new ServletException("Handler dispatch failed: " + err, err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// ......afterCompletion 与 multipart 清理略
}
getHandler 与 getHandlerAdapter 的实现就是"遍历策略列表"(同文件第 539、1156、1187 行):
java
for (HandlerMapping mapping : this.handlerMappings) { ...... }
for (HandlerAdapter adapter : this.handlerAdapters) { ...... }
render 方法(第 1267 行)里遍历的是 this.viewResolvers。三个策略列表都在 initStrategies 时从容器里自动收集(DispatcherServlet 按类型把所有 HandlerMapping bean 注入成列表)。
这段代码的设计价值:DispatcherServlet 对"具体怎么映射、怎么调用、怎么渲染"一无所知,它只知道"有一组策略,挨个问谁能处理" 。新增一种处理器类型(比如 WebFlux 的函数式路由)不需要改 DispatcherServlet,只要注册一个新的 HandlerAdapter;新增一种视图技术只要加一个 ViewResolver。这就是开闭原则在框架层的落地。
5.5 策略模式:TaskExecutor 任务执行族
spring-core/src/main/java/org/springframework/core/task/ 目录下(Glob 实证)有:TaskExecutor、AsyncTaskExecutor、SyncTaskExecutor(同步直接执行)、SimpleAsyncTaskExecutor(每次新建线程)、VirtualThreadTaskExecutor(虚拟线程)、TaskDecorator、TaskCallback 等。TaskExecutor 继承自 java.util.concurrent.Executor,Spring 把"异步执行"抽象成一个可替换策略:代码里注入 TaskExecutor,具体用同步、线程池还是虚拟线程,由配置决定,业务代码不感知。
VirtualThreadTaskExecutor(该包第 32-66 行)是 6.1 引入、7.0 保留的实现:
java
public class VirtualThreadTaskExecutor implements AsyncTaskExecutor {
private final ThreadFactory virtualThreadFactory;
public VirtualThreadTaskExecutor() {
this.virtualThreadFactory = new VirtualThreadDelegate().virtualThreadFactory();
}
public VirtualThreadTaskExecutor(String threadNamePrefix) {
this.virtualThreadFactory = new VirtualThreadDelegate().virtualThreadFactory(threadNamePrefix);
}
@Override
public void execute(Runnable task) {
this.virtualThreadFactory.newThread(task).start();
}
}
它把 JDK 21 的虚拟线程封装成 Spring 的 TaskExecutor,用户只需 @Async 或手动注入即可在虚拟线程上跑任务。类注释明确写:"A TaskExecutor implementation based on virtual threads in JDK 21+......@since 6.1"。
5.6 观察者模式:事件多播器 ApplicationEventMulticaster
观察者三要素:事件(ApplicationEvent)、监听器(ApplicationListener<E>)、事件分发器(ApplicationEventMulticaster)。
分发器接口(spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java 第 40-132 行):
java
public interface ApplicationEventMulticaster {
void addApplicationListener(ApplicationListener<?> listener);
void addApplicationListenerBean(String listenerBeanName);
void multicastEvent(ApplicationEvent event);
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
// removeApplicationListener* ......
}
默认实现 SimpleApplicationEventMulticaster(同包第 52 行)继承 AbstractApplicationEventMulticaster,核心分发逻辑(第 139-151 行):
java
Executor executor = getTaskExecutor();
if (executor != null) {
for (...) {
executor.execute(() -> invokeListener(listener, event));
}
}
else {
for (...) {
invokeListener(listener, event);
}
}
逐段解读:如果用户通过 setTaskExecutor(...)(第 92 行)配了一个执行器,每个监听器的回调都被丢进执行器异步执行;否则同步逐个调用。invokeListener(第 162 行)再包裹错误处理,把监听器异常交给 ErrorHandler。
对应到 refresh() 流程:第 8 步 initApplicationEventMulticaster() 在容器里找一个名为 applicationEventMulticaster 的 bean,没有就 new 一个默认的;第 10 步 registerListeners() 把所有 ApplicationListener bean 注册进去;第 12 步 finishRefresh() 发布 ContextRefreshedEvent------所有监听该事件的 bean 此时被回调。业务代码 @EventListener 注解的方法,也是被 EventListenerMethodProcessor(一个 BeanPostProcessor)适配成 ApplicationListener 后接入这条链的。
这套观察者机制的设计要点:事件发布者(ApplicationEventPublisher)与监听器完全解耦 ,上下文只负责多播;同步还是异步由 TaskExecutor 策略决定;监听器按事件类型泛型 ApplicationListener<ContextRefreshedEvent> 自动过滤------AbstractApplicationEventMulticaster 内部用 ResolvableType 做泛型匹配,保证只回调关心该事件类型的监听器。
5.7 组合与适配器:AOP ProxyFactory
AOP 体系在 spring-aop 模块,核心是把"通知(Advice)+ 切入点(Pointcut)+ 目标对象(TargetSource)"组合成一个代理。ProxyFactory(spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java 第 36-110 行):
java
public class ProxyFactory extends ProxyCreatorSupport {
public Object getProxy() {
return createAopProxy().getProxy();
}
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
public static <T> T getProxy(Class<T> proxyInterface, Interceptor interceptor) {
return (T) new ProxyFactory(proxyInterface, interceptor).getProxy();
}
// ......
}
ProxyFactory 继承 ProxyCreatorSupport,后者持有一个 AdvisedSupport(通知链的持有容器)。getProxy() 的核心是 createAopProxy()------这又是一个策略选择:根据目标类是否实现接口、是否强制 CGLIB,决定创建 JdkDynamicAopProxy(JDK 动态代理)还是 ObjenesisCglibAopProxy(CGLIB 代理)。业务代码拿到的代理对象,对调用方而言与原始对象实现同样的接口,但方法调用被 ReflectiveMethodInvocation 编排成"拦截器链"(MethodInterceptor 列表)逐个执行。
这里的组合关系是:ProxyFactory 组合了 TargetSource(目标)、Advisor 列表(通知+切入点)、AopProxyFactory(代理创建策略);Advisor 又组合了 Advice(通知逻辑)与 Pointcut(切点匹配)。每一层都是"小策略对象可替换",@Transactional、@Async、@Cacheable 这些注解的背后,都是 ProxyFactory 在容器启动后由 BeanPostProcessor(如 InfrastructureAdvisorAutoProxyCreator)为目标 bean 动态织入代理。
5.8 约定优于配置:@Configuration 与 @ComponentScan 的入口
XML 时代要写一堆 <bean id="..." class="..."/>;注解时代只需要一个配置类。看 @Configuration 的定义(spring-context/src/main/java/org/springframework/context/annotation/Configuration.java 第 437-477 行):
java
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
// ......
}
关键两点:
@Configuration被@Component元注解标注 (第 437 行)。类注释第 86 行写明:"Since@Configurationis meta-annotated with@Component,@Configurationclasses are themselves candidates for component scanning"。这意味着配置类本身就是一个 bean,可以被@ComponentScan扫描到------配置类与业务类在容器眼里是平等的组件,只是它额外携带了 bean 定义方法。proxyBeanMethods() default true(第 477 行)。为 true 时,@Configuration类会被ConfigurationClassEnhancer用 CGLIB 增强,保证类内@Bean方法互相调用时返回的是容器里的单例(而不是每次 new 一个新对象);设为 false(@Configuration(proxyBeanMethods = false))则不增强,启动更快但禁止@Bean方法间直接互调。Spring Boot 的启动类大量用proxyBeanMethods = false追求启动性能。
@ComponentScan(同目录 ComponentScan.java)则是包扫描声明,其解析由 ComponentScanAnnotationParser 完成,扫描执行由 ClassPathBeanDefinitionScanner(同目录)完成。入口在 AnnotationConfigApplicationContext(同目录第 57、169、186 行):
java
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
public void register(Class<?>... componentClasses) { ...... }
public void scan(String... basePackages) { ...... }
}
register 注册一个或多个配置类,scan 直接扫包。容器启动时,ConfigurationClassPostProcessor(一个 BeanFactoryPostProcessor,同目录)解析这些配置类:读 @ComponentScan、@Import、@Bean 方法,把它们转成 BeanDefinition 注册进工厂。"约定优于配置"在这里的体现是:用户只要把配置类传给容器,剩下的扫描、解析、注册全由框架按约定完成,不再需要手工列每个 bean。
5.9 扩展点优先:BeanPostProcessor 与 BeanFactoryPostProcessor
Spring 框架自身的功能(AOP、@Autowired、@Configuration 代理、事件监听)几乎都是通过两个扩展点挂上去的,而不是写死在容器里。这就是"扩展点优先"设计哲学:容器预留钩子,功能以插件形式接入。
BeanFactoryPostProcessor(spring-beans/src/main/java/org/springframework/beans/factory/config/BeanFactoryPostProcessor.java 第 70-80 行):
java
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
它在所有 bean 定义加载完成后、任何 bean 实例化之前 被回调,能拿到 ConfigurableListableBeanFactory 去增删改 bean 定义。典型实现就是 ConfigurationClassPostProcessor------它在这个阶段解析 @Configuration 类、扫描包、注册 bean 定义。
BeanPostProcessor(同包 BeanPostProcessor.java 第 67-107 行):
java
public interface BeanPostProcessor {
default @Nullable Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
default @Nullable Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
它在每个 bean 初始化前后 被回调(对应 initializeBean 里的两个 applyBeanPostProcessors*)。注意两个方法都是 default 方法默认原样返回 bean------实现类按需重写。典型实现:AutowiredAnnotationBeanPostProcessor(处理 @Autowired)、CommonAnnotationBeanPostProcessor(处理 @PostConstruct)、AnnotationAwareAspectJAutoProxyCreator(AOP 代理创建)、EventListenerMethodProcessor(把 @EventListener 方法适配成监听器)。
这两个扩展点的调用时机在 refresh() 里是严格排序的:第 5 步先跑 BeanFactoryPostProcessor(此时还没实例化任何业务单例),第 6 步再注册 BeanPostProcessor(必须在第 11 步实例化单例之前注册好,否则拦不到)。这个顺序在 5.1 节的十二步骨架里看得很清楚。
理解了这两个扩展点,就能理解 Spring 的"可插拔"本质:你自己写一个 BeanPostProcessor,就能在不修改容器源码的情况下,给所有 bean 的初始化过程加横切逻辑------这就是 Spring 生态能长这么大的机制基础。
5.10 源码剖析补充:BeanDefinition 元模型与 doGetBean 完整链路
讲完扩展点,还有两个绕不开的"骨架级"类需要补一刀:BeanDefinition(bean 的元数据模型)与 AbstractBeanFactory.doGetBean(取 bean 的总入口)。前面反复提到"bean 定义",这里把它读透。
BeanDefinition 接口 (spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java 第 42 行):
java
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
@Nullable String getBeanClassName();
ConstructorArgumentValues getConstructorArgumentValues();
MutablePropertyValues getPropertyValues();
boolean isLazyInit();
boolean isPrototype();
// getScope, getDependsOn, getRole, getParentName ......
}
BeanDefinition 是"如何创建一个 bean"的元数据描述:bean 类名、构造参数值(ConstructorArgumentValues)、属性值(MutablePropertyValues)、作用域、是否懒加载、依赖哪些 bean、父定义名。它同时继承 AttributeAccessor(附加任意属性)与 BeanMetadataElement(携带资源来源 getResourceDescription(),便于报错时定位是哪个 XML/配置类定义的)。
关键设计:bean 定义与 bean 实例分离 。容器启动时先把所有 BeanDefinition 注册进 BeanDefinitionRegistry,此时一个实例都没创建;BeanFactoryPostProcessor 改的是这个元数据层;直到 preInstantiateSingletons 才根据元数据批量创建实例。这种"定义在前、实例在后"的两阶段,正是 BeanFactoryPostProcessor 能介入的原因------它介入的是元数据阶段。常见实现类有 RootBeanDefinition(合并后的最终定义)、ChildBeanDefinition(继承父定义)、ScannedGenericBeanDefinition(@ComponentScan 扫出来的)、ConfigurationClassBeanDefinition(@Bean 方法产生的)。
AbstractBeanFactory.doGetBean (spring-beans/.../support/AbstractBeanFactory.java 第 236-380 行)是所有 getBean 的总入口。贴主链路:
java
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object @Nullable [] args, boolean typeCheckOnly)
throws BeansException {
String beanName = transformedBeanName(name);
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
beanInstance = getObjectForBeanInstance(sharedInstance, requiredType, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory abf) {
return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
}
// ......按 args/requiredType 委托父工厂
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(......, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
getBean(dep);
}
}
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, requiredType, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
beanInstance = getObjectForBeanInstance(prototypeInstance, requiredType, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
Scope scope = this.scopes.get(scopeName);
Object scopedInstance = scope.get(beanName, () -> { ...... createBean ...... });
beanInstance = getObjectForBeanInstance(scopedInstance, requiredType, name, beanName, mbd);
}
}
// ......类型转换与后处理略
}
逐段拆解这条主链路:
transformedBeanName(name)(第 1271-1272 行):先去掉&前缀、再做别名规范化(canonicalName),把&myFactory、别名aliasA统一成规范 bean 名。- 先查单例缓存 (第 244 行
getSingleton(beanName)):命中直接走getObjectForBeanInstance------这是FactoryBean解包的地方:如果实例是FactoryBean,而传入 name 不带&,就调factory.getObject()返回工厂产品;带&才返回工厂本身。FACTORY_BEAN_PREFIX约定在这里落地。 - prototype 创建中检查 (第 261 行):prototype 不缓存,若正在创建又来要,说明循环依赖,直接抛
BeanCurrentlyInCreationException------prototype 不支持循环依赖,因为它没有三级缓存。 - 父子容器回退 (第 266-284 行):本地没有该 bean 定义时,递归委托
parentBeanFactory.doGetBean。这正是HierarchicalBeanFactory接口在运行时的实现。 getMergedLocalBeanDefinition(第 296 行):把ChildBeanDefinition与父定义合并成一个RootBeanDefinition------"merged"意味着子类定义覆盖父定义后得到最终生效的元数据。dependsOn先于自身创建 (第 300-327 行):显式@DependsOn的 bean 要先getBean(dep)创建好,并检测isDependent防循环 depends-on。registerDependentBean还会维护依赖图,供销毁时逆序排序。- 按作用域三路分支 (第 330-380 行):singleton 走
getSingleton(beanName, ObjectFactory)(带三级缓存与锁);prototype 走beforePrototypeCreation→createBean→afterPrototypeCreation;自定义 scope 走Scope.get(name, objectFactory)(web 的 request/session 就是注册了这个 Scope 接口)。 - 异常时
destroySingleton(第 339 行):单例创建失败时,把可能因循环依赖提前暴露的实例从缓存清掉,避免脏数据残留。
这条链路把前面所有接口串起来了:BeanFactory.getBean → transformedBeanName → getSingleton(三级缓存)→ createBean → doCreateBean(实例化/填充/初始化)→ getObjectForBeanInstance(FactoryBean 解包)。理解它,就拿到了 IoC 容器的主调用栈地图。
5.11 源码剖析补充:DefaultListableBeanFactory 与 7.0 的并行单例启动
前面多次提到 DefaultListableBeanFactory,这里把它作为"默认完整容器实现"单独剖析。类声明(spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java 第 132 行):
java
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
它的继承链是:DefaultListableBeanFactory → AbstractAutowireCapableBeanFactory(创建逻辑)→ AbstractBeanFactory(getBean 模板)→ DefaultSingletonBeanRegistry(单例注册表)。它自己主要补两块:bean 定义的注册与查询 ,以及按类型自动装配的候选解析。
bean 定义的存储(第 191 行):
java
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
一个 ConcurrentHashMap 装所有 bean 定义,外加一个 beanDefinitionNames 列表维护注册顺序。BeanDefinitionRegistry 接口的 registerBeanDefinition 就写进这个 map;getBeanDefinition(String) 从 map 取。配合第 185 行的 autowireCandidateResolver(默认 SimpleAutowireCandidateResolver.INSTANCE),自动装配时判断某个 bean 是否适合作为注入候选。
preInstantiateSingletons 的 7.0 并行化(第 1102-1151 行)------这是 7.0 相对旧版最显著的启动期变化:
java
public void preInstantiateSingletons() throws BeansException {
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
this.preInstantiationThread.set(PreInstantiation.MAIN);
if (this.mainThreadPrefix == null) {
this.mainThreadPrefix = getThreadNamePrefix();
}
try {
List<CompletableFuture<?>> futures = new ArrayList<>();
for (String beanName : beanNames) {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
if (!mbd.isAbstract() && mbd.isSingleton()) {
CompletableFuture<?> future = preInstantiateSingleton(beanName, mbd);
if (future != null) {
futures.add(future);
}
}
}
if (!futures.isEmpty()) {
try {
CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0])).join();
}
catch (CompletionException ex) {
ReflectionUtils.rethrowRuntimeException(ex.getCause());
}
}
}
finally {
this.mainThreadPrefix = null;
this.preInstantiationThread.remove();
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName, false);
if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {
StartupStep smartInitialize = getApplicationStartup().start("spring.beans.smart-initialize")
.tag("beanName", beanName);
smartSingleton.afterSingletonsInstantiated();
smartInitialize.end();
}
}
}
逐段解读:
- 遍历的是
beanDefinitionNames的拷贝 (第 1109 行)。注释解释:初始化方法里可能又注册新 bean 定义,拷贝一份遍历避免ConcurrentModificationException。 - 收集
CompletableFuture并最后allOf().join()(第 1117-1134 行)。不是串行 for 循环getBean,而是每个 bean 可能产出一个 future,主线程最后等所有 future 完成。这是 7.0 为"并行启动"做的骨架。 preInstantiateSingleton支持后台初始化 (第 1153-1197 行):若mbd.isBackgroundInit()且配了BootstrapExecutor,先在主线程强制初始化dependsOn与factoryBean,再用CompletableFuture.runAsync(..., executor)在后台实例化当前 bean,同时通过addSingletonFactory登记一个会future.join()的工厂------保证其他线程来要这个 bean 时,会阻塞等它后台初始化完,而不是拿到半成品。BeanCurrentlyInCreationException在主线程被容忍跳过 (第 1193-1196 行):如果某 bean 已被其他后台线程初始化,主线程不再重复创建,只记一条 info 日志。这与 6.2 节讲的lenientCreationLock/currentCreationThreads是同一套并发模型------7.0 的容器启动已为虚拟线程并行做了系统性改造,不再是旧版"单线程串行实例化所有单例"。- 第二遍循环处理
SmartInitializingSingleton(第 1142-1150 行):所有单例实例化完后,才回调afterSingletonsInstantiated()。注意它与InitializingBean.afterPropertiesSet的区别:后者是每个 bean 自己初始化完 就调,前者是整个容器所有单例都就绪 后才调一次------这是"容器级启动完成"的扩展点,EventListenerMethodProcessor等基础设施就靠它做收尾。
按类型解析候选 :resolveNamedBean(第 1555、1569、1626 行一组重载)实现了"getBean(Class) 怎么找到唯一匹配 bean":先 getBeanNamesForType 列出所有候选,再按 @Primary/@Fallback/autowireCandidate 规则排序,唯一匹配才返回,多个匹配且无主候选则抛 NoUniqueBeanDefinitionException。这套逻辑是 @Autowired 按类型注入的底层支撑------容器不是"随便拿一个",而是经过候选排序与歧义检测。
把 DefaultListableBeanFactory 放回分层图:它是 ConfigurableListableBeanFactory 的默认实现,也是 GenericApplicationContext 内部持有的那个工厂。refresh() 第 11 步 finishBeanFactoryInitialization 最终调的就是这里的 preInstantiateSingletons。读者看到 7.0 启动比旧版快,很大程度就来自这段并行骨架与 singletonLock tryLock 快速路径。
6. 7.0.9 版本痕迹:与旧版资料不同的地方
很多网上的 Spring 源码分析文章停留在 5.x/6.x。本章最后专门把仓库里能实证的 7.0.9 变化集中讲清,避免读者拿旧资料对照时产生困惑。
6.1 JDK 工具链:构建用 JDK 25,字节码基线 release 17
工具链配置不在根 build.gradle,而在 buildSrc 的约定插件里。读 buildSrc/src/main/java/org/springframework/build/JavaConventions.java:
java
private static final JavaLanguageVersion DEFAULT_LANGUAGE_VERSION = JavaLanguageVersion.of(25);
private static final JavaLanguageVersion DEFAULT_RELEASE_VERSION = JavaLanguageVersion.of(17);
- 第 48 行:构建工具链用 JDK 25 (
applyToolchainConventions第 87-89 行把toolchain.languageVersion设为 25)。 - 第 54 行:编译产物的
-release基线是 JDK 17 (setJavaRelease第 122-133 行)。也就是说,Spring Framework 7.0.9 要求运行环境最低 JDK 17,但开发团队自己用 JDK 25 编译。
此外 spring-core/spring-core.gradle 第 13-15 行配置了多版本 JAR:
groovy
multiRelease {
releaseVersions 21, 24
}
即打出的 jar 是 Multi-Release JAR,在 META-INF/versions/21、META-INF/versions/24 下放针对新 JDK 的优化实现(例如 VirtualThreadDelegate 在 JDK 21+ 走虚拟线程实现,低版本走普通线程)。读者在 spring-core/src/main/java/org/springframework/core/task/ 看到的 VirtualThreadDelegate.java 就是这种版本分化的入口。
gradle.properties 里另外两个版本常量也以仓库为准:kotlinVersion=2.2.21、byteBuddyVersion=1.17.6(ByteBuddy 用于代理生成与 mock)。
6.2 锁机制:DefaultSingletonBeanRegistry 全面 ReentrantLock 化
这是 7.0 最值得讲的变化。spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java 第 82-137 行的字段区:
java
/** Common lock for singleton creation. */
final Lock singletonLock = new ReentrantLock();
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Creation-time registry of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>(16);
/** Custom callbacks for singleton creation/registration. */
private final Map<String, Consumer<Object>> singletonCallbacks = new ConcurrentHashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = Collections.synchronizedSet(new LinkedHashSet<>(256));
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation = ConcurrentHashMap.newKeySet(16);
/** Specific lock for lenient creation tracking. */
private final Lock lenientCreationLock = new ReentrantLock();
/** Specific lock condition for lenient creation tracking. */
private final Condition lenientCreationFinished = this.lenientCreationLock.newCondition();
/** Names of beans that are currently in lenient creation. */
private final Set<String> singletonsInLenientCreation = new HashSet<>();
/** Map from one creation thread waiting on a lenient creation thread. */
private final Map<Thread, Thread> lenientWaitingThreads = new HashMap<>();
/** Map from bean name to actual creation thread for currently created beans. */
private final Map<String, Thread> currentCreationThreads = new ConcurrentHashMap<>();
逐点对照旧版差异:
- 三级缓存全部是
ConcurrentHashMap(第 86、89、95 行)。旧版singletonFactories是new HashMap<>(16),因为它只在singletonLock内访问;7.0 改成ConcurrentHashMap,配合下面的 tryLock 快速路径。 singletonLock是包级可见的ReentrantLock(第 83 行,注意不是private,同包的AbstractBeanFactory等可以协同使用)。旧版这里没有独立锁对象,而是synchronized (this.singletonObjects)------以一级缓存 map 自身当监视器。getSingleton(String, boolean)(第 208-244 行)用tryLock()快速失败:
java
protected @Nullable Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock.
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
if (!this.singletonLock.tryLock()) {
// Avoid early singleton inference outside of original creation thread.
return null;
}
try {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// Singleton could have been added or removed in the meantime.
if (this.singletonFactories.remove(beanName) != null) {
this.earlySingletonObjects.put(beanName, singletonObject);
}
else {
singletonObject = this.singletonObjects.get(beanName);
}
}
}
}
}
finally {
this.singletonLock.unlock();
}
}
}
return singletonObject;
}
关键差异:跨线程来查早期引用时,如果原创建线程正持有锁,tryLock() 失败直接返回 null,而不是阻塞等待。注释写得很明白:"Avoid early singleton inference outside of original creation thread"------早期引用的生成只允许在原创建线程内做,其他线程此时应返回 null 让上层按"还没准备好"处理,避免并发下推断出状态不一致的半成品。这是为虚拟线程高并发场景做的设计:高并发下大量线程同时 getBean,阻塞等待会放大锁竞争。
-
新增"宽松创建"(lenient creation)机制 :
getSingleton(String, ObjectFactory)(第 255 行起)开头先调isCurrentThreadAllowedToHoldSingletonLock(),再this.singletonLock.tryLock();抢不到锁时进入lenientCreationLock保护下的宽松创建路径,把当前 bean 记入singletonsInLenientCreation,并在lenientWaitingThreads里登记等待关系。这是 6.2 引入、7.0 完善的并发创建优化:当一个线程持有锁正在创建 bean A,另一个线程来创建不相关的 bean B 时,不必干等 A 创建完,可以走宽松路径并行创建 B,最后通过currentCreationThreads与lenientCreationFinished条件汇合。 -
新增
singletonCallbacks(第 92、169-172 行):addSingletonCallback允许在单例注册完成后收到回调(Consumer<Object>)。这是 7.0 给 AOT/运行时 hints 等场景加的钩子。
6.3 日志桥接:spring-jcl 模块消失,日志抽象迁入 spring-core
旧版有独立 spring-jcl 模块,把 org.apache.commons.logging.LogFactory 桥接到 JDK Logging/Log4j。本仓库 settings.gradle 的 27 个 include 里没有 spring-jcl,Glob **/jcl/** 无结果。日志抽象现在位于 spring-core/src/main/java/org/springframework/core/log/:LogAccessor(门面)、LogDelegateFactory(按 classpath 存在的日志实现选择 delegate)、CompositeLog(组合多个 log)、LogMessage(延迟格式化消息)。
但 spring-core.gradle 第 78 行仍 api("commons-logging:commons-logging")------即 7.0.9 仍以 commons-logging API 作为兼容入口,只是不再自带桥接模块,而是由 LogDelegateFactory 在运行期探测 classpath 上的真实日志实现(Log4j2/ SLF4J/JUL)。
更硬的证据在 buildSrc/.../ArchitectureRules.java 第 49-59 行:ArchUnit 规则禁止业务类直接依赖 org.slf4j.LoggerFactory ------框架代码不许绕过自家日志抽象直接碰 SLF4J,必须走 LogAccessor。这条规则保证了日志门面不被穿透。
6.4 可空注解:从 spring-core.lang 迁到 JSpecify
旧版用 org.springframework.lang.Nullable/@NonNull。7.0.9 里 BeanFactory.java 第 19 行是 import org.jspecify.annotations.Nullable;;spring-core.gradle 第 79 行 api("org.jspecify:jspecify");ArchitectureRules.java 第 54-58 行禁止依赖老的 org.springframework.lang.NonNull/Nullable。这是一次面向 JSpecify 标准的迁移------org.springframework.lang 包在 7.0 仅为兼容保留,新代码一律用 JSpecify 注解。
6.5 架构约束被 ArchUnit 锁死
buildSrc/.../ArchitectureRules.java 用 ArchUnit 定义了一组架构守卫:
allPackagesShouldBeFreeOfTangles()(第 30-33 行):整个框架包结构不得出现循环依赖(SlicesRuleDefinition.slices()...beFreeOfCycles()),并忽略org.springframework.asm/cglib/javapoet/objenesis这几个内嵌 shaded 包。noClassesShouldCallStringToLowerCaseWithoutLocale()/toUpperCase(第 35-47 行):禁止无 Locale 的大小写转换,强制Locale.ROOT。classesShouldNotImportForbiddenTypes()(第 49-59 行):禁直接依赖reactor.core.support.Assert、org.slf4j.LoggerFactory、老的 spring-lang 注解。javaClassesShouldNotImportKotlinAnnotations()(第 61-74 行):Java 源文件不得依赖org.jetbrains.annotations。
这些规则是"可执行的架构文档":每次构建都会跑,违反即失败。第 3 节讲的模块依赖方向,除了 Gradle 配置,还有这组 ArchUnit 规则在包级别兜底。
6.6 补充:AOT / GraalVM Native Image 支持
7.0.9 还能看到 GraalVM Native Image 适配的痕迹。prepareBeanFactory 第 756 行有 NativeDetector.inNativeImage() 判断------在原生镜像里跳过 LoadTimeWeaver 织入。spring-core.gradle 第 82 行 compileOnly("org.graalvm.sdk:graal-sdk") 与第 120 行 jar manifest 的 Dependencies 属性(jdk.unsupported,org.jboss.vfs,注释注明 "for WildFly (Objenesis and VfsUtils)")都说明:核心库为原生镜像做了条件化分支------在 JIT 环境与 AOT 静态编译环境走不同代码路径。配合 buildSrc/.../RuntimeHintsAgentPlugin.java(运行时 hints 采集 agent),Spring 7 把"反射/动态代理需要在构建期登记元数据"这件事做成了构建工具链的一环。这也是为什么 spring-core 内嵌 javapoet(代码生成)与 objenesis(绕过构造器):原生镜像不支持运行期随便反射,能代码生成就代码生成、能直接实例化就别走反射。
7. 运行机制:把源码串成一次完整启动
前面按"类"拆开讲,本节按"一次 new AnnotationConfigApplicationContext(AppConfig.class).refresh() 的执行流"把骨架串起来,并讲清数据与锁如何流转。
7.1 启动时序:从构造到就绪
scss
用户线程
│
├─ new AnnotationConfigApplicationContext(AppConfig.class)
│ └─ 父类 GenericApplicationContext 构造时新建 DefaultListableBeanFactory
│
├─ refresh() [AbstractApplicationContext:581]
│ │ startupShutdownLock.lock() ← 7.0.9 ReentrantLock
│ │
│ ├─ prepareRefresh() ← 置启动时间、初始化事件列表
│ ├─ obtainFreshBeanFactory()
│ │ └─ obtainFreshBeanFactory → 子类加载/复用 BeanFactory
│ │ (GenericApplicationContext 直接拿内部工厂)
│ ├─ prepareBeanFactory(beanFactory) ← 注册 AwareProcessor、解析器
│ ├─ postProcessBeanFactory(beanFactory) ← 子类钩子(默认空)
│ ├─ invokeBeanFactoryPostProcessors(beanFactory)
│ │ └─ ConfigurationClassPostProcessor 解析 @Configuration
│ │ → @ComponentScan 扫描 → 注册 BeanDefinition
│ │ → @Import / @Bean 方法 → 注册 BeanDefinition
│ ├─ registerBeanPostProcessors(beanFactory) ← 把 BPP 列表注册进工厂
│ │ (AutowiredAnnotationBPP、CommonAnnotationBPP、
│ │ ConfigurationClassPostProcessor 本身等)
│ ├─ initMessageSource() ← 找/建 MessageSource bean
│ ├─ initApplicationEventMulticaster() ← 找/建 ApplicationEventMulticaster
│ ├─ onRefresh() ← 子类钩子
│ │ (Web 容器在此启动内嵌服务器)
│ ├─ registerListeners() ← 把 ApplicationListener bean 接入多播器
│ ├─ finishBeanFactoryInitialization(beanFactory)
│ │ └─ beanFactory.preInstantiateSingletons()
│ │ └─ 对每个非懒加载单例 getBean → doGetBean → doCreateBean
│ │ ├─ createBeanInstance(实例化)
│ │ ├─ applyMergedBeanDefinitionPostProcessors
│ │ ├─ addSingletonFactory(三级缓存登记,循环依赖用)
│ │ ├─ populateBean(属性注入)
│ │ └─ initializeBean(Aware + BPP-before + init + BPP-after)
│ └─ finishRefresh()
│ ├─ lifecycleProcessor.onRefresh()
│ └─ publishEvent(ContextRefreshedEvent) ← 观察者链触发
│
└─ 容器就绪,可对外 getBean / 接收请求
这条时序里有三个关键的"顺序不可调换"约束,全在源码里:
BeanFactoryPostProcessor必须在BeanPostProcessor之前跑(第 5 步 vs 第 6 步)。因为 BFPP 要改 bean 定义,而 BPP 是拦 bean 实例的------定义都没定型,注册 BPP 没有意义。BeanPostProcessor必须在preInstantiateSingletons之前注册 (第 6 步 vs 第 11 步)。否则业务单例实例化时 BPP 还没就位,@Autowired都不会生效。- 事件多播器必须在
registerListeners之前初始化(第 8 步 vs 第 10 步),监听器要注册到一个已存在的多播器上。
7.2 数据与锁的流转
容器启动期间,并发模型由两把锁主导:
startupShutdownLock(ReentrantLock,AbstractApplicationContext:214) :保护整个refresh()/close()的互斥。同一时刻只允许一个线程刷新/关闭上下文;doClose()用tryLock(100ms)等待正在刷新的线程收尾。singletonLock(ReentrantLock,DefaultSingletonBeanRegistry:83) :保护单例注册表的写入。getSingleton(String, ObjectFactory)创建单例时先tryLock,抢不到且判定为宽松创建场景时走lenientCreationLock+singletonsInLenientCreation并行创建;读路径(getSingleton(String, boolean))先无锁查一级/二级缓存,只有需要生成早期引用时才tryLock,抢不到直接返回 null。
缓存层级是:singletonObjects(成品,一级)→ earlySingletonObjects(半成品早期引用,二级)→ singletonFactories(ObjectFactory,三级)。doCreateBean 在实例化后、属性填充前调 addSingletonFactory 登记三级缓存,这样循环依赖的另一方在 populateBean 时能拿到早期引用;bean 创建完成后 addSingleton(第 159 行)把成品放入一级缓存,并 remove 掉二三级对应条目。
这套设计的核心权衡是:绝大多数 getBean 走无锁快速路径 (查 singletonObjects 即返回),只有并发创建循环依赖 bean 才进锁;7.0 的 tryLock 快速失败进一步把"跨线程推断早期引用"从阻塞变成返回 null,适配虚拟线程高并发。
8. 扩展用法与最佳实践
8.1 正确利用扩展点
- 写横切逻辑优先实现
BeanPostProcessor而非改容器 。实现postProcessAfterInitialization,在 bean 初始化完成后包一层代理或做校验。注意 BPP 是对所有 bean 生效的,要用beanName/bean instanceof过滤目标,避免给基础设施 bean(如ConfigurationClassPostProcessor自己)也套逻辑。 - 改 bean 定义用
BeanFactoryPostProcessor,不要用BeanPostProcessor。BFPP 阶段 bean 还没实例化,适合注册额外 bean 定义、修改 scope;BPP 阶段 bean 已经 new 出来了,改定义晚了。 - 自定义属性转换 :实现
Converter<S,T>并注册到ConversionService,不要在每个@Value处手写解析。
8.2 线程安全与性能注意点
- 单例 bean 本身要自己保证线程安全。容器只保证单例只有一个实例,不保证你的 bean 方法是线程安全的------无状态 bean 天然安全,有状态 bean(如带可变字段的 Service)要加锁或改用 prototype。
prototype作用域的 bean 注入到单例时只会注入一次 。要拿到每次新实例,得用ObjectFactory<T>/Provider<T延迟查找,或注入ApplicationContext每次getBean。- 7.0 的虚拟线程支持 :在高并发 IO 场景,把
ApplicationEventMulticaster的 taskExecutor 设为VirtualThreadTaskExecutor,事件回调即异步跑在虚拟线程上;同步监听器异常不会互相阻断(SimpleApplicationEventMulticaster有 ErrorHandler)。
8.3 与相邻机制的配合
- AOP 代理与循环依赖 :
@Async/@Transactional注解 bean 互相依赖时,靠的就是三级缓存里的getEarlyBeanReference------SmartInstantiationAwareBeanPostProcessor在早期引用阶段就生成代理,保证循环双方拿到的是同一个代理对象。若把allowCircularReferences关掉(7.0 默认推荐显式声明),构造器循环依赖直接报错,这是正确的 fail-fast。 - 事件与事务 :
@Transactional方法里publishEvent默认是同步的(多播器没配 executor),监听器会加入当前事务上下文;要异步就配ApplicationEventMulticaster的 taskExecutor,但要意识到异步监听器脱离了原事务。
7.2 补充剖析:prepareBeanFactory 到底装配了哪些基础设施
refresh() 第 3 步 prepareBeanFactory(AbstractApplicationContext.java 第 729-775 行)常被略读,但它揭示了"上下文凭什么比裸 BeanFactory 多那么多能力"。逐行看这段装配:
java
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
// ......systemProperties / systemEnvironment / applicationStartup 同理
}
这段代码把"上下文能力"注入裸工厂,分五类:
- 表达式与属性编辑器 :
StandardBeanExpressionResolver让@Value("#{...}")的 SpEL 表达式能解析;ResourceEditorRegistrar注册Resource类型的属性编辑器,让@Value("classpath:config.yml")能自动转成Resource对象。 ApplicationContextAwareProcessor:一个BeanPostProcessor,在 bean 初始化前检测它是否实现EnvironmentAware/ResourceLoaderAware/ApplicationEventPublisherAware/MessageSourceAware/ApplicationContextAware,若是就回调对应的 setter。注意这与第 4 节ConfigurableListableBeanFactory.ignoreDependencyInterface的配合:这些 Aware 接口被显式忽略------即"不要按类型自动装配它们",而是由这个 BPP 专用回调注入。这就是为什么ApplicationContext本身能被注入:它通过registerResolvableDependency登记成"可解析依赖"。registerResolvableDependency:把BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext四个类型直接映射到上下文自身。于是业务 bean@Autowired ApplicationContext ctx能拿到容器------不是因为有个叫applicationContext的 bean,而是因为这个"可解析依赖"注册表。ApplicationListenerDetector:早注册的 BPP,负责检测哪些 bean 是ApplicationListener实现并登记到多播器(对应第 10 步registerListeners)。- 默认环境单例 :
environment、systemProperties、systemEnvironment、applicationStartup四个单例在此注册------业务代码@Value("${...}")、注入Environment的背后支撑。
这节补充说明了一个关键判断:ApplicationContext 相对 BeanFactory 的"增值",不是靠新容器,而是在裸工厂上通过 prepareBeanFactory 注册了一批 BPP 与可解析依赖 。这也是为什么 GenericApplicationContext 内部持有的 DefaultListableBeanFactory 一旦 refresh(),就自动具备了 Aware 回调、SpEL、环境注入等能力------一切都是这条装配线的产物。
9. 常见问题与坑(源码级解释)
9.1 为什么我自己 new 的对象不是 Spring bean,注入不进去?
因为 Spring 的 DI 只作用于容器管理的 bean 。AbstractAutowireCapableBeanFactory.doCreateBean 只处理经过 BeanDefinition 注册、由容器 createBean 出来的对象;你 new 出来的对象连 populateBean/initializeBean 都没走过,自然没有 @Autowired 注入。源码层面,@Autowired 的处理者 AutowiredAnnotationBeanPostProcessor 只在 initializeBean 的 applyBeanPostProcessorsBeforeInitialization 阶段被调用,手工 new 的对象进不了这个流程。要让外部对象也走一遍生命周期,可调 AutowireCapableBeanFactory.initializeBean(bean, name),但这属于例外用法。
9.2 为什么单例 bean 里注入 prototype bean,prototype 只生效一次?
preInstantiateSingletons 只实例化单例一次;prototype bean 是在每次 getBean 时才 doCreateBean。单例 bean 在容器启动时被创建,它对 prototype 依赖的注入只发生在那一次 populateBean------之后单例一直持有第一次拿到的 prototype 引用。源码层面,prototype 不进入三级缓存、不参与 preInstantiateSingletons,每次都新建。解法:单例里注入 ObjectFactory<MyProto> 或 Provider<MyProto>,每次调 getObject()/get() 触发新一次 getBean。
9.3 为什么构造器循环依赖报 BeanCurrentlyInCreationException,而 setter 循环依赖能跑?
源码在 doCreateBean:addSingletonFactory 登记三级缓存发生在 createBeanInstance(实例化完成)之后、populateBean(属性填充)之前。setter 循环依赖时,A 构造完就把早期引用放进三级缓存,B 在 populateBean 时能拿到 A 的早期引用;但构造器循环依赖时,A 还在构造器里------createBeanInstance 都没返回,三级缓存还没登记,B 在 A 的构造器里就要 A 的引用,自然拿不到。这是"提前暴露只能暴露已构造完对象"这一源码事实的直接后果。7.0 推荐显式 setAllowCircularReferences(false),让构造器循环依赖 fail-fast。
9.4 为什么 @Transactional/@Async 自调用失效?
这类注解靠 AOP 代理生效:容器为目标 bean 生成代理对象,代理在方法调用时先织入事务/异步逻辑。但自调用 this.method() 走的是原始对象引用,不经过代理------源码层面,ProxyFactory.getProxy() 返回的代理才织入拦截器链,this 指向的是 doCreateBean 里那个原始 bean,不是代理对象。解法:把自调用拆到另一个 bean,或注入自身代理(@Lazy self/AopContext.currentProxy())。
9.5 为什么 BeanPostProcessor 里注入的 bean 没有 @Autowired 效果?
BPP 自身也是 bean,但它在 refresh() 第 6 步就被注册进工厂,早于第 11 步业务单例实例化。BPP 自己的 @Autowired 依赖要在 BPP 实例化时解析------此时很多业务 bean 还不存在。源码层面,registerBeanPostProcessors 阶段实例化 BPP 时,容器只保证基础设施 bean 就绪,BPP 不要注入普通业务单例,否则会触发过早实例化甚至循环依赖。
9.6 为什么 spring-web 里的 RestClient 能脱离 Spring 容器单独用?
因为 spring-web.gradle 对 spring-context 是 optional 而非 api:RestClient/HttpMessageConverter 这些类的源码不引用 ApplicationContext,只依赖 spring-beans+spring-core 的抽象。这是构建期就锁死的解耦------如果你看到 RestClient 源码里出现 org.springframework.context 的 import,构建会被 ArchUnit 的包规则卡住。理解这一点,就理解了为什么"Web 层 ≠ IoC 容器"。
10. 阅读源码路线图
给资深开发者的建议顺序,每一步都给出理由:
- 先读
BeanFactory接口本身 (spring-beans/.../factory/BeanFactory.java)。它是整个容器的契约,类注释里的生命周期顺序表就是全局地图。理由:先知道"容器承诺做什么",再看实现。 - 顺着继承链往下读 :
HierarchicalBeanFactory→ListableBeanFactory→ConfigurableBeanFactory→ConfigurableListableBeanFactory。每读一个接口问"它比父接口多了什么能力"。理由:接口继承链就是能力分层图,比直接读实现类高效。 - 读
AbstractApplicationContext.refresh()(spring-context/.../support/AbstractApplicationContext.java:581)。把十二步骨架背下来,再逐 step 跳进对应方法。理由:refresh 是容器启动的总调度,抓住它就抓住了主线。 - 读
DefaultListableBeanFactory与AbstractAutowireCapableBeanFactory:从doGetBean→getSingleton→doCreateBean→createBeanInstance/populateBean/initializeBean一路跟进去。理由:这是单例创建的完整调用链,理解了它就理解了 IoC 90%。 - 读
DefaultSingletonBeanRegistry:重点看三级缓存字段与getSingleton(String, boolean)的 tryLock 快速路径。理由:这是 7.0 并发模型变化最集中的地方。 - 再读扩展点 :
BeanPostProcessor/BeanFactoryPostProcessor接口,然后看AutowiredAnnotationBeanPostProcessor、ConfigurationClassPostProcessor两个典型实现。理由:扩展点是"框架怎么被插件化"的关键。 - Web 栈只看
DispatcherServlet.doDispatch:顺着getHandler→getHandlerAdapter→ha.handle→render把三个策略串一遍即可,不必细读每个实现。理由:Web 机制是 IoC 之上的策略装配,主线清楚即可。 - 最后看
build.gradle/ArchitectureRules.java:反过来用构建脚本验证你对模块分层的理解。理由:架构规则是可执行文档,能检验你有没有读懂依赖方向。
不建议一上来就读 spring-context 的 74677 行全部源码------应先抓接口与骨架,再按需钻进具体实现。本章后面各主题章节会沿这条路线展开具体机制。
11.1 本章边界:哪些内容刻意只点到为止
为避免与"逐机制深入"的后续主题重复,本章只做骨架判断,以下内容仅点名而未展开:循环依赖的三级缓存完整推导(见 6.2 字段与 getSingleton(String, boolean),但未逐行走完 populateBean 反向解析);AOP 代理的 JDK 动态代理与 CGLIB 两种创建分支(ProxyFactory.createAopProxy 的选择逻辑);@Configuration 类的 CGLIB 增强原理(ConfigurationClassEnhancer);事务抽象 PlatformTransactionManager/TransactionDefinition 的策略族;WebFlux 的响应式契约 vs WebMVC 的 Servlet 契约差异。读者可按第 10 节路线图,从 DefaultListableBeanFactory、AbstractAutoProxyCreator、DispatcherServlet 三个入口分别深入。本章的价值在于:在钻进任何一个机制前,先建立"模块怎么分层、接口怎么继承、设计模式落在哪几个类、7.0 改了哪些锁"这张地图------有了地图,单点深入才不会迷路。
11. 本章小结
本章站在 7.0.9 仓库全景高度,做了五件事:
- 用实测数字建立规模感 :22 个 spring-* 模块约 5200 个
.java文件、72.5 万行 main 代码;spring-core(11.2 万行)与spring-web(11.1 万行)是两个等量级巨兽,spring-beans(5.5 万行)与spring-context(7.5 万行)构成 IoC 双层引擎,数据访问与 Web 适配层都是薄适配。 - 用 build.gradle 实证分层与依赖规则:core 零内部依赖;beans 只依赖 core;aop 依赖 beans 但不依赖 context;context 组合 aop/beans/core/expression;web 对 context 是 optional;webmvc 才硬依赖 context;数据访问层统一站在 beans+core 之上。这五条规则被 Gradle 配置 + ArchUnit 双重锁死。
- 逐个读接口文件讲清顶层抽象 :
BeanFactory五层链(Hierarchical/Listable/Configurable/ConfigurableListable)按"客户端视图→容器管理者视图"递进;ApplicationContext一次组合六个角色;Resource/ResourceLoader、ConversionService、MessageSource、Environment 各司其职。 - 把设计哲学落到具体类 :模板方法(
refresh()十二步、doCreateBean、AbstractMessageSourcefinal 骨架+abstract 钩子)、策略(HandlerMapping/Adapter/ViewResolver、TaskExecutor)、观察者(ApplicationEventMulticaster + SimpleApplicationEventMulticaster)、组合与适配器(ProxyFactory、Resource 体系)、约定优于配置(@Configuration 元注解 @Component、proxyBeanMethods 默认 true、@ComponentScan)、扩展点优先(BeanPostProcessor/BeanFactoryPostProcessor)。 - 明确 7.0.9 与旧版的差异 :构建用 JDK 25、字节码基线 release 17、多版本 JAR(21/24);
singletonLock取代synchronized(singletonObjects)、三级缓存全 ConcurrentHashMap、tryLock 快速失败、新增 lenient creation 机制;spring-jcl 模块消失、日志抽象迁入org.springframework.core.log;可空注解迁到 JSpecify;ArchUnit 规则锁死包级架构。
一句话概括:Spring Framework 的骨架是"窄接口分层 + 模板方法固定流程 + 策略/观察者可插拔扩展 + 扩展点优先插件化",模块依赖方向被构建脚本硬编码,7.0 进一步把锁机制 ReentrantLock 化以适配虚拟线程高并发。
本章关键类清单
| 类名 | 模块与相对路径 | 职责一句话 |
|---|---|---|
BeanFactory |
spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java | IoC 容器根接口:按名字/类型取 bean |
HierarchicalBeanFactory |
spring-beans/src/main/java/org/springframework/beans/factory/HierarchicalBeanFactory.java | 增加父子工厂层级与本地查询 |
ListableBeanFactory |
spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java | 可列举:按类型枚举所有 bean |
ConfigurableBeanFactory |
spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java | 可配置:设置父工厂、注册 BPP/作用域 |
ConfigurableListableBeanFactory |
spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableListableBeanFactory.java | 汇合点:可内省定义 + 预实例化单例 |
ApplicationContext |
spring-context/src/main/java/org/springframework/context/ApplicationContext.java | 门面接口:组合容器+环境+消息+事件+资源 |
ConfigurableApplicationContext |
spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java | 加 refresh/close/getBeanFactory 生命周期 |
AbstractApplicationContext |
spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java | refresh() 十二步模板方法骨架 |
GenericApplicationContext |
spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java | 通用可复用实现,实现 BeanDefinitionRegistry |
AnnotationConfigApplicationContext |
spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java | 注解驱动容器:register/scan 入口 |
DefaultListableBeanFactory |
spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java | 最完整的默认 bean 工厂实现 |
DefaultSingletonBeanRegistry |
spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java | 三级缓存、singletonLock、lenient creation |
AbstractAutowireCapableBeanFactory |
spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java | doCreateBean/initializeBean 创建骨架 |
BeanPostProcessor |
spring-beans/src/main/java/org/springframework/beans/factory/config/BeanPostProcessor.java | bean 初始化前后横切扩展点 |
BeanFactoryPostProcessor |
spring-beans/src/main/java/org/springframework/beans/factory/config/BeanFactoryPostProcessor.java | bean 定义加载后、实例化前扩展点 |
Resource |
spring-core/src/main/java/org/springframework/core/io/Resource.java | 统一资源抽象 |
ResourceLoader |
spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java | 按位置字符串选 Resource 的工厂 |
DefaultResourceLoader |
spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java | 前缀分发策略 + ProtocolResolver 扩展链 |
ConversionService |
spring-core/src/main/java/org/springframework/core/convert/ConversionService.java | 类型转换策略总线 |
MessageSource |
spring-context/src/main/java/org/springframework/context/MessageSource.java | 国际化消息解析接口 |
AbstractMessageSource |
spring-context/src/main/java/org/springframework/context/support/AbstractMessageSource.java | final 骨架 + abstract resolveCode 钩子 |
Environment |
spring-core/src/main/java/org/springframework/core/env/Environment.java | profile + PropertyResolver 环境抽象 |
DispatcherServlet |
spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java | doDispatch 调度三个策略族 |
HandlerMapping |
spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java | 请求→处理器执行链策略 |
HandlerAdapter |
spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerAdapter.java | 处理器调用策略 |
ViewResolver |
spring-webmvc/src/main/java/org/springframework/web/servlet/ViewResolver.java | 视图名→View 策略 |
ApplicationEventMulticaster |
spring-context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java | 事件多播器接口 |
SimpleApplicationEventMulticaster |
spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java | 同步/异步事件分发实现 |
ProxyFactory |
spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java | AOP 代理工厂:组合通知+目标+策略 |
VirtualThreadTaskExecutor |
spring-core/src/main/java/org/springframework/core/task/VirtualThreadTaskExecutor.java | JDK 21+ 虚拟线程 TaskExecutor 实现 |
Configuration |
spring-context/src/main/java/org/springframework/context/annotation/Configuration.java | 配置类注解(元注解 @Component) |
JavaConventions |
buildSrc/src/main/java/org/springframework/build/JavaConventions.java | 工具链 JDK 25、release 17 约定 |
ArchitectureRules |
buildSrc/src/main/java/org/springframework/build/architecture/ArchitectureRules.java | ArchUnit 架构守卫规则 |
后续章节将沿本章给出的路线图,逐个深入 IoC 容器创建、循环依赖解析、AOP 代理、事件机制、Web 调度等具体主题------本章只负责把骨架与设计判断讲透。