bigywater.tech
Spring · 2

BeanDefinition:Bean 在被创建之前是什么

Bean 在被创建之前是什么?@Bean 方法、@Component、手写注册的定义有什么不同?创建之前能不能改?父子定义怎么合并?同名的 Bean 为什么在 Boot 里会报错?这篇直接读 BeanDefinition 的各个属性,在 Spring Framework 7.0.9 上逐项验证。

4 种
这篇读出的 BeanDefinition 类型:Generic、ScannedGeneric、ConfigurationClass、Root(合并后)
true / false
纯 Spring 与 SpringApplication 上下文里 allowBeanDefinitionOverriding 的默认值
2 个
一个 abstract 父定义加两个子定义时,容器里真正创建的 Greeter 实例数

定义的三种来源

实测 · 本机执行输出
  manual     定义类型=GenericBeanDefinition 类=Main$Greeter scope=(空=singleton) lazy=true factoryMethod=null 来源=null
  main.Scanned 定义类型=ScannedGenericBeanDefinition 类=Main$Scanned scope=singleton lazy=false factoryMethod=null 来源=Main$Scanned.class
  main.Conf  定义类型=ScannedGenericBeanDefinition 类=Main$Conf$$SpringCGLIB$$0 scope=singleton lazy=false factoryMethod=null 来源=Main$Conf.class
  viaBean    定义类型=ConfigurationClassBeanDefinition 类=null scope=(空=singleton) lazy=false factoryMethod=viaBean 来源=demo.Main$Conf
  创建之前 manual 的单例已创建吗?false(lazy=true);getBean 之后 Greeter(手写的定义)#48e 已创建=true
  viaBean 的实例:Greeter(来自 @Bean)#158;@Bean 的 initMethod 属性在定义里:init

手写、扫描、@Bean 方法各有不同的 BeanDefinition 类型;@Bean 的定义没有 beanClassName,只有 factoryMethod。

创建之前可以改

实测 · 本机执行输出
  BFPP 看到的定义:scope=singleton greeting=原值
  refresh 之后两次 getBean:Greeter(被 BFPP 改过)#489 与 Greeter(被 BFPP 改过)#385;同一个对象?false(定义里的 scope 被改成了 prototype)

BeanFactoryPostProcessor 在 Bean 创建前拿到定义,可以改属性值甚至 scope。

定义合并

实测 · 本机执行输出
  child 自己的定义:beanClassName=null initMethod=null 属性数=0
  child 合并后:类=Main$Greeter initMethod=init greeting=父定义的值;合并后的类型 RootBeanDefinition
  getBean(child)=Greeter(父定义的值)#b3c getBean(child2)=Greeter(子定义覆盖)#45c
  getBean(abstract 的 parentDef):BeanIsAbstractException
  容器里 Greeter 类型的 Bean 个数:2(abstract 的定义不会被创建)

子定义继承父定义的类、init-method 和属性,合并成 RootBeanDefinition;abstract 的父定义不会被创建。

同名覆盖

实测 · 本机执行输出
  纯 Spring Framework 的 DefaultListableBeanFactory 默认 isAllowBeanDefinitionOverriding = true
  allowBeanDefinitionOverriding=true:没有报错,getBean(dup)=Greeter(第二个)#710
  allowBeanDefinitionOverriding=false:BeanDefinitionOverrideException: Invalid bean definition with name 'dup' defined in null: Cannot register bean definition [Generic bean: class=demo.Main$Greeter; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null] for bean 'dup' since there is already [Generic bean: class=demo.Main$Greeter; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; fallback=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null] bound.
  SpringApplication 创建的上下文(AnnotationConfigApplicationContext)的 isAllowBeanDefinitionOverriding = false
  纯 Spring Framework 里后注册的定义悄悄覆盖前面的;SpringApplication 创建的上下文把它设成 false(上面最后一行是实际读出来的值)

纯 Spring 默认允许后者覆盖前者,SpringApplication 的上下文不允许,启动时就报错。

分步演示

BeanDefinition 到底是什么
本步的对象

    本文用到的工具与边界

    • 本机 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 的 AnnotationConfigApplicationContext;最后一个实验用 SpringApplication(WebApplicationType.NONE)读它创建的上下文的默认值
    • 没有验证:XML 配置的 BeanDefinition 读取;BeanDefinitionRegistryPostProcessor 的注册时机;@Primary、@DependsOn 在定义里的字段;AOT 时 BeanDefinition 被预先生成的情形;大量定义(上万个)对启动时间的影响
    京ICP备2026067502号-1京公网安备11010502063487号