bigywater.tech
Spring · 12

FactoryBean 与 ObjectProvider:延迟与可选依赖

FactoryBean 的产品什么时候创建?"&" 前缀是什么?ObjectProvider 的 getIfAvailable、getIfUnique、stream 各自在没有、一个、多个候选时怎么表现?它真的是“延迟”注入吗?这篇在 Spring Framework 7.0.9 上逐项验证。

2 条
singleton 产品的 FactoryBean:refresh 时创建的记录条数(只有 1 条,即工厂本体;isEagerInit 时是 2 条)
null / 异常
两个候选时 ObjectProvider.getIfUnique() 与 getIfAvailable() 的结果
[p2, p1]
p1 的 @Order 是 2、p2 是 1 时 orderedStream() 的顺序(stream() 是 [p1, p2])

FactoryBean

实测 · 本机执行输出
  --- refresh 完成时(single)
  1. 创建 FactoryBean 本体(singleton=true,getObjectType=Conn)
  --- getBean("conn") 两次之后
  1. 创建产品 Conn#1
  getBean("conn") 的类型 Conn;getBean("&conn") 的类型 ConnFactory;两次是同一个产品?true;getBeansOfType(Conn.class).size() = 1

工厂本体在 refresh 时创建,产品在第一次 getBean 时创建;"&名字" 取工厂本体。

isSingleton

实测 · 本机执行输出
  --- refresh 完成时(proto)
  1. 创建 FactoryBean 本体(singleton=false,getObjectType=Conn)
  --- getBean("conn") 两次之后
  1. 创建产品 Conn#1
  2. 创建产品 Conn#2
  getBean("conn") 的类型 Conn;getBean("&conn") 的类型 ConnFactory;两次是同一个产品?false;getBeansOfType(Conn.class).size() = 1

false 时每次 getBean 调用一次 getObject。

isEagerInit

实测 · 本机执行输出
  --- refresh 完成时(eager)
  1. 创建 FactoryBean 本体(singleton=true,getObjectType=Conn)
  2. 创建产品 Conn#1
  --- getBean("conn") 两次之后
  1. 创建产品 Conn#2
  2. 创建产品 Conn#3
  getBean("conn") 的类型 Conn;getBean("&conn") 的类型 EagerFactory;两次是同一个产品?false;getBeansOfType(Conn.class).size() = 1

SmartFactoryBean.isEagerInit 让产品也在 refresh 时创建。

SmartFactoryBean 不缓存

实测 · 本机执行输出
  --- refresh 完成时(eager2)
  1. 创建 FactoryBean 本体(Eager2:直接实现 SmartFactoryBean,isSingleton 与 isEagerInit 都写成 true)
  2. 创建产品 Conn#1
  --- getBean("conn") 两次之后
  1. 创建产品 Conn#2
  2. 创建产品 Conn#3
  getBean("conn") 的类型 Conn;getBean("&conn") 的类型 Eager2;两次是同一个产品?false;getBeansOfType(Conn.class).size() = 1

容器不缓存 SmartFactoryBean 的产品,每次 getBean 都新建(源码注释:可能返回多种类型,不缓存)。

类型为 null

实测 · 本机执行输出
  getObjectType() 返回 null:getBeanNamesForType(Conn.class) = [];getBean("conn") 照样能拿到 Conn;getBean(Conn.class) → NoSuchBeanDefinitionException

getObjectType 返回 null 时,按名字能取到,按类型取不到。

ObjectProvider

实测 · 本机执行输出
  (none)
    --- refresh 完成时(none)
    1. 构造 User(拿到了 ObjectProvider,还没有解析任何 Pay)
    getIfAvailable() → null;getIfUnique() → null;getObject() → NoSuchBeanDefinitionException;getIfAvailable(默认) → 默认
    stream() 的顺序 → [];orderedStream() 的顺序 → []
    --- 上面这些调用的过程中
  (one)
    --- refresh 完成时(one)
    1. 构造 User(拿到了 ObjectProvider,还没有解析任何 Pay)
    2. 构造 P1
    getIfAvailable() → p1;getIfUnique() → p1;getObject() → p1;getIfAvailable(默认) → p1
    stream() 的顺序 → [p1];orderedStream() 的顺序 → [p1]
    --- 上面这些调用的过程中
    --- 两次 lazy.getObject()
    1. 构造 Lazy1(被 ObjectProvider 引用的 prototype)
    2. 构造 Lazy1(被 ObjectProvider 引用的 prototype)
  (two)
    --- refresh 完成时(two)
    1. 构造 User(拿到了 ObjectProvider,还没有解析任何 Pay)
    2. 构造 P1
    3. 构造 P2
    getIfAvailable() → NoUniqueBeanDefinitionException;getIfUnique() → null;getObject() → NoUniqueBeanDefinitionException;getIfAvailable(默认) → NoUniqueBeanDefinitionException
    stream() 的顺序 → [p1, p2];orderedStream() 的顺序 → [p2, p1]
    --- 上面这些调用的过程中

没有、一个、多个候选时各取法的结果不同;它只推迟 prototype 和 @Lazy,不推迟 singleton。

分步演示

FactoryBean 与 ObjectProvider
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9;第四节引用了 spring-beans-7.0.9-sources.jar 里 FactoryBeanRegistrySupport 的一句源码注释作为解释
    • 没有验证:@Bean 方法返回 FactoryBean 时泛型类型推断;FactoryBean 产品的销毁回调;ObjectProvider.ifAvailable / ifUnique;jakarta.inject.Provider 与 ObjectFactory 的差别;AOT 对 FactoryBean 类型推断的限制
    京ICP备2026067502号-1京公网安备11010502063487号