Spring Framework 7.0.9 深度剖析-03-IoC 容器核心:BeanFactory 与 BeanDefinition 体系

Spring Framework 7.0.9 深度剖析-03-IoC 容器核心:BeanFactory 与 BeanDefinition 体系

一、本章导读

如果说整个 Spring Framework 是一台巨大的机器,那么 BeanFactory 就是这台机器的主轴。开发者日常写的 @Service、@Autowired、@Configuration,最终都要落到这条主轴上:先有一份描述"这个对象该怎么造、叫什么、依赖谁"的元数据(BeanDefinition),再由一个工厂把这份元数据兑现成真正的 Java 对象。本章要做的,就是把这条主轴从接口到数据结构、从注册到查找、从单例缓存到依赖候选筛选,逐层拆开看清楚。

本章的全部源码都来自仓库 spring-beans 模块,核心入口目录是 spring-beans/src/main/java/org/springframework/beans/factory/,以及它下面的 factory/support/(实现类)与 factory/config/(配置面接口)。涉及的核心类包括:

  • 接口族:BeanFactory、HierarchicalBeanFactory、ListableBeanFactory、AutowireCapableBeanFactory、ConfigurableBeanFactory、ConfigurableListableBeanFactory,以及辅助接口 FactoryBean、SmartFactoryBean、AutowireCandidateResolver、DependencyDescriptor、InjectionPoint。
  • 实现族:DefaultSingletonBeanRegistry(单例注册表)、AbstractBeanFactory(doGetBean 主流程)、AbstractAutowireCapableBeanFactory(实例化与属性填充的骨架,本章只点到为止,生命周期细节留给后续章节)、DefaultListableBeanFactory(最终的全功能工厂,也是 ApplicationContext 内部真正持有的那个工厂)。
  • 元模型族:BeanDefinition 接口及其实现 AbstractBeanDefinition、GenericBeanDefinition、RootBeanDefinition、ChildBeanDefinition,以及注解驱动场景下的 AnnotatedGenericBeanDefinition、ScannedGenericBeanDefinition。

需要的前置知识很简单:知道什么是依赖注入、写过 @Autowired、见过 ApplicationContext.getBean() 即可。读完本章你应当能回答这些问题:

  1. BeanFactory 那一大坨接口到底谁管什么?为什么不把所有方法塞到一个接口里?
  2. DefaultListableBeanFactory 内部到底用了哪几个 Map、哪几个 List 来存"bean 定义"和"单例实例"?注册一个新定义时,并发安全是怎么保证的?同名重复注册会发生什么?
  3. 一次 getBean("userService") 调用,从接口方法到最终拿到对象,中间经过了哪几层?名字里的 &、别名、父工厂回退、单例缓存命中分别在哪一步处理?
  4. @Autowired UserService userService; 在容器里是怎么找到那个合适的 bean 的?候选有多个时,@Primary、@Priority、字段名分别在什么优先级上起作用?
  5. 为什么 BeanDefinition 有 Generic、Root、Child 这么多子类?"合并后的 BeanDefinition"(MergedBeanDefinition)到底合并了什么、缓存在哪里?

本章聚焦"工厂接口族 + 注册表数据结构 + BeanDefinition 元模型 + 依赖解析入口"。bean 的创建、属性填充、初始化回调、循环依赖的三级缓存细节,属于生命周期范畴,本章只在 doGetBean 必经之路上点到为止,不展开。

一个贯穿全章的版本事实先放在这里:本章所有代码均来自 Spring Framework 7.0.9 (gradle.properties 中 version=7.0.9)。7.0 在预实例化阶段引入了后台并行实例化 能力(bootstrapExecutor、isBackgroundInit()、PreInstantiation 线程标记),单例锁也从 6.x 的 synchronized 块演进为 ReentrantLock 的 tryLock 快速失败策略------这些与网上常见的 5.x/6.0 教程描述不同,本章会在对应位置明确点出。

二、核心概念:先建立一张全景图

2.1 BeanFactory:IoC 容器的"最小客户视图"

BeanFactory(路径 spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java)是访问 Spring bean 容器的根接口 。它的类注释里有一句关键定位:"This is the basic client view of a bean container"------它是给使用方 看的最小视图。也就是说,业务代码拿到一个 BeanFactory 引用时,它能做的事情被刻意限制在"查、取、判断"这三类:

  • 取对象:getBean 家族;
  • 查存在:containsBean;
  • 查属性:isSingleton、isPrototype、getType、isTypeMatch、getAliases。

它不包含 任何"注册一个 bean 定义进去"的方法,也不包含"枚举所有 bean"的方法。这种刻意的窄接口设计,是 Spring 接口隔离原则(ISP)的典型体现:客户不应该看见它用不到的东西。

这个接口里定义了一个全容器最重要的常量(见 BeanFactory.java 第 131-137 行):

java 复制代码
String FACTORY_BEAN_PREFIX = "&";

/**
 * Character variant of {@link #FACTORY_BEAN_PREFIX}.
 * @since 6.2.6
 */
char FACTORY_BEAN_PREFIX_CHAR = '&';

& 前缀是 FactoryBean 机制的协议基石:按名字取 bean 时,名字带 & 表示"我要那个 FactoryBean 本身",不带 & 表示"我要 FactoryBean 生产出来的那个对象"。7.0.9 额外提供了一个 char 型变体 FACTORY_BEAN_PREFIX_CHAR(自 6.2.6 起),用于避免在热路径上频繁做字符串拼接------这是一个微小但真实存在的性能优化点,后文讲 transformedBeanName 时会再遇到。

2.2 接口族的继承关系

BeanFactory 并不是孤立的,它周围有一组"按用途拆分"的子接口。7.0.9 中这条继承链是:

java 复制代码
                       BeanFactory(根:取/查/判)
                         ▲
           ┌─────────────┼──────────────────┐
           │             │                  │
  HierarchicalBeanFactory  ListableBeanFactory  (无父)
           │             │
           └──────┬──────┘
                  │
        ConfigurableBeanFactory(可配置面:scope/类加载器/类型转换/占位符)
                  ▲
                  │
        AutowireCapableBeanFactory(外部创建 bean + 注入能力)
                  ▲
                  │
        ConfigurableListableBeanFactory(聚合:可枚举 + 可配置 + 预实例化)

它们各自"管什么",用一张职责边界表说清楚(路径均相对于 spring-beans/src/main/java/org/springframework/beans/factory/):

接口 路径 一句话职责 典型方法
BeanFactory BeanFactory.java 最小客户视图:按名/按类型取单个 bean、判断单例原型、查别名 getBean、containsBean、isSingleton、getType、getAliases
HierarchicalBeanFactory HierarchicalBeanFactory.java 父子工厂层次:拿到父工厂、只查本地不回溯祖先 getParentBeanFactory、containsLocalBean
ListableBeanFactory ListableBeanFactory.java 可枚举:批量列出 bean 名、按类型批量取 Map containsBeanDefinition、getBeanDefinitionNames、getBeanNamesForType、getBeansOfType
AutowireCapableBeanFactory config/AutowireCapableBeanFactory.java 对容器外部 new 出来的对象执行完整 bean 生命周期 createBean(Class)、autowireBean、initializeBean、applyBeanPostProcessorsBefore/AfterInitialization
ConfigurableBeanFactory config/ConfigurableBeanFactory.java 工厂的"配置面板":设父工厂、注册 scope、加 BeanPostProcessor、类型转换器、占位符解析器、注册可解析依赖类型 setParentBeanFactory、registerScope、addBeanPostProcessor、setBeanClassLoader、resolveEmbeddedValue
ConfigurableListableBeanFactory config/ConfigurableListableBeanFactory.java 聚合上述能力 + 冻结配置 + 预实例化单例 ignoreDependencyInterface、registerResolvableDependency、resolveDependency、freezeConfiguration、preInstantiateSingletons

这张表是本章的"地图"。后面每一节,我们都会回到这张表,看某个方法究竟落在哪个接口、由哪个实现类兑现。

2.3 三个必须先搞懂的核心抽象

在进入源码之前,先把三个贯穿全章的抽象立起来:

(1)BeanDefinition(bean 定义) 。它是"造一个 bean 的配方",不是 bean 本身。接口在 config/BeanDefinition.java,描述了:这个 bean 叫什么类(getBeanClassName)、什么作用域(getScope)、是否懒加载(isLazyInit)、依赖谁(getDependsOn)、有哪些属性值和构造参数、初始化/销毁方法叫什么(getInitMethodName/getDestroyMethodName)、角色是什么(getRole)、父定义是谁(getParentName)。BeanDefinition 本身不持有实例,它只是元数据。真正的对象是工厂在运行时根据这份配方"new"出来的。

(2)Singleton(单例注册表)与 BeanDefinition(定义注册表)是两张不同的表 。新手最容易混淆的一点:beanDefinitionMap 存的是配方 (BeanDefinition 对象),singletonObjects 存的是成品 (已经造好的 Java 对象)。一个是"图纸",一个是"仓库里的货"。getBean("userService") 第一次走"按图纸造货入库",之后走"直接取货"。本章会反复回到这两张表的配合上。

(3)FactoryBean(工厂 bean)是一种特殊的 bean 。普通 bean 本身就是它要暴露的对象;FactoryBean 是一个"能生产别的 bean 的 bean"。它自己也存在容器里(名字不带 & 时它就是一个普通 singleton),但 getBean("xxx") 拿到的却是它 getObject() 生产出来的对象。& 前缀就是用来在这两者之间切换的开关。

把这三件事记住,后面的源码读起来就不会迷路。

三、源码剖析(一):BeanFactory 接口族逐个拆解

这一节我们不看实现,只看接口本身。接口是契约,把契约读懂,实现类做什么就一目了然。

3.1 BeanFactory:getBean 家族与"查"方法

打开 BeanFactory.java,除了 & 前缀常量,它一共定义了五组 getBean 重载、一组 getBeanProvider、以及 containsBean/isSingleton/isPrototype/isTypeMatch/getType/getAliases。先看 getBean 家族(第 155-232 行):

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;

逐组解读:

  1. 按名取 :getBean(String name) 是最经典的入口。注释里写明三件事:会把别名翻译回规范名;本地找不到会问父工厂;返回值永不为 null(但可能是一个代表 null 的 NullBean 占位,用 equals(null) 判断)。
  2. 按名 + 按类型校验 :getBean(String, Class<T>) 在按名取到之后多一步类型检查,类型不符抛 BeanNotOfRequiredTypeException,而不是让调用方在强转时才炸 ClassCastException。
  3. 按名 + 显式构造参数 :getBean(String, Object...) 的 args 只在新建实例时生效 ------也就是说它只对 prototype(或尚未创建的 singleton)有意义。注释里专门警告:如果给了 args 但目标 bean 不是 prototype,会抛 BeanDefinitionStoreException。原因很简单:singleton 只造一次,你第二次传不同的构造参数进去,到底听谁的?容器选择直接报错而不是悄悄忽略。
  4. 按类型取 :getBean(Class<T>) 是 3.0 引入的"by-type"入口。它的语义注释很关键------"goes into ListableBeanFactory by-type lookup territory",也就是说这个方法虽然定义在根接口上,实际行为要靠 ListableBeanFactory 的批量能力来支撑。它会在容器里找"唯一匹配该类型的 bean";找不到抛 NoSuchBeanDefinitionException,找到多个抛 NoUniqueBeanDefinitionException。
  5. 按类型 + 显式参数 :getBean(Class<T>, Object...) 是 4.1 的组合版。

注意一个细节:所有这些 getBean 在根接口上都只是声明 ,真正的实现落在 AbstractBeanFactory.doGetBean(按名)和 DefaultListableBeanFactory.resolveBean(按类型)里。接口族设计的精髓就在这里------根接口只暴露行为签名,把"按名"和"按类型"两条路线的实现分别下沉到不同的抽象层。

再看 getBeanProvider(第 243-282 行):

java 复制代码
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

@since 7.0
<T> ObjectProvider<T> getBeanProvider(ParameterizedTypeReference<T> requiredType);

ObjectProvider 是 4.0 引入、5.1 完善的"延迟查找句柄"。它允许你拿到一个 provider,在真正需要时才去容器里取,而且支持 getIfAvailable()(没有就返回 null 而不是抛异常)、getIfUnique()(唯一才返回)、ifAvailable(Consumer) 等宽容语义。7.0.9 新增了第三个重载:接受 ParameterizedTypeReference<T>,让你能写出 getBeanProvider(new ParameterizedTypeReference<List<Foo>>() {}) 这样的带泛型精确匹配------这是为了让编程式取带泛型的 bean 时不必丢失泛型信息。

"查"方法组里最值得讲的是 getType 的重载(第 385-405 行):

java 复制代码
@Nullable Class<?> getType(String name) throws NoSuchBeanDefinitionException;

@since 5.2
@Nullable Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

第二个重载的 allowFactoryBeanInit 参数是 5.2 加的。为什么需要它?因为对一个 FactoryBean 来说,要知道它生产出来的对象类型,理想情况是读它 getObjectType() 的返回值;但如果这个 FactoryBean 自己还没初始化、getObjectType() 又写得很笨(必须等到 getObject() 跑过才能确定类型),那类型探测就可能被迫把整个 FactoryBean 初始化出来。allowFactoryBeanInit=false 就是给"类型检查场景"开的绿灯变红灯:宁可返回 null 也不要为了看一眼类型而触发副作用初始化。这个参数在后面 getBeanNamesForType 的批量扫描里至关重要。

3.2 HierarchicalBeanFactory:父子层次的"只读切面"

这个接口极其短小,全文 52 行,只有两个方法(HierarchicalBeanFactory.java 第 39-50 行):

java 复制代码
@Nullable BeanFactory getParentBeanFactory();

boolean containsLocalBean(String name);

它存在的意义是把"层次结构"从根接口里剥离出来。根接口 BeanFactory 的注释里说:"如果本地找不到,会问父工厂"------但这种"自动向上回溯"的行为,只对部分方法 成立。HierarchicalBeanFactory 补了一个反向操作:containsLocalBean(name) 只查本地、忽略祖先。这在框架内部很有用:比如注册一个 bean 定义时,必须确认本地没有重名,不能被父工厂里的同名 bean 干扰。

setParentBeanFactory() 并不在这个接口上,而是在 ConfigurableBeanFactory 上------这又是一次读写分离:HierarchicalBeanFactory 只读父引用,设置父工厂属于"配置面"。

3.3 ListableBeanFactory:可枚举的批量视图

ListableBeanFactory extends BeanFactory(第 60 行)。它的类注释开宗明义:它是"可以枚举所有 bean、而不是客户一个个按名查"的工厂。典型实现是 XML 驱动的工厂(事先把所有定义都加载进来了)。

它的核心方法分三组。第一组是"定义表"的直接计数与列举(第 71-90 行):

java 复制代码
boolean containsBeanDefinition(String beanName);
int getBeanDefinitionCount();
String[] getBeanDefinitionNames();

注意这里的语义边界:这些方法只看本地 、只看 bean 定义 ,忽略父工厂、也忽略 registerSingleton 手动塞进来的单例。也就是说,你手工 registerSingleton("foo", obj) 一个对象,getBeanDefinitionCount() 不会把它算进去,但 getBeanNamesForType/getBeansOfType 会把它算进去------类注释第 40-48 行专门解释了这种不对称。

第二组是按类型列举名字:getBeanNamesForType(...) 有四个重载(Class/ResolvableType × 无参/带 includeNonSingletons, allowEagerInit)。第三组是按类型批量取实例:getBeansOfType(...) 返回 Map<String,T>。这两组方法的注释里反复强调一件事:它们只内省顶层 bean,不检查嵌套的内部 bean ;而且返回的名字/实例应当尽量按定义顺序排列 。这个"按定义顺序"的承诺非常重要,后文讲 List 注入顺序时会用到。

这个接口还有一个 5.3 加的便捷方法(第 110 行):<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType, boolean allowEagerInit),让批量场景也能拿到 provider。

3.4 AutowireCapableBeanFactory:把生命周期开放给容器外

AutowireCapableBeanFactory extends HierarchicalBeanFactory(注意它继承的是 HierarchicalBeanFactory 而不是 ListableBeanFactory)。它解决的是一个特殊问题:当你手里有一个自己 new 出来的对象(不是容器造的),想让容器对它执行一遍完整的注入 + 初始化回调,该怎么办?

它的关键方法(config/AutowireCapableBeanFactory.java):

java 复制代码
<T> T createBean(Class<T> beanClass) throws BeansException;                       // 137 行
Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck);  // 194 行
Object initializeBean(Object existingBean, String beanName);                     // 286 行
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName); // 305 行
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName);  // 325 行
void destroyBean(Object existingBean);                                            // 336 行

以及几个装配模式常量:AUTOWIRE_NO = 0(第 71 行)、AUTOWIRE_BY_NAME = 1(第 79 行)。

这个接口是 Spring 与第三方框架集成的桥梁。比如一个 Servlet 容器里 new 出来的 Filter,没法走 Spring 的构造注入,但只要拿到 AutowireCapableBeanFactory,调一次 autowireBean(filter),就能让它身上的 @Autowired 字段被填上。它和"容器内部造 bean"共用同一套 BeanPostProcessor 流水线,区别只是入口------内部走 createBean(String beanName, RootBeanDefinition mbd, ...)(带定义),外部走 createBean(Class)(无定义、纯类)。

3.5 ConfigurableBeanFactory:工厂的"配置面板"

ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry。它把"运行中的工厂"可以被调整的那些旋钮集中起来。主要能力:

  • 层次 :setParentBeanFactory(设置父工厂,与 HierarchicalBeanFactory.getParentBeanFactory 配对)。
  • 类加载 :setBeanClassLoader/getBeanClassLoader,工厂加载 bean class 时用哪个 ClassLoader。
  • BeanPostProcessor 注册表 :addBeanPostProcessor、getBeanPostProcessorCount。
  • 作用域注册 :registerScope(scopeName, Scope),web 环境的 request/session scope 就是这么塞进来的。
  • 类型转换 :setTypeConverter、getTypeConverter,以及 addPropertyEditorRegistrar。
  • 占位符解析 :resolveEmbeddedValue(String),把 ${...} 解析掉。
  • 临时类加载器 :setTempClassLoader(用于类型匹配时的临时类加载,不真正加载到主 ClassLoader)。
  • 缓存开关 :setCacheBeanMetadata、isCacheBeanMetadata。

它还继承了 SingletonBeanRegistry(路径 config/SingletonBeanRegistry.java),这个接口定义了单例注册表的对外面:registerSingleton、getSingleton、containsSingleton、getSingletonNames、destroySingletons。也就是说,"单例表"是一个独立的接口,被 ConfigurableBeanFactory 继承进来------这解释了为什么 DefaultListableBeanFactory 一族从 DefaultSingletonBeanRegistry 一路往上继承时,单例表能力是最底层。

3.6 ConfigurableListableBeanFactory:最后的聚合接口

ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory。它把前面所有能力聚合成"一个全功能工厂应当长什么样"。它新增的关键方法(config/ConfigurableListableBeanFactory.java):

java 复制代码
void ignoreDependencyInterface(Class<?> ifc);                       // 64 行
void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue); // 82 行
BeanDefinition getBeanDefinition(String beanName);                   // 110 行
void clearMetadataCache();                                          // 136 行
void freezeConfiguration();                                         // 146 行
boolean isConfigurationFrozen();                                   // 154 行
void preInstantiateSingletons() throws BeansException;              // 178 行

逐个点一下:

  • ignoreDependencyInterface:告诉容器"凡是实现了这个接口的字段,不要尝试按类型自动装配"。AbstractApplicationContext.prepareBeanFactory 用它把一堆 *Aware 接口(EnvironmentAware、ApplicationContextAware 等)排除在自动装配之外------因为这些 Aware 回调是容器自己通过 BeanPostProcessor 显式塞值的,不应该走 @Autowired 的候选筛选流程。
  • registerResolvableDependency:往一个"特殊可解析依赖表"里登记一对 (类型, 值)。后面讲到 findAutowireCandidates 时会看到,BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext 这四个"内建可注入对象"就是靠这个方法注册的。
  • freezeConfiguration/isConfigurationFrozen:冻结配置。冻结之后,bean 定义表被视为不可变,工厂可以放心地缓存按类型查询的结果(allBeanNamesByType 等缓存就是靠冻结开关激活的)。
  • preInstantiateSingletons:触发"所有非懒加载单例的预实例化"------这是 Spring 容器启动的最后一步,也是本章要重点剖析的方法。

至此,接口族讲完了。下一节进入真正的实现:DefaultListableBeanFactory 的数据结构。

四、源码剖析(二):DefaultListableBeanFactory 的数据结构

DefaultListableBeanFactory(路径 spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java,全文 2813 行)是整个 spring-beans 模块的"最终答卷":

java 复制代码
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

它的继承链是:

bash 复制代码
DefaultSingletonBeanRegistry(单例表)
  → FactoryBeanRegistrySupport(FactoryBean 产物缓存)
    → AbstractBeanFactory(doGetBean 主流程 + 合并定义缓存)
      → AbstractAutowireCapableBeanFactory(createBean/populate/initialize)
        → DefaultListableBeanFactory(定义表 + 按类型筛选 + 预实例化)

它实现了三个接口:ConfigurableListableBeanFactory(全功能工厂面)、BeanDefinitionRegistry(定义注册表面,在 support/BeanDefinitionRegistry.java)、Serializable。下面把它自己声明的字段逐个拆开------这些字段就是"工厂内部到底存了什么"的答案。

4.1 定义表:beanDefinitionMap 与 beanDefinitionNames

看第 190-209 行:

java 复制代码
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

/** Map from bean name to merged BeanDefinitionHolder. */
private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);

/** Map of bean definition names with a primary marker plus corresponding type. */
private final Map<String, Class<?>> primaryBeanNamesWithType = new ConcurrentHashMap<>(16);

/** Map of singleton and non-singleton bean names, keyed by dependency type. */
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);

/** Map of singleton-only bean names, keyed by dependency type. */
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);

/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

/** List of names of manually registered singletons, in registration order. */
private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);

逐字段讲:

  • beanDefinitionMap :核心中的核心。ConcurrentHashMap,key 是 bean 名,value 是 BeanDefinition 配方。它是"图纸仓库"。之所以用 ConcurrentHashMap 而不是 HashMap,是因为注册定义和查找定义可能发生在不同线程(启动时注册在主线程,业务线程可能并发 getBean)。
  • beanDefinitionNames :一个 volatile List<String>,按注册顺序 保存所有 bean 名。为什么除了 Map 还要一个 List?因为 ConcurrentHashMap 不保证遍历顺序,而 ListableBeanFactory 承诺"返回的名字按定义顺序",预实例化时也必须按注册顺序来(顺序对循环依赖排查、对 @Order 排序都有影响)。注意它是 volatile 且是引用 volatile------一旦启动后还要新增定义,整个 List 会被替换成一个新的 ArrayList(后面 registerBeanDefinition 会看到这个"写时复制"动作),而不是原地 add。
  • manualSingletonNames :volatile Set<String>(LinkedHashSet),记录那些不是通过 bean 定义、而是通过 registerSingleton 手动塞进来的单例名。为什么要单独记一份?因为 preInstantiateSingletons 遍历的是 beanDefinitionNames,手动注册的单例没有定义、不需要"预实例化",但 getBeanNamesForType 这类批量查询又必须把它们包含进去------单独记账,方便区分两条来源。
  • allBeanNamesByType / singletonBeanNamesByType :两个"按类型索引"的缓存。key 是依赖类型,value 是该类型对应的所有 bean 名数组。这是 getBeanNamesForType 的加速缓存:一旦配置冻结,按类型查就不必每次都遍历全表做 isTypeMatch,直接查这个索引即可。
  • primaryBeanNamesWithType :记录哪些 bean 标了 @Primary。注册时(第 1329-1331 行)如果 beanDefinition.isPrimary(),就往这里放一条。这是 7.0 为加速 determinePrimaryCandidate 做的小优化。
  • mergedBeanDefinitionHolders :缓存 bean 名到 BeanDefinitionHolder 的映射,用于注解配置场景下的合并定义持有者。

4.2 依赖装配相关字段

再看第 174-188 行:

java 复制代码
/** Whether to allow re-registration of a different definition with the same name. */
private @Nullable Boolean allowBeanDefinitionOverriding;

/** Whether to allow eager class loading even for lazy-init beans. */
private boolean allowEagerClassLoading = true;

private @Nullable Executor bootstrapExecutor;

/** Optional OrderComparator for dependency Lists and arrays. */
private @Nullable Comparator<Object> dependencyComparator;

/** Resolver to use for checking if a bean definition is an autowire candidate. */
private AutowireCandidateResolver autowireCandidateResolver = SimpleAutowireCandidateResolver.INSTANCE;

/** Map from dependency type to corresponding autowired value. */
private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);
  • allowBeanDefinitionOverriding :是否允许同名覆盖。注意它是 @Nullable Boolean,默认 null;判断时用 isAllowBeanDefinitionOverriding(),实现是 !Boolean.FALSE.equals(this.allowBeanDefinitionOverriding)------也就是默认 true (允许覆盖),只有显式设为 false 才禁止。Spring Boot 2.1+ 把它改成了默认 false,那是 Boot 做的调整,不是 spring-beans 本身。
  • bootstrapExecutor :7.0 的新字段!一个 Executor。配了它之后,标了 backgroundInit 的 bean 可以被丢到后台线程去实例化。这是本章后面要重点讲的并行预实例化的开关。
  • dependencyComparator :给 List/数组注入用的排序比较器。通常被 ApplicationContext 设为 AnnotationAwareOrderComparator,于是 @Order/Ordered/@Priority 注解才能生效。
  • autowireCandidateResolver :策略接口,判断"某个 bean 定义能不能当某个注入点的候选"。默认是 SimpleAutowireCandidateResolver.INSTANCE(只看 isAutowireCandidate() 标志位);到了 spring-context 环境会被换成 ContextAnnotationAutowireCandidateResolver,加上 @Qualifier、@Lazy 代理等扩展。
  • resolvableDependencies :内建可注入对象表。前面提过的 BeanFactory、ResourceLoader 等就登记在这里。它的查找逻辑在 findAutowireCandidates 里------候选筛选时,先遍历这张表,凡是类型能赋值给依赖类型的,直接作为候选加进去。

4.3 冻结与并发控制字段

第 211-221 行:

java 复制代码
/** Cached array of bean definition names in case of frozen configuration. */
private volatile String @Nullable [] frozenBeanDefinitionNames;

/** Whether bean definition metadata may be cached for all beans. */
private volatile boolean configurationFrozen;

/** Name prefix of main thread: only set during pre-instantiation phase. */
private volatile @Nullable String mainThreadPrefix;

private final NamedThreadLocal<PreInstantiation> preInstantiationThread =
		new NamedThreadLocal<>("Pre-instantiation thread marker");
  • configurationFrozen :配置冻结标志。冻结之后,beanDefinitionNames 被快照成 frozenBeanDefinitionNames 数组(不可变),allBeanNamesByType 等按类型缓存才被认为是安全的。
  • mainThreadPrefix + preInstantiationThread :这一对是 7.0 并行预实例化的"线程身份识别"机制。预实例化开始时记录主线程名前缀(比如 main),如果之后冒出一个同名前缀的线程,就认为它是"外部启动线程",要走严格锁;普通后台线程则用宽松锁。PreInstantiation 是一个枚举(MAIN/BACKGROUND),用 NamedThreadLocal 绑定到当前线程。

另外还有一个类级常量(第 147 行):

java 复制代码
public static final String STRICT_LOCKING_PROPERTY_NAME = "spring.locking.strict";

以及对应的字段(第 168 行):

java 复制代码
private final @Nullable Boolean strictLocking = SpringProperties.checkFlag(STRICT_LOCKING_PROPERTY_NAME);

这个系统属性是 6.2.6 引入的:默认情况下,工厂会根据线程名推断要不要严格加锁;显式设 spring.locking.strict=true/false 可以强制开关。这是 7.0 并发模型的一个调节阀。

把这几十个字段归类一下,DefaultListableBeanFactory 内部其实维护了四组表:

  1. 定义表 :beanDefinitionMap + beanDefinitionNames(配方仓库);
  2. 单例表 :继承自 DefaultSingletonBeanRegistry 的 singletonObjects/earlySingletonObjects/singletonFactories(成品仓库,下一章细讲);
  3. 按类型索引缓存 :allBeanNamesByType/singletonBeanNamesByType/primaryBeanNamesWithType;
  4. 特殊依赖表 :resolvableDependencies。

理解了这四组表的分工,registerBeanDefinition 和 getBean 就只是"在这几张表上做增删查"而已。下一节我们就从写操作 registerBeanDefinition 开始。

五、源码剖析(三):registerBeanDefinition 全流程

注册一个 bean 定义,是容器启动期最频繁的写操作。DefaultListableBeanFactory.registerBeanDefinition(第 1251-1332 行)的完整源码如下,我们逐段拆。

5.1 方法签名与定义校验

java 复制代码
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
		throws BeanDefinitionStoreException {

	Assert.hasText(beanName, "Bean name must not be empty");
	Assert.notNull(beanDefinition, "BeanDefinition must not be null");

	if (beanDefinition instanceof AbstractBeanDefinition abd) {
		try {
			abd.validate();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Validation of bean definition failed", ex);
		}
	}
	// ......
}

第一段做三件事:断言 bean 名非空、断言定义非空、如果这个定义是 AbstractBeanDefinition 家族的,先调一次 validate()。validate() 在 AbstractBeanDefinition.java 第 1235-1244 行:

java 复制代码
public void validate() throws BeanDefinitionValidationException {
	if (hasMethodOverrides() && getFactoryMethodName() != null) {
		throw new BeanDefinitionValidationException(
				"Cannot combine factory method with container-generated method overrides: " +
				"the factory method must create the concrete bean instance.");
	}
	if (hasBeanClass()) {
		prepareMethodOverrides();
	}
}

校验规则很朴素:方法覆写(lookup-method/replaced-method,即 CGLIB 增强的那套)不能与工厂方法并存------因为工厂方法已经决定了实例怎么造,容器没法再往里插方法覆写;如果 beanClass 已经解析成了 Class,就把方法覆写逐个 prepareMethodOverride,核对类里到底有没有这个方法名(见第 1265-1274 行:方法数为 0 报错;方法数为 1 则标记"非重载",避免运行时做参数类型检查的开销)。

一个版本事实 :任务书里提到"last-modified 检查",但在 7.0.9 的 registerBeanDefinition 里并没有 任何基于时间戳或 lastModified 的比较逻辑。同名覆盖的判断只看"是否允许覆盖"这个布尔开关,不看两个定义的新旧。这一点后面"常见问题与坑"还会再强调。

5.2 同名覆盖:existingDefinition != null 分支

java 复制代码
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
	if (!isBeanDefinitionOverridable(beanName)) {
		throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
	}
	else {
		logBeanDefinitionOverriding(beanName, beanDefinition, existingDefinition);
	}
	this.beanDefinitionMap.put(beanName, beanDefinition);
}

如果 beanDefinitionMap 里已经有同名定义:

  1. 先问 isBeanDefinitionOverridable(beanName)。这个方法在第 1447-1449 行,实现就是 return isAllowBeanDefinitionOverriding();------即看那个 allowBeanDefinitionOverriding 布尔开关。如果不允许覆盖,直接抛 BeanDefinitionOverrideException(这个异常类在 support/BeanDefinitionOverrideException.java,4.1 引入,专门承载"旧定义 + 新定义"两份信息,方便排障)。
  2. 允许覆盖,则调 logBeanDefinitionOverriding 打日志(第 1334-1370 行),日志分三档:旧定义角色 < 新定义角色(用户 bean 被框架 bean 替换)、两个定义 equals 不同、两个定义 equals 相同,分别用 info/debug/trace 输出。注意它不报错、不返回 ,直接 beanDefinitionMap.put 覆盖。
  3. 覆盖完成后,beanDefinitionNames 不需要动(名字本来就在表里),但要在后面触发 resetBeanDefinition。

这里有个设计要点:覆盖是"后注册的赢" 。谁后 registerBeanDefinition,谁就留在 Map 里。这也是为什么 Spring Boot 允许你用一个自定义 bean 去覆盖 Spring Boot 自动配置里注册的那个同名 bean------只要你注册得更晚。

5.3 新名字:别名冲突与"启动期 vs 启动后"

java 复制代码
else {
	if (isAlias(beanName)) {
		String aliasedName = canonicalName(beanName);
		if (!isBeanDefinitionOverridable(aliasedName)) {
			if (containsBeanDefinition(aliasedName)) {  // alias for existing bean definition
				throw new BeanDefinitionOverrideException(
						beanName, beanDefinition, getBeanDefinition(aliasedName));
			}
			else {  // alias pointing to non-existing bean definition
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition for bean '" + beanName +
						"' since there is already an alias for bean '" + aliasedName + "' bound.");
			}
		}
		else {
			if (logger.isInfoEnabled()) {
				logger.info("Removing alias '" + beanName + "' for bean '" + aliasedName +
						"' due to registration of bean definition for bean '" + beanName + "': [" +
						beanDefinition + "]");
			}
			removeAlias(beanName);
		}
	}
	// ......
}

新名字分支首先要处理"撞别名":如果这个名字当前是别人的别名(isAlias(beanName) 为 true),那注册新定义就等于"抢占"这个名字。如果不允许覆盖,要分两种情况报错------别名指向一个已存在的定义(抛 BeanDefinitionOverrideException),别名指向一个还不存在的定义(抛 BeanDefinitionStoreException)。允许覆盖则 removeAlias(beanName),把别名删掉,日志说明原因。这一段保证了"名字资源"的一致性:一个名字在同一时刻要么是定义名、要么是别名、要么是手动单例,不能两占。

接下来是并发安全的关键判断:

java 复制代码
	if (hasBeanCreationStarted()) {
		// Cannot modify startup-time collection elements anymore (for stable iteration)
		synchronized (this.beanDefinitionMap) {
			this.beanDefinitionMap.put(beanName, beanDefinition);
			List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
			updatedDefinitions.addAll(this.beanDefinitionNames);
			updatedDefinitions.add(beanName);
			this.beanDefinitionNames = updatedDefinitions;
			removeManualSingletonName(beanName);
		}
	}
	else {
		// Still in startup registration phase
		this.beanDefinitionMap.put(beanName, beanDefinition);
		this.beanDefinitionNames.add(beanName);
		removeManualSingletonName(beanName);
	}
	this.frozenBeanDefinitionNames = null;

这是 7.0.9 注册路径里最能体现并发设计的一段:

  • hasBeanCreationStarted() 判断"是否已经开始造 bean 了"。启动早期(所有定义还没注册完),单线程往里塞定义,直接 put + add,无锁。
  • 一旦已经开始造 bean (比如 BeanFactoryPostProcessor 在处理过程中又注册了新定义,或业务代码运行时热注册),就不能再原地修改 beanDefinitionNames 这个 ArrayList 了------因为别的线程可能正在遍历它做预实例化,原地 add 会触发 ConcurrentModificationException。于是走 synchronized (this.beanDefinitionMap) 块,在锁内把整个 List 复制一份新的 ,追加新名字,然后替换引用 (this.beanDefinitionNames = updatedDefinitions)。这就是为什么 beanDefinitionNames 要声明成 volatile:引用替换对其他线程立即可见。
  • 不管哪个分支,都 removeManualSingletonName(beanName)------一个新定义注册进来,如果之前这个名字是手动单例,就把它从手动单例名单里划掉。
  • frozenBeanDefinitionNames = null:配置一旦变动,冻结时拍的快照就作废了。

5.4 重置旧状态:resetBeanDefinition

方法末尾(第 1321-1331 行):

java 复制代码
if (existingDefinition != null || containsSingleton(beanName)) {
	resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
	clearByTypeCache();
}

// Cache a primary marker for the given bean.
if (beanDefinition.isPrimary()) {
	this.primaryBeanNamesWithType.put(beanName, Void.class);
}

如果这次注册是"覆盖了一个已有定义"或"这个名字已经有单例实例了",就必须调 resetBeanDefinition(beanName) 把之前缓存的派生状态清掉。resetBeanDefinition(第 1412-1439 行)做四件事:

  1. clearMergedBeanDefinition(beanName)------把合并定义缓存里这个 bean 标记为 stale;
  2. destroySingleton(beanName)------把已经造好的同名单例销毁掉(不然新定义配出来的还是旧实例);
  3. primaryBeanNamesWithType.remove(beanName)------清掉 primary 标记;
  4. 遍历所有 MergedBeanDefinitionPostProcessor,通知它们"这个定义被重置了";然后递归 地找所有以这个 bean 为 parent 的子定义,对它们也调 resetBeanDefinition。

最后,如果新定义标了 primary,往 primaryBeanNamesWithType 放一条缓存。

把 registerBeanDefinition 串起来看,它是一个"读-校验-冲突检测-写表-清缓存-通知派生者"的完整事务。它的并发模型可以总结为一句话:启动期无锁快路径,启动后锁内写时复制。

六、源码剖析(四):preInstantiateSingletons 与 7.0 的并行预实例化

这是 Spring 容器启动的"开火"瞬间。DefaultListableBeanFactory.preInstantiateSingletons()(第 1101-1151 行)在 7.0.9 里的完整源码:

java 复制代码
@Override
public void preInstantiateSingletons() throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}

	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

	// Trigger initialization of all non-lazy singleton beans...
	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();
		}
	}
}

6.1 逐段解读

  1. 复制一份名单再遍历 。new ArrayList<>(this.beanDefinitionNames)------注释说明原因:init 方法里可能反过来注册新 bean 定义,遍历副本允许这种"边遍历边注册"的行为,不会 ConcurrentModificationException。
  2. 绑定线程身份 。preInstantiationThread.set(PreInstantiation.MAIN) 把当前线程标记为"主线程预实例化者";如果还没记录主线程名前缀,用 getThreadNamePrefix()(第 1240-1244 行)取线程名最后一个 - 之前的部分------比如 main 线程名就是 main,Test worker - 1 就取 Test worker。这个前缀后面用来识别"冒名顶替的外部线程"。
  3. 逐个判断是否需要预实例化 。对每个名字拿合并定义 getMergedLocalBeanDefinition(beanName),只处理"非抽象 + 单例"的 bean。注意 getMergedLocalBeanDefinition 返回的是合并后的 RootBeanDefinition------哪怕注册时是个 ChildBeanDefinition 或 AnnotatedGenericBeanDefinition,到这里已经被合并成 RootBeanDefinition 了。
  4. 调用 preInstantiateSingleton 。这个方法在 7.0.9 里返回一个 CompletableFuture<?> ------这是和旧版最大的不同。旧版(6.x)这个方法是 void,主循环同步调 getBean(beanName)。7.0 允许它返回一个 future,代表"这个 bean 在后台线程造着"。
  5. 收集 future 并 join 。所有后台任务提交完后,CompletableFuture.allOf(...).join() 阻塞等它们全部结束。任何一个失败,拆 CompletionException 把真实异常抛出来。
  6. SmartInitializingSingleton 回调 。所有单例造完后,再遍历一次名单,对每个 singleton 实例判断是不是 SmartInitializingSingleton,是就调 afterSingletonsInstantiated()。这个回调的时机很关键------它发生在所有常规单例都就绪之后 ,所以它内部 getBean 不会触发循环依赖。

6.2 preInstantiateSingleton:前台/后台分支

第 1153-1199 行:

java 复制代码
private @Nullable CompletableFuture<?> preInstantiateSingleton(String beanName, RootBeanDefinition mbd) {
	if (mbd.isBackgroundInit()) {
		Executor executor = getBootstrapExecutor();
		if (executor != null) {
			// Force initialization of depends-on beans in mainline thread.
			String[] dependsOn = mbd.getDependsOn();
			if (dependsOn != null) {
				for (String dep : dependsOn) {
					getBean(dep);
				}
			}
			// Force initialization of factory reference in mainline thread.
			String factoryBeanName = mbd.getFactoryBeanName();
			if (factoryBeanName != null) {
				getBean(factoryBeanName);
			}
			// Instantiate current bean in background thread.
			CompletableFuture<?> future = CompletableFuture.runAsync(
					() -> instantiateSingletonInBackgroundThread(beanName), executor);
			addSingletonFactory(beanName, () -> {
				try {
					future.join();
				}
				catch (CompletionException ex) {
					ReflectionUtils.rethrowRuntimeException(ex.getCause());
				}
				return future;  // not to be exposed, just to lead to ClassCastException in case of mismatch
			});
			return (!mbd.isLazyInit() ? future : null);
		}
		else if (logger.isInfoEnabled()) {
			logger.info("Bean '" + beanName + "' marked for background initialization " +
					"without bootstrap executor configured - falling back to mainline initialization");
		}
	}

	if (!mbd.isLazyInit()) {
		try {
			instantiateSingleton(beanName);
		}
		catch (BeanCurrentlyInCreationException ex) {
			logger.info("Bean '" + beanName + "' marked for pre-instantiation (not lazy-init) " +
					"but currently initialized by other thread - skipping it in mainline thread");
		}
	}
	return null;
}

这一段是 7.0 并发模型的心脏,逐段讲:

  • mbd.isBackgroundInit() :这个 bean 是否被标记为"后台初始化"(7.0 在 RootBeanDefinition 上加了 backgroundInit 标志,可以通过 BeanDefinitionBuilder 或 XML 属性开启)。
  • 配了 bootstrapExecutor 才真正并行。如果没配 executor,打一条 info 日志"退化为主线程初始化",落到下面的同步分支。这是一种优雅降级------标了后台初始化但没配执行器,行为和旧版一致。
  • 后台实例化前的"主先"保证 。要把 bean 丢到后台线程去造,它的 dependsOn 依赖和 factoryBeanName 工厂引用必须先在主线程造好 。原因:后台线程造这个 bean 时会反过来 getBean(dep),如果 dep 还没造,就会跨线程触发创建;而单例创建的早期暴露(三级缓存)设计上只允许在原创建线程内推断(前面提过的 tryLock 快速失败)。所以容器选择在主线程把这些"前置依赖"先造好,后台线程只造这个 bean 本身。
  • addSingletonFactory 的妙用 。注意那个 lambda:它往三级缓存里塞了一个 ObjectFactory,这个 factory 的 getObject() 会 future.join() 等后台线程造完,然后返回 future 本身(注释说"不是用来暴露的,只是为了类型不匹配时抛 ClassCastException")。这意味着:在后台线程还没造完时,别的线程如果来 getBean(beanName),会被这个 ObjectFactory 阻塞住直到后台造完------既不允许提前拿到半成品,也不会死锁。
  • BeanCurrentlyInCreationException 的宽容处理 。主线程走到一个 bean 时,如果它已经被别的后台线程开始造了(instantiateSingleton 内部会抛这个异常),主线程不报错,只打一条 info "skipping it in mainline thread",直接跳过。这就是"宽松锁"哲学:多个线程同时准备造同一个 bean,谁先抢到算谁的,后来者让开。

6.3 instantiateSingleton 与 SmartFactoryBean

第 1217-1227 行:

java 复制代码
private void instantiateSingleton(String beanName) {
	if (isFactoryBean(beanName)) {
		Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
		if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {
			getBean(beanName);
		}
	}
	else {
		getBean(beanName);
	}
}

对普通 bean 直接 getBean。对 FactoryBean,先 getBean("&beanName") 把 FactoryBean 本身造出来;然后判断它是不是 SmartFactoryBean 且 isEagerInit()------如果是,再 getBean(beanName) 把它生产的对象也提前造出来。SmartFactoryBean(路径 factory/SmartFactoryBean.java)在普通 FactoryBean 之外加了两个 default 方法(第 105、124 行):isPrototype() 和 isEagerInit()。普通 FactoryBean 的产物是懒加载的(第一次 getBean("xxx") 才调 getObject()),但 SmartFactoryBean.isEagerInit() 返回 true 时,容器在预实例化阶段就把产物也造好------比如一个数据库连接池的 FactoryBean,希望启动时就建好池。

6.4 这个设计为什么重要

旧版 Spring 启动是单线程的:所有单例在主线程按顺序造,bean 越多启动越慢。7.0 的方案不是简单地"所有 bean 丢线程池并行造"------那会破坏循环依赖的早期暴露语义。它的策略是:

  • 默认行为不变 :不配 bootstrapExecutor,一切照旧单线程;
  • 显式 opt-in :只有标了 backgroundInit 的 bean 才进后台;
  • 前置依赖主线程先造:保证后台 bean 启动时它的依赖已经在主线程就绪;
  • 三级缓存挂 future:让并发访问者安全阻塞等待。

这种"保守并行"是 Spring 一贯的工程风格------不牺牲正确性去换速度。STRICT_LOCKING_PROPERTY_NAME 那个系统属性,则给了一个"回退到 6.1 严格锁风格"的逃生阀。

七、源码剖析(五):AbstractBeanFactory.doGetBean 全流程

doGetBean(路径 spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java 第 235-400 行)是 getBean 的真正实现。所有 getBean(...) 重载都汇聚到这里(第 194-222 行:四个 public getBean 方法各自一行 return doGetBean(...))。下面贴出完整方法体并逐段讲。

7.1 名字解析与单例快路径

java 复制代码
@SuppressWarnings("unchecked")
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) {
		if (logger.isTraceEnabled()) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
						"' that is not fully initialized yet - a consequence of a circular reference");
			}
			else {
				logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
			}
		}
		beanInstance = getObjectForBeanInstance(sharedInstance, requiredType, name, beanName, null);
	}

第一步 transformedBeanName(name)(第 1271-1272 行):

java 复制代码
protected String transformedBeanName(String name) {
	return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

它做两件事:先用 BeanFactoryUtils.transformedBeanName 剥掉开头的 & 前缀(7.0.9 里用 FACTORY_BEAN_PREFIX_CHAR 这个 char 常量做前缀判断,避免字符串拼接),再用 canonicalName 把别名链一路追到最终规范名。也就是说,传进来的 name 可能是 "&myJndiObject"、"aliasOfUserService" 或 "userService",经过这一步都变成干净的 beanName = "userService"。

紧接着 getSingleton(beanName) 查单例表。这个方法(继承自 DefaultSingletonBeanRegistry)走的是三级缓存查找:一级 singletonObjects、二级 earlySingletonObjects、三级 singletonFactories。如果命中(说明这个 singleton 已经造好了,或者正在被别人造、有早期引用),且调用方没有传构造参数 args,就直接进入 getObjectForBeanInstance 后处理------这是热路径,绝大多数 getBean 都停在这里,不进下面的创建逻辑。

注意 trace 日志里的分支:isSingletonCurrentlyInCreation(beanName) 为 true 意味着拿到的是"正在创建中的早期引用"------这是循环依赖的信号。

7.2 原型循环检测与父工厂回退

java 复制代码
	else {
		// Fail if we're already creating this bean instance:
		// We're assumably within a circular reference.
		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);
			}
			else if (args != null) {
				// Delegation to parent with explicit args.
				return (T) parentBeanFactory.getBean(nameToLookup, args);
			}
			else if (requiredType != null) {
				// No args -> delegate to standard getBean method.
				return parentBeanFactory.getBean(nameToLookup, requiredType);
			}
			else {
				return (T) parentBeanFactory.getBean(nameToLookup);
			}
		}
  • 原型循环依赖直接报错 。singleton 的循环依赖可以靠三级缓存绕过去,但 prototype 不行------prototype 不缓存,容器没法提前暴露一个"正在造的 prototype"引用。所以一旦发现"当前线程正在造这个 prototype",立刻抛 BeanCurrentlyInCreationException。
  • 父工厂回退 。如果本地工厂没有这个 bean 的定义(!containsBeanDefinition(beanName)),且有父工厂,就把请求转发给父工厂。originalBeanName(name) 把原始的 & 前缀和别名信息恢复出来(因为父工厂里的别名表是独立的)。如果父工厂也是 AbstractBeanFactory 家族,直接递归调它的 doGetBean;否则按 args/requiredType 的有无,选对应的 getBean 重载转发。这就是 HierarchicalBeanFactory 层次结构在 getBean 路径上的兑现------只在本地定义表找不到时才向上问。

7.3 合并定义与 depends-on

java 复制代码
		if (!typeCheckOnly) {
			markBeanAsCreated(beanName);
		}

		StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
				.tag("beanName", name);
		try {
			if (requiredType != null) {
				beanCreation.tag("beanType", requiredType::toString);
			}
			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(mbd.getResourceDescription(), beanName,
								"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
					}
					registerDependentBean(dep, beanName);
					try {
						getBean(dep);
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
								"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
					}
					// ...... BeanCreationException 包装略
				}
			}
  • markBeanAsCreated(beanName) :标记"已经开始创建这个 bean"。这一步把 alreadyCreated 集合里加一条,hasBeanCreationStarted() 从此返回 true------前面 registerBeanDefinition 里那个并发分支就靠它判断。
  • getMergedLocalBeanDefinition(beanName) :拿合并后的 RootBeanDefinition。注册时的 GenericBeanDefinition/ChildBeanDefinition 到这里全部拍平成 RootBeanDefinition。
  • checkMergedBeanDefinition (第 1512-1516 行):如果合并定义标了 abstract,抛 BeanIsAbstractException------抽象定义只是用来当父模板的,不能直接 getBean。
  • depends-on 处理 。如果配了 depends-on,先检查循环依赖(isDependent:如果 dep 已经反过来依赖 beanName,那就是循环 depends-on,直接报错);再 registerDependentBean(dep, beanName) 登记"beanName 依赖 dep"这个关系(这张关系表用于销毁顺序:dep 要在 beanName 之后销毁);最后 getBean(dep) 强制把依赖先造出来。注意 depends-on 是硬依赖 ------它和 @Autowired 的注入不同,depends-on 只保证"先造",不负责把 dep 引用塞进 beanName 的字段。

7.4 三大作用域分支

java 复制代码
			// Create bean instance.
			if (mbd.isSingleton()) {
				sharedInstance = getSingleton(beanName, () -> {
					try {
						return createBean(beanName, mbd, args);
					}
					catch (BeansException ex) {
						// Explicitly remove instance from singleton cache: It might have been put there
						// eagerly by the creation process, to allow for circular reference resolution.
						destroySingleton(beanName);
						throw ex;
					}
				});
				beanInstance = getObjectForBeanInstance(sharedInstance, requiredType, name, beanName, mbd);
			}

			else if (mbd.isPrototype()) {
				// It's a prototype -> create a new instance.
				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();
				if (!StringUtils.hasLength(scopeName)) {
					throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
				}
				Scope scope = this.scopes.get(scopeName);
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
				}
				try {
					Object scopedInstance = scope.get(beanName, () -> {
						beforePrototypeCreation(beanName);
						try {
							return createBean(beanName, mbd, args);
						}
						finally {
							afterPrototypeCreation(beanName);
						}
					});
					beanInstance = getObjectForBeanInstance(scopedInstance, requiredType, name, beanName, mbd);
				}
				catch (IllegalStateException ex) {
					throw new ScopeNotActiveException(beanName, scopeName, ex);
				}
			}

三个分支结构高度一致:拿一个对象(singleton 从单例表取/造、prototype 直接造、自定义 scope 从 Scope 实现里取/造),然后都过一道 getObjectForBeanInstance。

  • singleton 分支 :getSingleton(beanName, ObjectFactory) 是带创建逻辑的重载------它先查单例表(查不到才执行工厂 lambda),把 createBean(beanName, mbd, args) 包在 lambda 里。创建失败时 destroySingleton(beanName) 清理可能被早期暴露出去的半成品。
  • prototype 分支 :beforePrototypeCreation/afterPrototypeCreation 在当前线程的 prototypesCurrentlyInCreation ThreadLocal 里登记/摘除,配合前面那个"原型循环检测"。
  • 自定义 scope 分支 :this.scopes 是 AbstractBeanFactory 第 158 行声明的 Map<String, Scope>,web 环境的 request/session 就注册在这里。scope.get(beanName, objectFactory) 由 Scope 实现决定"是复用还是新建",比如 session scope 就是从 HttpSession 里取属性。

7.5 收尾:类型转换与元数据清理

java 复制代码
		}
		catch (BeansException ex) {
			beanCreation.tag("exception", ex.getClass().toString());
			beanCreation.tag("message", String.valueOf(ex.getMessage()));
			cleanupAfterBeanCreationFailure(beanName);
			throw ex;
		}
		finally {
			beanCreation.end();
			if (!isCacheBeanMetadata()) {
				clearMergedBeanDefinition(beanName);
			}
		}
	}

	return adaptBeanInstance(name, beanInstance, requiredType);
}
  • 失败时 cleanupAfterBeanCreationFailure 把这个 bean 从"正在创建"等集合里清掉,避免污染后续请求。
  • finally 里:如果工厂不配缓存 bean 元数据(isCacheBeanMetadata() 为 false,这种工厂用完即弃),就 clearMergedBeanDefinition(beanName) 把合并定义标记为 stale------下次再 getBean 时重新合并。
  • 最后 adaptBeanInstance(第 402-419 行):如果调用方传了 requiredType 而实际对象不是该类型,尝试用 TypeConverter 做一次转换(比如把 String 转成 Integer),转不了抛 BeanNotOfRequiredTypeException。

至此,doGetBean 的全貌就清楚了:名字解析 → 单例快路径 → 原型循环检测 → 父工厂回退 → 合并定义 → depends-on → 按 scope 创建 → FactoryBean 后处理 → 类型适配 。其中 createBean 本身的细节(实例化、属性填充、初始化)属于生命周期范畴,本章不展开。

八、源码剖析(六):FactoryBean 机制与 getObjectForBeanInstance

8.1 FactoryBean 接口本身

FactoryBean<T>(路径 spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java)只有三个方法:

java 复制代码
@Nullable T getObject() throws Exception;
@Nullable Class<?> getObjectType();
default boolean isSingleton() {
	return true;
}
  • getObject():生产 bean 实例。
  • getObjectType():提前告诉容器这个工厂会生产什么类型。注释(第 97-116 行)强调:这个方法可能在 FactoryBean 完全初始化之前就被调用,不能依赖初始化期间才建立的状态;如果返回 null,自动装配会直接忽略这个 FactoryBean------所以正确实现它很重要。
  • isSingleton():默认 true,意味着 getObject() 每次返回同一个引用,工厂会把产物缓存起来。

SmartFactoryBean<T> extends FactoryBean<T>(同目录,第 52 行)加了两个 default 方法:isPrototype()(第 105 行)和 isEagerInit()(第 124 行)。前面预实例化那节已经见过 isEagerInit 的作用。

8.2 getObjectForBeanInstance:FactoryBean 后处理

doGetBean 无论哪个分支拿到对象后,都要过一道 getObjectForBeanInstance(第 1841-1882 行)。完整源码:

java 复制代码
protected Object getObjectForBeanInstance(Object beanInstance, @Nullable Class<?> requiredType,
		String name, String beanName, @Nullable RootBeanDefinition mbd) {

	// Don't let calling code try to dereference the factory if the bean isn't a factory.
	if (BeanFactoryUtils.isFactoryDereference(name)) {
		if (beanInstance instanceof NullBean) {
			return beanInstance;
		}
		if (!(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
		}
		if (mbd != null) {
			mbd.isFactoryBean = true;
		}
		return beanInstance;
	}

	// Now we have the bean instance, which may be a normal bean or a FactoryBean.
	// If it's a FactoryBean, we use it to create a bean instance, unless the
	// caller actually wants a reference to the factory.
	if (!(beanInstance instanceof FactoryBean<?> factoryBean)) {
		return beanInstance;
	}

	Object object = null;
	if (mbd != null) {
		mbd.isFactoryBean = true;
	}
	else {
		object = getCachedObjectForFactoryBean(beanName);
	}
	if (object == null) {
		// Return bean instance from factory.
		// Caches object obtained from FactoryBean if it is a singleton.
		if (mbd == null && containsBeanDefinition(beanName)) {
			mbd = getMergedLocalBeanDefinition(beanName);
		}
		boolean synthetic = (mbd != null && mbd.isSynthetic());
		object = getObjectFromFactoryBean(factoryBean, requiredType, beanName, !synthetic);
	}
	return object;
}

逐段讲:

  1. & 前缀分支 。BeanFactoryUtils.isFactoryDereference(name) 判断原始 name(注意是 name 不是 beanName------beanName 已经被 transformedBeanName 剥过 & 了)是不是带 &。如果带 &,调用方明确要 FactoryBean 本身:
    • 如果实际对象是 NullBean,原样返回;
    • 如果实际对象不是 FactoryBean,抛 BeanIsNotAFactoryException------你要 &xxx 说明你以为 xxx 是工厂,结果它不是,这是用法错误;
    • 否则标记 mbd.isFactoryBean = true,直接返回 FactoryBean 本身 ,不调 getObject()。
  2. 普通 bean 直通 。如果拿到的对象连 FactoryBean 都不是,直接返回------普通 bean 的后处理到此结束。
  3. 不带 & 的 FactoryBean 。走到这里说明 beanInstance 确实是个 FactoryBean,但调用方要的是它生产的对象。如果有 mbd,标记 mbd.isFactoryBean=true;否则先查 FactoryBean 产物缓存(getCachedObjectForFactoryBean,这是 FactoryBeanRegistrySupport 维护的一个缓存 Map)。
  4. 缓存未命中则调 getObjectFromFactoryBean 。如果 mbd 为 null 但本地有定义,临时合并一份 mbd。synthetic 标志决定是否做后置处理:合成的(synthetic,比如内部 bean)不做 BeanPostProcessor 回调。真正调 factoryBean.getObject() 的是 getObjectFromFactoryBean(在 FactoryBeanRegistrySupport 里),它负责把 singleton 产物缓存起来。

这个方法是 & 协议的物理落点:带 & 返回工厂,不带 & 返回工厂产品 。doGetBean 里两处调用它------快路径(第 255 行)和创建后(第 343/356/378 行)------用的是同一个方法,所以无论 singleton 还是 prototype 还是自定义 scope,FactoryBean 后处理逻辑完全一致。

8.3 类型提前探测:为什么 getType 可能触发 FactoryBean 初始化

FactoryBean.getObjectType() 的设计目的,就是让容器在不真正调 getObject() 的情况下知道产物类型。这在按类型查找(getBeanNamesForType、@Autowired 候选筛选)时至关重要------比如你写 @Autowired DataSource ds;,容器要能列出所有类型为 DataSource 的 bean,包括那些由 FactoryBean<DataSource> 生产出来的。

但问题来了:如果某个 FactoryBean 的 getObjectType() 写得不规范,必须等到 getObject() 跑过才能算出类型怎么办?这时容器有两个选择:

  • 保守:返回 null,承认"类型不可知"。
  • 激进 :把 FactoryBean 初始化出来,调一次 getObject() 看实际类型。

getType(String, boolean allowFactoryBeanInit) 那个参数就是控制这个开关的。类型匹配的批量场景(比如 getBeansOfType)会传 allowEagerInit,决定要不要为了看类型而触发副作用。这也是为什么 ListableBeanFactory.getBeanNamesForType 的注释反复警告:"passing in true for this flag will initialize FactoryBeans"。

8.4 FactoryBean 在 findAutowireCandidates 中的位置

后面讲依赖解析时会看到,按类型找候选 bean 时,容器对每个候选名字调 isTypeMatch(beanName, type)。这个方法内部会判断"这个 bean 是不是 FactoryBean,如果是,比较的是它 getObjectType() 的类型,而不是 FactoryBean 本身的类型"。这就是为什么你 @Autowired DataSource 能注入到一个 <bean id="dataSource" class="...DataSourceFactoryBean"/>------容器比较的是 FactoryBean 生产的 BasicDataSource 类型,不是 FactoryBean 本身。

一个常见坑:如果 DataSourceFactoryBean.getObjectType() 返回 null,那 @Autowired DataSource 就找不到它。这正是 FactoryBean 注释里那句"Autowiring will simply ignore FactoryBeans that return null here"的含义。

九、源码剖析(七):BeanDefinition 元模型

BeanDefinition 是 IoC 容器的"配方接口"。这一节把它的继承树从根到叶讲清楚。

9.1 BeanDefinition 接口

路径 spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java(378 行)。它本身继承了两个标记接口:AttributeAccessor(属性访问器,在 beans/PropertyAccessor.java 隔壁,实际在 core/AttributeAccessor.java)和 BeanMetadataElement(元数据元素,在 beans/BeanMetadataElement.java)。

它定义的关键方法(按职责分组):

  • 类定位 :getBeanClassName()(第 121 行)、setBeanClassName、getParentName()(第 97 行)。
  • 作用域 :getScope()/setScope(String)(第 128-134 行)。
  • 懒加载 :isLazyInit()(第 147 行)。
  • 依赖 :getDependsOn()/setDependsOn。
  • 构造参数与属性 :getConstructorArgumentValues()、getPropertyValues(),以及两个 5.1 的 default 方法 hasConstructorArgumentValues()(第 253 行)、hasPropertyValues()(第 269 行)。
  • 生命周期方法 :getInitMethodName()(第 283 行)、getDestroyMethodName()(第 295 行)。
  • 角色 :getRole()(第 316 行),返回 ROLE_APPLICATION(用户定义)/ROLE_SUPPORT(外部支持)/ROLE_INFRASTRUCTURE(容器自身)三档。
  • 工厂相关 :getFactoryBeanName()/getFactoryMethodName()。
  • 候选标志 :isAutowireCandidate()、isPrimary()。

AttributeAccessor 接口提供了一组"任意名字-值"的附件能力:setAttribute(String, Object)/getAttribute(String)/hasAttribute/attributeNames。它的实现是 AttributeAccessorSupport(core/AttributeAccessorSupport.java),内部维护一个 Map<String, Object>。为什么 BeanDefinition 需要这个?因为框架的很多扩展(比如注解元数据、AOP 配置标记)不想污染 BeanDefinition 的正式字段,就以"属性"的形式挂上去。BeanMetadataAttributeAccessor(beans/factory/config/BeanMetadataAttributeAccessor.java)在 AttributeAccessorSupport 基础上加了 source(来源资源)支持,AbstractBeanDefinition 就继承它。

9.2 AbstractBeanDefinition:配方的通用骨架

路径 support/AbstractBeanDefinition.java,第 61 行:

java 复制代码
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
		implements BeanDefinition, Serializable {

它把 BeanDefinition 接口的通用字段全部实现了。关键字段(第 169-221 行):

java 复制代码
private volatile @Nullable Object beanClass;   // 169 行:可能是 Class,也可能是 String 类名
private @Nullable String scope;
private boolean lazyInit;
private @Nullable String[] dependsOn;
private @Nullable Supplier<?> instanceSupplier;  // 195 行:7.0 编程式注册用
private int role = BeanDefinition.ROLE_APPLICATION; // 221 行

注意 beanClass 字段的类型是 Object 而不是 Class<?>------它既可以是已经解析好的 Class,也可以是一个尚未加载的 String 类名 。getBeanClassName()(第 421-422 行)做了防御性读取:

java 复制代码
Object beanClassObject = this.beanClass;  // defensive access to volatile beanClass field
return (beanClassObject instanceof Class<?> clazz ? clazz.getName() : (String) beanClassObject);

这种"二态字段"设计是为了支持延迟类加载 :XML 解析阶段只拿到类名字符串,不立刻 Class.forName;等到真正需要实例化时才解析成 Class。这就是为什么 allowEagerClassLoading 那个开关能生效------by-type 查询时如果类还没加载,可以选择不强行加载。

validate() 方法前面 registerBeanDefinition 那节已经讲过:校验方法覆写与工厂方法不兼容。

9.3 GenericBeanDefinition:通用配方

路径 support/GenericBeanDefinition.java。它是 2.5 引入的"通用版"定义,取代了早期 XML 里 <bean> 元素必须区分 root/child 的繁琐。它就是 AbstractBeanDefinition 加一个 parentName 字段:

java 复制代码
public void setParentName(@Nullable String parentName) { ... }
public @Nullable String getParentName() { ... }

GenericBeanDefinition 的特点是可继承父定义 但自身也可以独立存在(不强制有父)。XML 命名空间、注解驱动注册出来的定义,绝大多数都是 GenericBeanDefinition 或它的子类。

9.4 ChildBeanDefinition:父子继承的显式子类

路径 support/ChildBeanDefinition.java。版本事实:7.0.9 中它并没有被 @Deprecated 标记 ------它依然是一个活的、可编译的类。它的构造函数强制要求 parentName:

java 复制代码
public ChildBeanDefinition(String parentName) { ... }
public ChildBeanDefinition(String parentName, MutablePropertyValues pvs) { ... }

它的语义是"必须依附于一个父定义",自己只覆盖部分属性。在 XML 时代,<bean id="child" parent="parentBean"> 解析出来就是 ChildBeanDefinition。现在注解驱动场景几乎不用它,但它仍然存在于类路径上,合并定义逻辑必须兼容它。

9.5 RootBeanDefinition:合并终点

路径 support/RootBeanDefinition.java,第 64 行 public class RootBeanDefinition extends AbstractBeanDefinition。它是"已经合并好的、可以直接用于创建"的定义。它不支持 parentName(getParentName 默认返回 null),因为它已经是父-子合并的产物。

它比 AbstractBeanDefinition 多出来的,是一堆运行时解析缓存字段(第 77-122 行):

java 复制代码
volatile @Nullable ResolvableType targetType;
volatile @Nullable Class<?> resolvedTargetType;
volatile @Nullable Boolean isFactoryBean;
volatile @Nullable ResolvableType factoryMethodReturnType;
volatile @Nullable Method factoryMethodToIntrospect;
volatile @Nullable String resolvedDestroyMethodName;
volatile @Nullable Boolean beforeInstantiationResolved;
private @Nullable Set<Member> externallyManagedConfigMembers;
private @Nullable Set<String> externallyManagedInitMethods;
private @Nullable Set<String> externallyManagedDestroyMethods;

这些字段全是 volatile,因为合并后的 RootBeanDefinition 可能被多个线程并发访问。它们的作用是把"第一次解析出来的结果"缓存住:比如第一次判断出这个 bean 是不是 FactoryBean,就把结论存在 isFactoryBean 里,下次直接用,不用重复 introspect。externallyManaged* 那几个集合存的是被 @Autowired/@Resource 标注的成员和方法------由 MergedBeanDefinitionPostProcessor 在合并后填进去。

9.6 注解场景:AnnotatedGenericBeanDefinition 与 ScannedGenericBeanDefinition

  • AnnotatedGenericBeanDefinition :路径 spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java。它在 GenericBeanDefinition 之上加了 AnnotatedTypeMetadata(注解元数据)和 MethodMetadata(方法元数据)。编程式 registry.registerBean(MyConfig.class) 或 AnnotatedBeanDefinitionReader 读 @Configuration 类时用它。
  • ScannedGenericBeanDefinition :路径 spring-context/src/main/java/org/springframework/context/annotation/ScannedGenericBeanDefinition.java。组件扫描(@ComponentScan)扫出来的 bean 用它------它持有扫描时读到的注解元数据,但此时类可能还没被类加载(这是它和 AnnotatedGenericBeanDefinition 的关键区别:后者类已经加载,前者用 ASM 读元数据、类延迟加载)。

这两个类的共同父类是 AnnotatedBeanDefinition(spring-beans/.../factory/annotation/AnnotatedBeanDefinition.java),它扩展了 BeanDefinition,加了 getMetadata()、getFactoryMethodMetadata() 两个方法。

9.7 MergedBeanDefinition:为什么要"合并"

容器里同时存在这么多种 BeanDefinition:注册进来的是 GenericBeanDefinition/ChildBeanDefinition/AnnotatedGenericBeanDefinition,但创建时需要的是一个"最终配方"。这个拍平过程就是合并 ,产物是 RootBeanDefinition,缓存在 AbstractBeanFactory.mergedBeanDefinitions(第 164 行):

java 复制代码
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);

getMergedBeanDefinition(第 1396-1472 行)的关键逻辑前面已经读过,这里总结它的合并规则:

  1. 如果注册定义没有 parentName:如果它本身是 RootBeanDefinition,cloneBeanDefinition() 复制一份;否则 new RootBeanDefinition(bd) 把它包成 Root。
  2. 如果有 parentName:递归 getMergedBeanDefinition(parentBeanName) 拿到父的合并定义,new RootBeanDefinition(pbd) 深拷贝父,再 mbd.overrideFrom(bd) 用子定义覆盖同名属性。overrideFrom(在 AbstractBeanDefinition 里)的规则是"子定义显式设过的属性覆盖父的,没设的沿用父的"。
  3. 合并后如果 scope 为空,默认设为 singleton(SCOPE_SINGLETON)。
  4. 如果是嵌套在一个非 singleton 外部 bean 里的内部 bean,强制把内部 bean 的 scope 改成外部 bean 的 scope------不然会出现"外层 request scope、内层 singleton"这种矛盾。
  5. 合并结果按条件缓存(cacheMergedBeanDefinition,第 1501-1503 行,就是往 mergedBeanDefinitions 里 put)。

缓存失效靠 stale 标志:clearMergedBeanDefinition(第 1523-1528 行)不删条目,只把 bd.stale = true;下次访问时发现 mbd.stale 就重新合并。这就是 registerBeanDefinition 末尾 resetBeanDefinition 要调 clearMergedBeanDefinition 的原因------旧配方变了,合并缓存作废。

至此,BeanDefinition 体系的全貌是:接口(BeanDefinition)→ 抽象骨架(AbstractBeanDefinition)→ 可注册的具体配方(Generic/Root/Child/Annotated/Scanned)→ 运行时合并产物(RootBeanDefinition + 缓存)。

十、源码剖析(八):依赖解析 doResolveDependency 与候选筛选

@Autowired 字段到底怎么找到 bean 的?入口是 DefaultListableBeanFactory.resolveDependency,它调 doResolveDependency(第 1661-1776 行)。这是 IoC 容器里最精巧的方法之一,我们逐段拆。

10.1 六个步骤总览

java 复制代码
public @Nullable Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
		@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

	InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
	try {
		// Step 1: pre-resolved shortcut for single bean match, for example, from @Autowired
		Object shortcut = descriptor.resolveShortcut(this);
		if (shortcut != null) {
			return shortcut;
		}

		Class<?> type = descriptor.getDependencyType();

		// Step 2: pre-defined value or expression, for example, from @Value
		Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
		// ...... @Value 解析 + 类型转换,略
		// Step 3: declared dependency name or qualifier-suggested name
		if (descriptor.usesStandardBeanLookup()) { ...... }
		// Step 4a: stream / array / collection / map
		Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
		if (multipleBeans != null) { return multipleBeans; }
		// Step 4b: direct bean matches
		Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
		// Step 5: determine single candidate
		if (matchingBeans.size() > 1) {
			autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
			// ......
		}
		// Step 6: validate single result
		// ......
	}
	finally {
		ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
	}
}

六个步骤:

  1. 短路 :descriptor.resolveShortcut(this)。少数特殊注入点(如某些场景下的 @Bean 方法参数)可以直接给出结果,跳过后面所有流程。
  2. @Value :getAutowireCandidateResolver().getSuggestedValue(descriptor)。如果注入点标了 @Value("${...}"),这里拿到值字符串,再 resolveEmbeddedValue 解析占位符、evaluateBeanDefinitionString 求值 SpEL、最后用 TypeConverter 转成字段类型。这一步和"按类型找 bean"完全无关------它是值注入。
  3. 按名字直查 :如果注入点有声明的依赖名(比如字段名叫 userService)或 qualifier 建议名,先试 containsBean(dependencyName),命中且类型匹配、是 autowire candidate、不是 fallback、没有 primary 冲突、不是自引用,就直接 resolveBean(dependencyName, ...) 返回。这是"by-name"快路径------字段名恰好等于某个 bean 名时,不必走全表扫描。
  4. 多 bean 集合 :如果依赖类型是 Stream/数组/List/Map,走 resolveMultipleBeans 把所有匹配的 bean 收集成集合。
  5. 找候选 + 定唯一 :findAutowireCandidates 拿到所有类型匹配的候选 Map;候选多于一个时,determineAutowireCandidate 在里面挑一个。
  6. 校验 :resolveInstance 检查 NullBean、做 isAssignableValue 类型校验。

finally 里把 InjectionPoint 恢复成之前的值------setCurrentInjectionPoint 用 ThreadLocal 记录"当前正在解析哪个注入点",这个上下文在 @Value 求值 SpEL、ObjectFactory 延迟解析时都会用到。

10.2 findAutowireCandidates:候选从哪来

第 1959-2005 行:

java 复制代码
protected Map<String, Object> findAutowireCandidates(
		@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

	String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
			this, requiredType, true, descriptor.isEager());
	Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
	for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
		Class<?> autowiringType = classObjectEntry.getKey();
		if (autowiringType.isAssignableFrom(requiredType)) {
			Object autowiringValue = classObjectEntry.getValue();
			autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
			if (requiredType.isInstance(autowiringValue)) {
				result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
				break;
			}
		}
	}
	for (String candidate : candidateNames) {
		if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
			addCandidateEntry(result, candidate, descriptor, requiredType);
		}
	}
	// ...... 空结果时的 fallback 两轮尝试,略
	return result;
}

逐段讲:

  1. beanNamesForTypeIncludingAncestors :列出本地 + 父工厂里所有类型匹配的 bean 名。这一步可能触发 isTypeMatch,进而可能触发 FactoryBean 类型探测。
  2. 先查内建可注入对象表 resolvableDependencies 。遍历这张表,凡是登记的类型能赋值给依赖类型(autowiringType.isAssignableFrom(requiredType))且实际值是该类型的实例,就作为候选加进去。key 用 identityToString(autowiringValue)------因为内建对象没有 bean 名。
  3. 再遍历普通候选 。跳过自引用(isSelfReference:注入点所在的 bean 自己),且 isAutowireCandidate(candidate, descriptor) 为 true(即这个 bean 定义没标 autowire-candidate="false",且通过了 qualifier 检查)才加入。
  4. 空结果时的两轮 fallback :第一轮用 descriptor.forFallbackMatch() 放宽类型匹配(处理泛型擦除等情况);第二轮连自引用也放进来------这是为了在极端情况下(比如一个 bean 依赖自己的早期引用)还能凑出候选。

addCandidateEntry(第 2011-2028 行)有个性能细节:对于已经是单例的 bean,直接 resolveCandidate 拿实例;否则只放 getType(candidateName)(Class 对象),不提前实例化。注释说"prevent early bean initialization ahead of primary candidate selection"------在还没决定选哪个之前,不要急着造 bean。

10.3 determineAutowireCandidate:多候选时的仲裁顺序

第 2038-2082 行:

java 复制代码
protected @Nullable String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
	Class<?> requiredType = descriptor.getDependencyType();
	// Step 1: check primary candidate
	String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
	if (primaryCandidate != null) {
		return primaryCandidate;
	}
	// Step 2a: match bean name against declared dependency name
	String dependencyName = descriptor.getDependencyName();
	if (dependencyName != null) {
		for (String beanName : candidates.keySet()) {
			if (matchesBeanName(beanName, dependencyName)) {
				return beanName;
			}
		}
	}
	// Step 2b: match bean name against qualifier-suggested name
	String suggestedName = getAutowireCandidateResolver().getSuggestedName(descriptor);
	if (suggestedName != null) {
		for (String beanName : candidates.keySet()) {
			if (matchesBeanName(beanName, suggestedName)) {
				return beanName;
			}
		}
	}
	// Step 3: check highest priority candidate
	String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
	if (priorityCandidate != null) {
		return priorityCandidate;
	}
	// Step 4: pick unique default-candidate
	String defaultCandidate = determineDefaultCandidate(candidates);
	if (defaultCandidate != null) {
		return defaultCandidate;
	}
	// Step 5: pick directly registered dependency
	for (Map.Entry<String, Object> entry : candidates.entrySet()) {
		Object beanInstance = entry.getValue();
		if (beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) {
			return entry.getKey();
		}
	}
	return null;
}

这是"多个 bean 都匹配时选谁"的完整优先级链,顺序非常重要:

  1. @Primary :第一个判断。determinePrimaryCandidate(第 2092-2128 行)先找所有标了 primary 的候选;如果找到两个 primary,且它们都是本地定义(candidateLocal == primaryLocal),直接抛 NoUniqueBeanDefinitionException------两个 primary 是配置错误。如果一个本地一个父工厂,选本地的(本地覆盖祖先)。找不到 primary 时,第二遍找"非 fallback"的唯一候选(isFallback,@Fallback 注解)。
  2. 按注入点声明名匹配 :字段名/参数名等于 bean 名。比如候选是 userService 和 userServiceV2,注入点字段叫 userService,就选前者。
  3. 按 qualifier 建议名匹配 :getSuggestedName(descriptor)------@Qualifier("xxx") 里的值。
  4. @Priority :determineHighestPriorityCandidate,基于 jakarta.annotation.Priority,数值越小优先级越高。
  5. 默认候选 :determineDefaultCandidate------只有一个"非默认"候选时选它。
  6. 内建可注入对象 :最后才考虑 resolvableDependencies 里的对象。

这个顺序和网上很多教程说的"@Primary 优先于字段名"一致,但要注意:字段名匹配排在 @Priority 之前 。也就是说,如果你有两个候选都没标 primary,但其中一个名字恰好等于字段名,那名字匹配赢,@Priority 根本不参与。

10.4 resolvableDependencies:内建可注入对象

AbstractApplicationContext.prepareBeanFactory(spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java 第 747-750 行)注册了四个:

java 复制代码
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

而 Environment 不在这四个里面 ------它不是通过 registerResolvableDependency 注册的,而是作为一个普通 singleton bean 注册,同时配合 ignoreDependencyInterface(EnvironmentAware.class)(第 737 行)让 EnvironmentAware 回调走 BeanPostProcessor 路径。这个精确差别值得记住:BeanFactory/ResourceLoader/ApplicationEventPublisher/ApplicationContext 是"特殊可解析依赖"(不经过候选筛选),Environment 是"普通 bean"。

registerResolvableDependency 的实现(DefaultListableBeanFactory 第 887-897 行)有个校验:autowiredValue 要么是 ObjectFactory,要么必须是 dependencyType 的实例,否则抛 IllegalArgumentException。

10.5 AutowireCandidateResolver:策略扩展点

接口 support/AutowireCandidateResolver.java 前面已经读过,它定义了一组 default 方法:isAutowireCandidate、isRequired、hasQualifier、getSuggestedName、getSuggestedValue、getLazyResolutionProxyIfNecessary。

默认实现 SimpleAutowireCandidateResolver.INSTANCE 只做最朴素的判断。到了 web/注解环境,GenericTypeAwareAutowireCandidateResolver(加泛型匹配)→ QualifierAnnotationAutowireCandidateResolver(加 @Qualifier/@Value)→ ContextAnnotationAutowireCandidateResolver(在 spring-context 的 context/annotation/ 包下,加 @Lazy 代理)。最后这个实现会在 @Lazy 注入点上生成一个延迟解析代理,调用时才真正去容器里取 bean------这就是为什么 @Lazy 能打破循环依赖。

本章聚焦工厂本身,这层 resolver 的具体扩展留给注解配置章节。

十一、源码剖析(九):DependencyDescriptor、按类型 getBean 与类型缓存

前面讲 doResolveDependency 时,有几个配角没展开。这一节把它们补深,让依赖解析这条链路完整闭合。

15.1 DependencyDescriptor 与 InjectionPoint

InjectionPoint(路径 spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java)是注入点的基类,封装"这个待注入位置是一个字段还是一个方法参数"。它持有一个 AnnotatedElement(被注解的元素)和一个 MethodParameter(如果是方法/构造参数)。

DependencyDescriptor extends InjectionPoint(路径 config/DependencyDescriptor.java)在它之上加了注入语义:

  • getDependencyType():依赖的实际类型(字段类型,或方法参数类型)。
  • getResolvableType():带泛型信息的类型------这是为什么 Map<String, UserService> 这种泛型能被正确解析。
  • isRequired():是否必需(@Autowired(required=true))。
  • resolveCandidate(String beanName, Class<?> beanType, BeanFactory beanFactory):真正去容器里把这个候选 bean 取出来。
  • resolveShortcut(BeanFactory):短路钩子。
  • forFallbackMatch():返回一个放宽匹配规则的副本(泛型擦除场景用)。
  • usesStandardBeanLookup():7.0 的新方法,判断"这个注入点是否走标准按名查找"------前面 doResolveDependency Step 3 用它决定要不要试字段名匹配。

为什么需要这么一个描述符对象,而不是直接传 Field/MethodParameter?因为注入点可能是字段、构造参数、方法参数三种形态,它们的反射 API 不一样。DependencyDescriptor 把它们统一成"我要一个什么类型、叫什么名、必需与否"的抽象,后面 doResolveDependency 的逻辑就不必关心自己面对的是字段还是参数。

15.2 getBean(Class) 的完整路径

getBean(Class<T>) 不走 doGetBean,它走的是 DefaultListableBeanFactory 自己的 resolveBean 路线。第 380-389 行:

java 复制代码
@SuppressWarnings("unchecked")
@Override
public <T> T getBean(Class<T> requiredType, @Nullable Object @Nullable ... args) throws BeansException {
	Assert.notNull(requiredType, "Required type must not be null");
	Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
	if (resolved == null) {
		throw new NoSuchBeanDefinitionException(requiredType);
	}
	return (T) resolved;
}

resolveBean 内部调 resolveNamedBean(第 1569 行起)。它的逻辑是:

  1. getBeanNamesForType(requiredType) 列出所有匹配的 bean 名。
  2. 候选为空 → 返回 null(抛 NoSuchBeanDefinitionException)。
  3. 候选只有一个 → 直接 resolveNamedBean(candidateNames[0], requiredType, args) 走 doGetBean。
  4. 候选多个 → 逐个判断:是不是 primary、名字是否匹配参数......最终选出一个;选不出来就抛 NoUniqueBeanDefinitionException。

注意这个路径和 doResolveDependency 的候选仲裁顺序不完全相同 ------getBean(Class) 是给人用的"手动按类型取",它的仲裁更简单(primary + 唯一 + 名字匹配),而 @Autowired 自动注入走的是 doResolveDependency 那条更复杂的链(还要处理集合、@Value、@Lazy 代理)。理解这个差异,能解释一个现象:为什么 @Autowired Foo foo 能正常注入,但 getBean(Foo.class) 却报 NoUniqueBeanDefinitionException------两者用的仲裁代码不完全一样。

15.3 getBeanNamesForType 与按类型缓存

getBeanNamesForType(第 602 行起)是 findAutowireCandidates 和 getBeansOfType 的公共基础。它的性能关键是那两个缓存 Map:allBeanNamesByType 和 singletonBeanNamesByType。

逻辑大致是:

  • 如果配置已冻结(configurationFrozen=true),先查 allBeanNamesByType.get(type),命中直接返回------不必遍历全表做 isTypeMatch。
  • 未命中则遍历 beanDefinitionNames,对每个 bean 名调 isTypeMatch(beanName, type, allowFactoryBeanInit),收集匹配的名字,结果写进缓存。
  • getBeansOfType(第 727-740 行)更简单:先 getBeanNamesForType 拿名字数组,再对每个名字 getBean 取实例,塞进 Map。

这解释了 freezeConfiguration() 的价值:冻结之后,按类型查找从"每次 O(n) 遍历 + 类型匹配"变成"O(1) 查缓存"。容器启动完成、单例都造好后,调一次 freezeConfiguration() 是有性能收益的------ApplicationContext.refresh() 在 finishBeanFactoryInitialization 之后会自动冻结。

15.4 ConfigurableBeanFactory 的其余配置面

补几个前面没展开但日常会用到的配置项:

  • registerScope(String scopeName, Scope scope) :注册自定义作用域。Scope 接口(config/Scope.java)只有四个方法:get(name, objectFactory)、remove(name)、registerDestructionCallback(name, callback)、getConversationId()。web 环境的 RequestScope/SessionScope 就实现了它------get 方法从 HttpServletRequest/HttpSession 属性里取 bean,取不到就用 objectFactory.getObject() 造一个塞回去。
  • setTypeConverter(TypeConverter) :替换默认类型转换器。doGetBean 末尾 adaptBeanInstance、doResolveDependency 里 @Value 转换都用它。
  • addBeanPostProcessor(BeanPostProcessor) :加一个后置处理器。注意它有一个隐含约束------必须在任何 bean 实例化之前加 ,因为已经在创建中的 bean 不会回头应用新的 processor。AbstractBeanFactory 里有个 beanPostProcessors List 和一个 beanPostProcessorCache(按类型分组的缓存),加 processor 时会重建缓存。
  • resolveEmbeddedValue(String) :解析 ${...} 占位符。背后是 EmbeddedValueResolver(config/EmbeddedValueResolver.java),最终委托给 PropertySourcesPlaceholderConfigurer。
  • setTempClassLoader:设置临时类加载器。组件扫描时用 ASM 读注解元数据,不真正加载类;等到真正需要实例化时才切回主 ClassLoader。这就是为什么扫描阶段即使类路径有问题也不会立刻报错。

15.5 ObjectProvider 的实现本质

ObjectProvider(路径 factory/ObjectProvider.java)继承了 ObjectFactory<T> 和 Iterable<T>。它的方法:getIfAvailable()、getIfUnique()、ifAvailable(Consumer)、iterator()、stream()、orderedStream()。

在 DefaultListableBeanFactory 里,getBeanProvider 返回的是一个 BeanProvider(内部类,在 AbstractBeanFactory 或 DefaultListableBeanFactory 里),它持有"beanFactory 引用 + requiredType + allowEagerInit",每次调 getObject() 时才真正走 getBean(requiredType)。这就是"延迟"的本质------provider 本身不持有 bean,只持有"怎么去找"的上下文。

@Lazy 注入能打破循环依赖,靠的也是类似机制:ContextAnnotationAutowireCandidateResolver.getLazyResolutionProxyIfNecessary 生成一个代理对象注入字段,代理内部持有 DependencyDescriptor 和 beanFactory,第一次调方法时才真正 resolveDependency------此时双方都已初始化完成,不再循环。

15.6 一个被忽略的细节:MarkBeanAsCreated 与并发

doGetBean 里那行 markBeanAsCreated(beanName),除了标记"开始创建",还把 bean 名加进 alreadyCreated 集合。这个集合在两个地方被用:

  1. hasBeanCreationStarted() 判断"启动期是否结束"------前面 registerBeanDefinition 的写时复制分支就靠它。
  2. isSingletonCurrentlyInCreation 系列并发判断的辅助信号。

7.0 的并行预实例化之所以能安全地"多个线程同时准备造 bean",靠的就是这个集合配合 singletonsCurrentlyInCreation(一个 Set<String>,在 DefaultSingletonBeanRegistry 第 101 行)。当后台线程 A 开始造 beanX,它把 beanX 加进 singletonsCurrentlyInCreation;主线程随后走到 beanX,instantiateSingleton 发现它正在被造,抛 BeanCurrentlyInCreationException,主线程 catch 后跳过------这就是 preInstantiateSingleton 第 1193-1196 行那段宽容处理的由来。

十二、运行机制:一次 getBean("userService") 的四层落点

前面九节都是"拆零件",这一节把它们装回去,走一遍真实的 getBean("userService")。我们用"接口 → 实现 → 数据结构 → 缓存"四层视角,看每一步落在哪。

11.1 调用栈全貌

假设一个标准 Spring Boot 应用,内部是 AnnotationConfigApplicationContext,它持有的 beanFactory 就是一个 DefaultListableBeanFactory。现在业务代码调 context.getBean("userService"):

scss 复制代码
业务代码
  └─ ApplicationContext.getBean("userService")          [spring-context]
       └─ AbstractApplicationContext 委托给 beanFactory
            └─ AbstractBeanFactory.getBean(String)      [spring-beans/AbstractBeanFactory.java:195]
                 └─ doGetBean("userService", null, null, false)   [:236]

第一层:接口 。业务代码拿到的引用类型是 ApplicationContext(它 extends ListableBeanFactory 等),但它自己不存 bean,全部委托给内部的 beanFactory。

11.2 名字解析:transformedBeanName

scss 复制代码
doGetBean
  └─ transformedBeanName("userService")
       ├─ BeanFactoryUtils.transformedBeanName(...)   剥 '&' 前缀(此处无)
       └─ canonicalName("userService")                查别名表,一路追到规范名

第二层:实现 。canonicalName 在 SimpleAliasRegistry(core/SimpleAliasRegistry.java)里,别名表是一个 Map<String,String> aliasMap。假设 "userService" 不是别名,原样返回。

第三层:数据结构 。aliasMap。

11.3 单例快路径

scss 复制代码
doGetBean
  └─ getSingleton("userService")               [DefaultSingletonBeanRegistry:208]
       ├─ singletonObjects.get("userService")  一级缓存
       ├─ earlySingletonObjects.get(...)       二级(一级没命中才查)
       └─ singletonFactories.get(...)         三级(二级没命中 + 正在创建才查)

第三层:数据结构。三个 ConcurrentHashMap。这是热路径------第一次 getBean 时这里返回 null(bean 还没造),之后每次都命中一级缓存。

11.4 创建路径(假设是第一次)

快路径没命中,进入 else 分支:

scss 复制代码
isPrototypeCurrentlyInCreation("userService")  → false(不是 prototype)
getParentBeanFactory() != null && !containsBeanDefinition("userService")
   → containsBeanDefinition 返回 true(本地有定义),不回退父工厂
markBeanAsCreated("userService")
getMergedLocalBeanDefinition("userService")
   └─ mergedBeanDefinitions.get("userService")       第四层:合并定义缓存
        (未命中)→ getMergedBeanDefinition(beanName, bd)
                    ├─ synchronized(mergedBeanDefinitions)
                    ├─ 无 parentName → new RootBeanDefinition(bd)
                    ├─ 默认 scope=singleton
                    └─ cacheMergedBeanDefinition → mergedBeanDefinitions.put(...)
checkMergedBeanDefinition → 非抽象,放行
dependsOn → null,跳过
mbd.isSingleton() → true
getSingleton("userService", () -> createBean(...))
   └─ [DefaultSingletonBeanRegistry:255] 三级缓存 + 加锁 + 执行工厂 lambda
        └─ createBean("userService", mbd, null)   [AbstractAutowireCapableBeanFactory]
             └─ doCreateBean → 实例化 → populateBean → initializeBean
                  (细节属生命周期章,此处不展开)

第四层:缓存 。mergedBeanDefinitions 这次合并的结果被缓存下来,下次 getBean 同一个 bean 直接命中。

11.5 FactoryBean 后处理

java 复制代码
getObjectForBeanInstance(sharedInstance, null, "userService", "userService", mbd)
  ├─ isFactoryDereference("userService") → false(无 &)
  ├─ sharedInstance instanceof FactoryBean → 假设不是,直接返回
  └─ (若是 FactoryBean,则查 FactoryBean 产物缓存 → getObjectFromFactoryBean)

11.6 类型适配与返回

csharp 复制代码
adaptBeanInstance("userService", bean, null)
  └─ requiredType 为 null,不做转换,直接返回

11.7 如果这次 getBean 是为了 @Autowired 注入

假设字段是 @Autowired UserService userService;,走的不是 getBean(String),而是 resolveDependency:

java 复制代码
DefaultListableBeanFactory.resolveDependency(descriptor, "orderService", ...)
  └─ doResolveDependency
       ├─ descriptor.resolveShortcut → null
       ├─ getAutowireCandidateResolver().getSuggestedValue → null(不是 @Value)
       ├─ usesStandardBeanLookup → 字段名 "userService",containsBean 命中
       │    → isTypeMatch / isAutowireCandidate / !isFallback / !hasPrimaryConflict / !isSelfReference
       │    → resolveBean("userService", ...) → 回到上面 11.2-11.6 的 getBean 流程
       └─ (字段名不匹配时才走 findAutowireCandidates + determineAutowireCandidate)

11.8 四层视角总结

把这次调用涉及的"表"按层归类:

层 数据结构 所在类 这次访问做了什么
别名表 aliasMap SimpleAliasRegistry canonicalName 追规范名
定义表 beanDefinitionMap / beanDefinitionNames DefaultListableBeanFactory containsBeanDefinition 确认本地有
合并定义缓存 mergedBeanDefinitions AbstractBeanFactory 第一次合并 RootBeanDefinition 并缓存
单例三级缓存 singletonObjects/earlySingletonObjects/singletonFactories DefaultSingletonBeanRegistry 第一次未命中触发创建,之后命中一级
FactoryBean 产物缓存 factoryBeanObjectCache FactoryBeanRegistrySupport 普通 bean 不涉及

这就是"接口 → 实现 → 数据结构 → 缓存"四层的完整落点。新手最容易迷糊的地方是:为什么 getBean 要查这么多表? 答案是每一层都在解决不同的问题------别名表解决"名字别名",定义表解决"配方存不存在",合并缓存解决"父子定义拍平",单例缓存解决"复用还是新建"。

11.9 单例注册表与定义注册表如何配合

最后把这两张表的配合关系讲透:

  • 启动期 :BeanDefinitionReader(XML 读取器)或 ComponentScan 把所有配方写进 beanDefinitionMap/beanDefinitionNames。此时 singletonObjects 是空的。
  • 预实例化期 :preInstantiateSingletons 遍历 beanDefinitionNames,对每个非抽象 singleton 调 getBean,触发 createBean,成品被 addSingleton 写进 singletonObjects。
  • 运行期 :业务 getBean 先查 singletonObjects,命中直接返回;未命中才回 beanDefinitionMap 找配方现造。

两张表的生命周期是:定义表先满,单例表后满;定义表是"图纸仓库",单例表是"成品仓库" 。registerSingleton 是一个例外入口------它跳过定义表,直接往单例表塞现成对象(比如 Spring 把 Environment 这个对象直接 registerSingleton 进去,不建配方)。

这也解释了为什么 containsBeanDefinition 和 containsBean 的行为不同:前者只看定义表,后者两张表都看。

十三、扩展用法与最佳实践

12.1 脱离 ApplicationContext,直接用 DefaultListableBeanFactory

DefaultListableBeanFactory 是一个可以独立使用的 IoC 容器,不依赖 spring-context。下面是一个最小可运行例子(注意:纯演示,不引入任何 XML/注解扫描):

java 复制代码
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

// 1. 编程式注册一个 bean 定义
beanFactory.registerBeanDefinition("userService",
		new RootBeanDefinition(UserService.class));

// 2. 再注册一个,并通过构造参数引用 userService
RootBeanDefinition orderBd = new RootBeanDefinition(OrderService.class);
orderBd.getConstructorArgumentValues()
		.addIndexedArgumentValue(0, new RuntimeBeanReference("userService"));
beanFactory.registerBeanDefinition("orderService", orderBd);

// 3. 触发预实例化(可选;不调的话就是懒加载)
beanFactory.preInstantiateSingletons();

// 4. 取对象
UserService userService = beanFactory.getBean("userService", UserService.class);

7.0 还提供了更简洁的 registerBean(在 GenericApplicationContext/DefaultListableBeanFactory 的 BeanDefinitionRegistry 默认方法里,基于 InstanceSupplier):

java 复制代码
beanFactory.registerBean("userService", UserService.class, () -> new UserService());

InstanceSupplier(路径 support/InstanceSupplier.java)是 5.0 引入的函数式接口,允许用 lambda 直接指定创建逻辑,跳过反射实例化。

手动注册单例用 registerSingleton(继承自 SingletonBeanRegistry):

java 复制代码
// 不建配方,直接把现成对象塞进单例表
beanFactory.registerSingleton("environment", envObject);

它和 registerBeanDefinition 的区别前面讲过:前者没有配方、不出现在 beanDefinitionNames、不参与预实例化,只出现在单例表和 getBeanNamesForType 结果里。

12.2 编程式使用的注意点

  1. 必须自己加 BeanPostProcessor 。裸 DefaultListableBeanFactory 不会有任何 @Autowired 处理能力------那是 AutowiredAnnotationBeanPostProcessor 干的事,而它是 spring-context 里的类。如果你希望 @Autowired 生效,得自己 beanFactory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor())。
  2. 必须自己调 preInstantiateSingletons 。裸工厂不会自动启动。这和 ApplicationContext.refresh() 自动调它不同。
  3. 并发与热更新 。registerBeanDefinition 在 hasBeanCreationStarted() 之后走锁内写时复制,是线程安全的,但它会触发 resetBeanDefinition → destroySingleton------意味着启动后再注册同名定义,会把已经造好的单例销毁。在生产环境做"热更新 bean"是危险操作:持有旧引用的地方还在用旧对象,新 getBean 拿到的是新对象,两份状态可能不一致。
  4. allowBeanDefinitionOverriding 默认 true 。裸工厂里同名后注册的定义会静默覆盖先注册的。如果你的场景要求严格(比如插件系统),建议 beanFactory.setAllowBeanDefinitionOverriding(false),让覆盖直接抛异常。

12.3 BeanFactory 与 ApplicationContext 的选择

这两个不是二选一的竞争关系,而是包含关系 :ApplicationContext 内部持有一个 DefaultListableBeanFactory,在它之上加了一层。差异点:

能力 BeanFactory(裸 DefaultListableBeanFactory) ApplicationContext
bean 实例化/依赖注入 有 有(委托给内部 factory)
注解配置类注册/组件扫描 需手动加 reader 开箱即用
BeanPostProcessor 自动注册 否 是
国际化(MessageSource) 无 有
事件发布(ApplicationEventPublisher) 无 有
环境抽象(Environment/Profile) 无 有
启动时预实例化单例 需手动调 refresh() 自动调
资源加载(ResourceLoader) 无 有

选择建议:

  • 库/中间件作者 :如果你的组件需要被宿主应用集成,依赖 BeanFactory 接口就够了------这样你的库既能在 ApplicationContext 里跑,也能在裸 BeanFactory 里跑,耦合最小。
  • 业务应用:直接用 ApplicationContext。裸 BeanFactory 的那些"手动活"(加 post-processor、调预实例化)在 ApplicationContext 里全自动。
  • 测试 :可以用 StaticListableBeanFactory(support/StaticListableBeanFactory.java)------它不创建 bean,只持有你手动塞进去的实例,写单测时mock 依赖特别方便。

12.4 getBeanProvider 的正确用法

getBeanProvider(Class) 返回的 ObjectProvider 是延迟查找句柄,比 getBean(Class) 更宽容:

java 复制代码
ObjectProvider<UserService> provider = beanFactory.getBeanProvider(UserService.class);
UserService s = provider.getIfAvailable();   // 没有就返回 null,不抛异常
UserService s2 = provider.getIfUnique();     // 唯一才返回,多个返回 null
provider.ifAvailable(s -> s.doWork());       // 有就消费

注入场景里,@Autowired ObjectProvider<UserService> 比 @Autowired UserService 更适合"可选依赖"或"需要延迟初始化"的场景。7.0 新增的 getBeanProvider(ParameterizedTypeReference) 让你能精确取带泛型的 bean:

java 复制代码
ObjectProvider<List<Foo>> p =
		beanFactory.getBeanProvider(new ParameterizedTypeReference<List<Foo>>() {});

12.5 线程安全要点总结

把本章涉及的并发模型收拢成几条:

  • 定义表写时复制 :启动后新增/删除 beanDefinitionNames 在 synchronized(beanDefinitionMap) 内做整表替换,volatile 保证引用可见。
  • 单例表 ReentrantLock :singletonLock 保护三级缓存的写路径,读路径先无锁查一级缓存。
  • 合并定义表 synchronized :getMergedBeanDefinition 在 synchronized(mergedBeanDefinitions) 内 double-check。
  • 7.0 并行预实例化 :靠 bootstrapExecutor + PreInstantiation ThreadLocal + 三级缓存挂 future 实现"宽松并行",用 tryLock 快速失败避免跨线程提前推断。
  • beanDefinitionMap 本身是 ConcurrentHashMap:单个 put/get 是线程安全的,但"读名字列表"这种复合操作仍需外部同步------这就是为什么要整表复制。

理解这些,你才能在自定义 BeanFactoryPostProcessor 或启动后动态注册 bean 时,知道哪些操作安全、哪些会踩坑。

附录:AbstractBeanFactory 的其余关键字段与 FactoryBeanRegistrySupport

为了让"工厂内部到底存了什么"这张地图更完整,补读 AbstractBeanFactory 的两个字段(第 143、158 行):

java 复制代码
private final Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>(4);
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
  • customEditors :自定义 PropertyEditor 注册表。ConfigurableBeanFactory.registerCustomEditor 往里加。属性填充时遇到字符串到特殊类型的转换(比如把 "2024-01-01" 转成 Date),就从这里查 editor。
  • scopes :作用域注册表。前面 doGetBean 的自定义 scope 分支用的就是它。默认只注册了 singleton 和 prototype 两个内建 scope;web 环境的 request/session/globalSession 由 AbstractApplicationContext 子类(如 XmlWebApplicationContext)启动时 registerScope 进去。

另外 AbstractBeanFactory 还维护着 beanPostProcessors(一个 List<BeanPostProcessor>)和一个按类型分组的 beanPostProcessorCache(BeanPostProcessorCache record,把 before/after/destructionAware/mergedDefinition 四类 processor 各缓存成数组)。这个缓存是 6.0 引入的优化------applyBeanPostProcessorsBeforeInitialization 等热路径不再每次遍历全 List 做 instanceof 判断,直接取缓存好的子数组。

FactoryBeanRegistrySupport 的产物缓存

getObjectForBeanInstance 里那个 getCachedObjectForFactoryBean,实现在 FactoryBeanRegistrySupport(路径 support/FactoryBeanRegistrySupport.java)。它维护一个 Map<String, Object> factoryBeanObjectCache:key 是 bean 名,value 是 FactoryBean 生产出来的单例产物。singleton 类型的 FactoryBean 产物第一次调 getObject() 后就缓存进来,之后 getBean("xxx") 直接命中缓存,不会重复调 getObject()。prototype 类型的 FactoryBean 不缓存,每次都新建。

这套设计和单例表是平行的:单例表存"FactoryBean 本身",factoryBeanObjectCache 存"FactoryBean 生产的对象" 。两级缓存各司其职,getBean("&xxx") 查前者,getBean("xxx") 查后者。

BeanDefinitionReaderUtils:注册动作的公共入口

最后补一个常被忽略的环节。XML/注解 reader 解析出一个定义后,并不直接调 beanFactory.registerBeanDefinition,而是走 BeanDefinitionReaderUtils.registerBeanDefinition(support/BeanDefinitionReaderUtils.java)。它额外做了两件事:处理 BeanDefinitionHolder 里带的别名(registerAlias),以及统一处理"定义名 + 别名"的注册。这也是为什么 registerBeanDefinition 里要专门处理"撞别名"分支------reader 可能在同一个名字上既注册定义又注册别名,顺序错乱就冲突。

把这几个配角补齐,IoC 工厂的内部结构就完全闭合了:接口族定义行为,DefaultListableBeanFactory 持有四表,AbstractBeanFactory 提供模板方法 doGetBean,FactoryBeanRegistrySupport 兜住 FactoryBean 产物缓存,BeanDefinitionReaderUtils 统一注册入口。本章的内容到这里自足闭环。

十四、常见问题与坑

13.1 getBean("xxx") 与 getBean("&xxx") 的区别

这是 FactoryBean 协议最容易踩的坑。假设容器里注册了:

java 复制代码
class MyFactoryBean implements FactoryBean<MyBean> { ... }
beanFactory.registerBeanDefinition("myBean", new RootBeanDefinition(MyFactoryBean.class));

那么:

  • getBean("myBean") → 调 MyFactoryBean.getObject(),返回一个 MyBean 实例。
  • getBean("&myBean") → 返回 MyFactoryBean 本身。

源码落点在 getObjectForBeanInstance(AbstractBeanFactory.java:1845):BeanFactoryUtils.isFactoryDereference(name) 判断原始 name 是否带 &。注意 transformedBeanName 已经把 & 剥掉了,所以判断必须用原始 name ,而不是剥过的 beanName。一个常见错误是以为 & 是别名------不是,它是一个前缀协议,在 doGetBean 入口处就被识别。

如果 getBean("&xxx") 但 xxx 实际不是 FactoryBean,会抛 BeanIsNotAFactoryException(第 1850 行)。

13.2 getBeansOfType 与 List 注入的顺序

@Autowired List<Handler> handlers; 注入的顺序由什么决定?答案在 resolveMultipleBeanCollection(第 1863-1887 行):

java 复制代码
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
		new MultiElementDescriptor(descriptor));
// ......
Object result = converter.convertIfNecessary(matchingBeans.values(), descriptor.getDependencyType());
if (result instanceof List<?> list && list.size() > 1) {
	Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
	if (comparator != null) {
		list.sort(comparator);
	}
}

顺序规则是:

  1. 先按 beanDefinitionNames 的注册顺序 (findAutowireCandidates 用的 beanNamesForTypeIncludingAncestors 返回的是定义顺序)。
  2. 如果配了 dependencyComparator (ApplicationContext 里是 AnnotationAwareOrderComparator),再按 @Order/Ordered/@Priority 排序。

所以:不写 @Order 时,顺序就是 bean 注册顺序(XML 是文档顺序,组件扫描是类路径扫描顺序------后者不稳定 ,依赖文件系统遍历顺序)。想要确定顺序,必须显式标 @Order。

13.3 重复 bean 名覆盖

两个同名 bean 定义注册进来,谁赢?看 registerBeanDefinition(第 1268-1277 行):

  • allowBeanDefinitionOverriding 默认 true:后注册的赢,旧的被静默替换(只打日志)。
  • 设为 false:直接抛 BeanDefinitionOverrideException。

没有"按定义内容新旧判断"的逻辑------7.0.9 里没有 lastModified 时间戳比较 。谁后注册谁赢,这是唯一规则。这也是 Spring Boot 2.1 开始把 allow-bean-definition-overriding 默认改成 false 的原因:防止开发者意外覆盖自动配置的 bean。

覆盖后会发生 resetBeanDefinition:合并定义缓存标记 stale、旧单例被 destroySingleton。也就是说,如果同名 singleton 已经造好了,再覆盖定义,旧实例会被销毁,下次 getBean 按新定义重建。

13.4 NoUniqueBeanDefinitionException 与 @Primary/@Priority 的解析顺序

多个候选 bean 匹配类型时,determineAutowireCandidate 的仲裁顺序(前面贴过完整源码)是:

  1. @Primary(多个 primary 直接报错);
  2. 字段名/参数名匹配;
  3. qualifier 建议名;
  4. @Priority(数值小优先);
  5. 默认候选(唯一非 fallback);
  6. 内建可注入对象。

一个容易搞混的点:字段名匹配排在 @Priority 前面 。假设两个 bean userServiceV1、userServiceV2 都标了 @Priority(1)/@Priority(2),但注入字段叫 userServiceV1,那会选 userServiceV1------因为第 2 步名字匹配就命中返回了,根本走不到第 4 步。

另一个坑:@Primary 和 @Priority 不在一个层级。@Primary 是"这个类型默认选我",@Priority 是"多个候选时排个序"。如果有两个候选都标了 @Primary,容器不做优先级仲裁,直接抛 NoUniqueBeanDefinitionException(determinePrimaryCandidate 第 2102-2106 行:两个本地 primary 直接抛异常)。

13.5 registerBeanDefinition 的"lastModified 语义"

任务书里提到这个点,这里明确:7.0.9 的 registerBeanDefinition 没有任何基于时间戳的 lastModified 比较 。同名覆盖的判断只看 isBeanDefinitionOverridable() 布尔开关。网上一些旧资料提到的"只有新定义比旧定义新才覆盖"的语义,在 spring-beans 本身并不存在------那是某些上层框架(如 Spring Boot 的条件评估)自己加的逻辑。以仓库代码为准:覆盖就是"后写覆盖先写"。

13.6 isTypeMatch 为什么可能触发 FactoryBean 初始化

getBeanNamesForType(SomeType.class) 看似只是"查类型",但它可能被迫初始化 FactoryBean。原因:要知道 FactoryBean 生产的对象类型,理想情况读 getObjectType();如果该方法实现得不理想,只能真的调一次 getObject()。这就是 allowEagerInit 参数存在的意义。性能敏感场景下,批量 by-type 查询要意识到这个副作用------它可能触发数据库连接池初始化之类的重量级操作。

13.7 为什么 containsBean 为 true 但 getBean 抛 NoSuchBeanDefinitionException

containsBean 的注释(BeanFactory.java:285-299)专门警告:它返回 true 不代表 getBean 一定能拿到实例。因为它只判断"有没有这个名字的定义或单例",不判断这个定义是不是 abstract、是不是 lazy。一个 abstract 的父模板定义,containsBean 返回 true,但 getBean 会抛 BeanIsAbstractException(checkMergedBeanDefinition,AbstractBeanFactory.java:1513)。

十五、本章小结

本章从 BeanFactory 接口族出发,逐层深入到 DefaultListableBeanFactory 的数据结构、registerBeanDefinition 的注册事务、preInstantiateSingletons 的 7.0 并行模型、doGetBean 的完整取对象流程、FactoryBean 的 & 协议、BeanDefinition 的元模型与合并机制、以及 doResolveDependency 的候选仲裁链。核心结论:

  1. 接口按用途拆分 :根接口管"取/查",ListableBeanFactory 管"枚举",Configurable* 管"配置",AutowireCapableBeanFactory 管"外部对象生命周期",最终聚合在 ConfigurableListableBeanFactory。
  2. 四张表 :定义表(beanDefinitionMap+beanDefinitionNames)、单例三级缓存(singletonObjects/earlySingletonObjects/singletonFactories)、按类型索引缓存、内建可注入对象表(resolvableDependencies)。
  3. 7.0 新特性 :并行预实例化(bootstrapExecutor+isBackgroundInit+PreInstantiation ThreadLocal)、getBeanProvider(ParameterizedTypeReference)、FACTORY_BEAN_PREFIX_CHAR char 常量。
  4. 候选仲裁顺序:@Primary → 字段名 → qualifier 名 → @Priority → 默认候选 → 内建对象。
  5. 合并定义 :注册时的多种 BeanDefinition 统一拍平成 RootBeanDefinition 缓存在 mergedBeanDefinitions,stale 标志控制失效。

本章关键类清单

类名 模块与相对路径 职责一句话
BeanFactory spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java IoC 容器根接口:getBean 家族与查方法
HierarchicalBeanFactory spring-beans/.../factory/HierarchicalBeanFactory.java 父子层次:getParentBeanFactory、containsLocalBean
ListableBeanFactory spring-beans/.../factory/ListableBeanFactory.java 可枚举:批量按名/按类型列举
AutowireCapableBeanFactory spring-beans/.../factory/config/AutowireCapableBeanFactory.java 外部对象执行完整 bean 生命周期
ConfigurableBeanFactory spring-beans/.../factory/config/ConfigurableBeanFactory.java 工厂配置面:scope/类加载器/类型转换/BeanPostProcessor
ConfigurableListableBeanFactory spring-beans/.../factory/config/ConfigurableListableBeanFactory.java 聚合接口:freeze、preInstantiateSingletons、resolveDependency
DefaultListableBeanFactory spring-beans/.../factory/support/DefaultListableBeanFactory.java 全功能工厂:定义表+候选筛选+预实例化
AbstractBeanFactory spring-beans/.../factory/support/AbstractBeanFactory.java doGetBean 主流程、合并定义缓存、FactoryBean 后处理
AbstractAutowireCapableBeanFactory spring-beans/.../factory/support/AbstractAutowireCapableBeanFactory.java createBean/doCreateBean 骨架(生命周期细节见后续章节)
DefaultSingletonBeanRegistry spring-beans/.../factory/support/DefaultSingletonBeanRegistry.java 单例三级缓存、singletonLock、销毁注册
FactoryBeanRegistrySupport spring-beans/.../factory/support/FactoryBeanRegistrySupport.java FactoryBean 产物缓存
BeanDefinition spring-beans/.../factory/config/BeanDefinition.java bean 配方接口
AbstractBeanDefinition spring-beans/.../factory/support/AbstractBeanDefinition.java 配方通用骨架与 validate
GenericBeanDefinition spring-beans/.../factory/support/GenericBeanDefinition.java 通用配方,可带 parentName
RootBeanDefinition spring-beans/.../factory/support/RootBeanDefinition.java 合并终点,运行时解析缓存字段
ChildBeanDefinition spring-beans/.../factory/support/ChildBeanDefinition.java 必须依附父定义的子配方(7.0.9 未废弃)
AnnotatedGenericBeanDefinition spring-beans/.../factory/annotation/AnnotatedGenericBeanDefinition.java 带注解元数据的通用配方
ScannedGenericBeanDefinition spring-context/src/main/java/org/springframework/context/annotation/ScannedGenericBeanDefinition.java 组件扫描产物,ASM 读元数据
FactoryBean spring-beans/.../factory/FactoryBean.java 工厂 bean:getObject/getObjectType/isSingleton
SmartFactoryBean spring-beans/.../factory/SmartFactoryBean.java FactoryBean 扩展:isEagerInit/isPrototype
DependencyDescriptor spring-beans/.../factory/config/DependencyDescriptor.java 注入点描述符:类型、名字、是否必需
InjectionPoint spring-beans/.../factory/InjectionPoint.java 注入点基类(字段/方法参数)
AutowireCandidateResolver spring-beans/.../factory/support/AutowireCandidateResolver.java 候选判断策略接口
SimpleAutowireCandidateResolver spring-beans/.../factory/support/SimpleAutowireCandidateResolver.java 默认候选判断:只看 isAutowireCandidate 标志
ContextAnnotationAutowireCandidateResolver spring-context/src/main/java/org/springframework/context/annotation/ContextAnnotationAutowireCandidateResolver.java @Lazy 代理等上下文扩展
ObjectProvider spring-beans/.../factory/ObjectProvider.java 延迟查找句柄
BeanDefinitionRegistry spring-beans/.../factory/support/BeanDefinitionRegistry.java 定义注册表面接口
SingletonBeanRegistry spring-beans/.../factory/config/SingletonBeanRegistry.java 单例注册表面接口
相关推荐
tianbin9111 小时前
大模型流式输出异常排查,API 中转站层面的问题定位思路
后端·云计算
ydjs678899-bot1 小时前
如何在Java中打印pdf静默打印和打印对话框选择
java·python·pdf
Shaoshing1 小时前
存储引擎与架构
java·开发语言·架构
鶴哥只手遮天1 小时前
Spring Framework 7.0.9 深度剖析-01-整体架构与设计哲学
java·后端·开源
const_qiu1 小时前
第 2.5 章 2.5.6:前端登录、Token 无感续签与路由守卫
java·前端·人工智能·状态模式
m4Rk_1 小时前
【论文阅读】Agent 记忆机制(102):UMA——用多个未来问题端到端训练一份共享记忆
论文阅读·人工智能·学习·开源·github
devilnumber1 小时前
Java 代理模式(简单理解回顾)
java·开发语言·代理模式
GetcharZp1 小时前
不止是企业知识库:腾讯开源 WeKnora,让文档变成可问答、可执行的知识系统
后端
她的男孩1 小时前
Spring Boot 4 升级别只看编译绿:Forge Admin 在 Jackson、Tomcat JSON 限流、MP 包名与 Flowable 库上踩过的坑
java·spring boot·后端