Spring Framework 7.0.9 深度剖析-01-整体架构与设计哲学

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.javapoet relocate 成 org.springframework.javapoet、把 org.objenesis relocate 成 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 实证:

  1. core 是唯一的"零内部依赖"模块 。它不 import 任何其他 spring 模块的类(buildSrc/.../ArchitectureRules.java 还通过 ArchUnit SlicesRuleDefinition.slices()...beFreeOfCycles() 强制整个框架包结构无环)。
  2. beans 只许依赖 core,绝不反向依赖 context。这是 IoC 引擎可被"单独拿去嵌入其他容器"的前提。
  3. aop 依赖 beans,但不依赖 context;context 反过来依赖 aop------方向是 context → aop → beans,绝不允许 aop → context 的反向引用。
  4. web 层对 context 是 optional:web/webflux 可以脱离 IoC 容器做纯 HTTP 编程;webmvc 因为要接管 Servlet 生命周期与注解映射,才硬依赖 context。
  5. 数据访问层(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 资源处理
	}
}

逐段解读:

  1. 先过 ProtocolResolver 扩展链 。protocolResolvers 是一个 LinkedHashSet(第 54 行),用户可通过 addProtocolResolver 注册自定义协议解析器(例如支持 myprotocol:xxx 这种自定义 scheme)。这是"策略链"扩展点------内置规则处理不了的位置,先问外部注册的策略。
  2. 按 / 开头判断走 getResourceByPath 。这是一个 protected 钩子方法,FileSystemXmlApplicationContext 之类的子类可以重写它,把 /xxx 解释成文件系统路径而非 classpath 路径------模板方法钩子。
  3. classpath: 前缀走 ClassPathResource。
  4. 其余一律先尝试当 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();
	}
}

逐段解读十二步:

  1. prepareRefresh()(第 667 行):刷新前预处理------校验必要属性、初始化早期事件列表、记录启动时间。
  2. obtainFreshBeanFactory() (第 719 行):告诉子类刷新内部 bean 工厂。AbstractRefreshableApplicationContext 的实现会新建一个 DefaultListableBeanFactory 并加载 bean 定义;GenericApplicationContext 则复用构造时就建好的工厂。这是模板方法留给子类的第一个可变点。
  3. prepareBeanFactory(beanFactory) (第 729 行):配置工厂------设置类加载器、表达式解析器、注册 ApplicationContextAwareProcessor 等内建后处理器、把 Environment/ResourceLoader 等依赖列为可解析。
  4. postProcessBeanFactory(beanFactory) (第 786 行):空实现的 protected 钩子,留给子类(如 WebApplicationContext 子类)在工厂就绪后加额外后处理器。
  5. invokeBeanFactoryPostProcessors(beanFactory) (第 794-795 行):委托 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors,执行所有 BeanFactoryPostProcessor------这一步会解析 @Configuration 类、扫描 @ComponentScan、注册 bean 定义。
  6. registerBeanPostProcessors(beanFactory) (第 811-812 行):同样委托给 PostProcessorRegistrationDelegate,把所有 BeanPostProcessor 注册进工厂------注意此时单例 bean 还没开始实例化,后处理器必须先注册好,才能拦截后续 bean 的创建。
  7. initMessageSource():初始化国际化消息源。
  8. initApplicationEventMulticaster()(第 853 行):初始化事件多播器(观察者模式的核心)。
  9. onRefresh() (第 906 行):protected 钩子,默认空。GenericWebApplicationContext 在这里初始化 Servlet 容器的主题源;Spring Boot 的 ServletWebServerApplicationContext 在这里启动内嵌 Web 服务器。这是模板方法最经典的扩展点------"容器就绪后、单例实例化前"这个时机。
  10. registerListeners() (第 914 行):把 ApplicationListener bean 注册到多播器。
  11. finishBeanFactoryInitialization(beanFactory) (第 942 行):调用 beanFactory.preInstantiateSingletons(),一次性实例化所有非懒加载单例------容器启动的"重活"在这一步。
  12. 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;
	// ......
}

关键两点:

  1. @Configuration 被 @Component 元注解标注 (第 437 行)。类注释第 86 行写明:"Since @Configuration is meta-annotated with @Component, @Configuration classes are themselves candidates for component scanning"。这意味着配置类本身就是一个 bean,可以被 @ComponentScan 扫描到------配置类与业务类在容器眼里是平等的组件,只是它额外携带了 bean 定义方法。
  2. 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);
		}
	}
	// ......类型转换与后处理略
}

逐段拆解这条主链路:

  1. transformedBeanName(name) (第 1271-1272 行):先去掉 & 前缀、再做别名规范化(canonicalName),把 &myFactory、别名 aliasA 统一成规范 bean 名。
  2. 先查单例缓存 (第 244 行 getSingleton(beanName)):命中直接走 getObjectForBeanInstance------这是 FactoryBean 解包的地方:如果实例是 FactoryBean,而传入 name 不带 &,就调 factory.getObject() 返回工厂产品;带 & 才返回工厂本身。FACTORY_BEAN_PREFIX 约定在这里落地。
  3. prototype 创建中检查 (第 261 行):prototype 不缓存,若正在创建又来要,说明循环依赖,直接抛 BeanCurrentlyInCreationException------prototype 不支持循环依赖,因为它没有三级缓存。
  4. 父子容器回退 (第 266-284 行):本地没有该 bean 定义时,递归委托 parentBeanFactory.doGetBean。这正是 HierarchicalBeanFactory 接口在运行时的实现。
  5. getMergedLocalBeanDefinition (第 296 行):把 ChildBeanDefinition 与父定义合并成一个 RootBeanDefinition------"merged"意味着子类定义覆盖父定义后得到最终生效的元数据。
  6. dependsOn 先于自身创建 (第 300-327 行):显式 @DependsOn 的 bean 要先 getBean(dep) 创建好,并检测 isDependent 防循环 depends-on。registerDependentBean 还会维护依赖图,供销毁时逆序排序。
  7. 按作用域三路分支 (第 330-380 行):singleton 走 getSingleton(beanName, ObjectFactory)(带三级缓存与锁);prototype 走 beforePrototypeCreation→createBean→afterPrototypeCreation;自定义 scope 走 Scope.get(name, objectFactory)(web 的 request/session 就是注册了这个 Scope 接口)。
  8. 异常时 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();
		}
	}
}

逐段解读:

  1. 遍历的是 beanDefinitionNames 的拷贝 (第 1109 行)。注释解释:初始化方法里可能又注册新 bean 定义,拷贝一份遍历避免 ConcurrentModificationException。
  2. 收集 CompletableFuture 并最后 allOf().join() (第 1117-1134 行)。不是串行 for 循环 getBean,而是每个 bean 可能产出一个 future,主线程最后等所有 future 完成。这是 7.0 为"并行启动"做的骨架。
  3. preInstantiateSingleton 支持后台初始化 (第 1153-1197 行):若 mbd.isBackgroundInit() 且配了 BootstrapExecutor,先在主线程强制初始化 dependsOn 与 factoryBean,再用 CompletableFuture.runAsync(..., executor) 在后台实例化当前 bean,同时通过 addSingletonFactory 登记一个会 future.join() 的工厂------保证其他线程来要这个 bean 时,会阻塞等它后台初始化完,而不是拿到半成品。
  4. BeanCurrentlyInCreationException 在主线程被容忍跳过 (第 1193-1196 行):如果某 bean 已被其他后台线程初始化,主线程不再重复创建,只记一条 info 日志。这与 6.2 节讲的 lenientCreationLock/currentCreationThreads 是同一套并发模型------7.0 的容器启动已为虚拟线程并行做了系统性改造,不再是旧版"单线程串行实例化所有单例"。
  5. 第二遍循环处理 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<>();

逐点对照旧版差异:

  1. 三级缓存全部是 ConcurrentHashMap (第 86、89、95 行)。旧版 singletonFactories 是 new HashMap<>(16),因为它只在 singletonLock 内访问;7.0 改成 ConcurrentHashMap,配合下面的 tryLock 快速路径。
  2. singletonLock 是包级可见的 ReentrantLock (第 83 行,注意不是 private,同包的 AbstractBeanFactory 等可以协同使用)。旧版这里没有独立锁对象,而是 synchronized (this.singletonObjects)------以一级缓存 map 自身当监视器。
  3. 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,阻塞等待会放大锁竞争。

  1. 新增"宽松创建"(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 条件汇合。

  2. 新增 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 / 接收请求

这条时序里有三个关键的"顺序不可调换"约束,全在源码里:

  1. BeanFactoryPostProcessor 必须在 BeanPostProcessor 之前跑(第 5 步 vs 第 6 步)。因为 BFPP 要改 bean 定义,而 BPP 是拦 bean 实例的------定义都没定型,注册 BPP 没有意义。
  2. BeanPostProcessor 必须在 preInstantiateSingletons 之前注册 (第 6 步 vs 第 11 步)。否则业务单例实例化时 BPP 还没就位,@Autowired 都不会生效。
  3. 事件多播器必须在 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 同理
}

这段代码把"上下文能力"注入裸工厂,分五类:

  1. 表达式与属性编辑器 :StandardBeanExpressionResolver 让 @Value("#{...}") 的 SpEL 表达式能解析;ResourceEditorRegistrar 注册 Resource 类型的属性编辑器,让 @Value("classpath:config.yml") 能自动转成 Resource 对象。
  2. ApplicationContextAwareProcessor :一个 BeanPostProcessor,在 bean 初始化前检测它是否实现 EnvironmentAware/ResourceLoaderAware/ApplicationEventPublisherAware/MessageSourceAware/ApplicationContextAware,若是就回调对应的 setter。注意这与第 4 节 ConfigurableListableBeanFactory.ignoreDependencyInterface 的配合:这些 Aware 接口被显式忽略------即"不要按类型自动装配它们",而是由这个 BPP 专用回调注入。这就是为什么 ApplicationContext 本身能被注入:它通过 registerResolvableDependency 登记成"可解析依赖"。
  3. registerResolvableDependency :把 BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext 四个类型直接映射到上下文自身。于是业务 bean @Autowired ApplicationContext ctx 能拿到容器------不是因为有个叫 applicationContext 的 bean,而是因为这个"可解析依赖"注册表。
  4. ApplicationListenerDetector :早注册的 BPP,负责检测哪些 bean 是 ApplicationListener 实现并登记到多播器(对应第 10 步 registerListeners)。
  5. 默认环境单例 :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. 阅读源码路线图

给资深开发者的建议顺序,每一步都给出理由:

  1. 先读 BeanFactory 接口本身 (spring-beans/.../factory/BeanFactory.java)。它是整个容器的契约,类注释里的生命周期顺序表就是全局地图。理由:先知道"容器承诺做什么",再看实现。
  2. 顺着继承链往下读 :HierarchicalBeanFactory → ListableBeanFactory → ConfigurableBeanFactory → ConfigurableListableBeanFactory。每读一个接口问"它比父接口多了什么能力"。理由:接口继承链就是能力分层图,比直接读实现类高效。
  3. 读 AbstractApplicationContext.refresh() (spring-context/.../support/AbstractApplicationContext.java:581)。把十二步骨架背下来,再逐 step 跳进对应方法。理由:refresh 是容器启动的总调度,抓住它就抓住了主线。
  4. 读 DefaultListableBeanFactory 与 AbstractAutowireCapableBeanFactory :从 doGetBean → getSingleton → doCreateBean → createBeanInstance/populateBean/initializeBean 一路跟进去。理由:这是单例创建的完整调用链,理解了它就理解了 IoC 90%。
  5. 读 DefaultSingletonBeanRegistry :重点看三级缓存字段与 getSingleton(String, boolean) 的 tryLock 快速路径。理由:这是 7.0 并发模型变化最集中的地方。
  6. 再读扩展点 :BeanPostProcessor/BeanFactoryPostProcessor 接口,然后看 AutowiredAnnotationBeanPostProcessor、ConfigurationClassPostProcessor 两个典型实现。理由:扩展点是"框架怎么被插件化"的关键。
  7. Web 栈只看 DispatcherServlet.doDispatch :顺着 getHandler → getHandlerAdapter → ha.handle → render 把三个策略串一遍即可,不必细读每个实现。理由:Web 机制是 IoC 之上的策略装配,主线清楚即可。
  8. 最后看 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 仓库全景高度,做了五件事:

  1. 用实测数字建立规模感 :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 适配层都是薄适配。
  2. 用 build.gradle 实证分层与依赖规则:core 零内部依赖;beans 只依赖 core;aop 依赖 beans 但不依赖 context;context 组合 aop/beans/core/expression;web 对 context 是 optional;webmvc 才硬依赖 context;数据访问层统一站在 beans+core 之上。这五条规则被 Gradle 配置 + ArchUnit 双重锁死。
  3. 逐个读接口文件讲清顶层抽象 :BeanFactory 五层链(Hierarchical/Listable/Configurable/ConfigurableListable)按"客户端视图→容器管理者视图"递进;ApplicationContext 一次组合六个角色;Resource/ResourceLoader、ConversionService、MessageSource、Environment 各司其职。
  4. 把设计哲学落到具体类 :模板方法(refresh() 十二步、doCreateBean、AbstractMessageSource final 骨架+abstract 钩子)、策略(HandlerMapping/Adapter/ViewResolver、TaskExecutor)、观察者(ApplicationEventMulticaster + SimpleApplicationEventMulticaster)、组合与适配器(ProxyFactory、Resource 体系)、约定优于配置(@Configuration 元注解 @Component、proxyBeanMethods 默认 true、@ComponentScan)、扩展点优先(BeanPostProcessor/BeanFactoryPostProcessor)。
  5. 明确 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 调度等具体主题------本章只负责把骨架与设计判断讲透。

相关推荐
程序员夏洛10 小时前
VMware Workstation Pro 卸载失败:安装向导提前结束、错误 1603 的排查与解决
java
User_芊芊君子10 小时前
【JavaEE初阶 - 网络原理】网络分几层?从自定义协议聊到 DNS
java·网络·java-ee
聚美智数17 小时前
智能结构化识别-图片结构解析-图像结构化解析-照片结构化解析
android·java·数据库
十年Java程序媛17 小时前
Java枚举Enum完整教程|替代静态常量,开发与面试要点
java·开发语言·spring boot·后端·面试
我是人✓18 小时前
SpringBoot静态资源映射揭秘
java·spring boot·后端
全栈弄潮儿18 小时前
Python实战第7期:字典与集合
后端·python·编程语言
码事漫谈18 小时前
DeepSeek 又更新!但怎么有个分数往下掉?
后端
吃饱了得干活18 小时前
用 PageHelper 同步数据到 ES:能跑,但这几个坑得先知道
后端·elasticsearch
金銀銅鐵18 小时前
[Java] 借助GUI展示class文件的内容(v1.0版本)
jvm·后端·ai编程