依赖查找的来源

先理解一个问题:Spring 到哪里找 Bean?

当我们调用下面的方法时,Spring 需要根据 Bean 名称或类型找到一个对象:

1
2
applicationContext.getBean("user");
applicationContext.getBean(User.class);

这就是依赖查找。Spring 主要从 IoC 容器中已经登记的两类对象里查找:

  1. BeanDefinition 创建的 Bean:容器根据 BeanDefinition 描述,创建并管理 Bean 实例。
  2. 手动注册的单例对象:已经存在的普通 Java 对象,被直接注册到容器中。

简单来说,依赖查找的范围可以概括为:

先在 BeanDefinition 创建的 Bean 中查找,再在容器登记的单例对象中查找。

BeanDefinition 创建的 Bean

BeanDefinition 是 Bean 的“定义信息”,描述了 Bean 的类型、名称以及创建方式。Spring 根据这些信息创建 Bean,并将 Bean 放入 IoC 容器中。

常见的定义方式有:

1
<bean id="user" class="xxx.xxx.User" />
1
2
3
4
@Bean
public User user() {
return new User();
}

也可以通过 BeanDefinitionBuilder 以编程方式创建 BeanDefinition,再通过 BeanDefinitionRegistry#registerBeanDefinition 注册。

Spring 内置的 BeanDefinition

除了我们自己定义的 Bean,Spring 还会注册一些用于支撑框架运行的 BeanDefinition。例如:

Bean 名称 Bean 实例 作用
internalConfigurationAnnotationProcessor ConfigurationClassPostProcessor 处理 Spring 配置类
internalAutowiredAnnotationProcessor AutowiredAnnotationBeanPostProcessor 处理 @Autowired@Value
internalCommonAnnotationProcessor CommonAnnotationBeanPostProcessor 处理 @PostConstruct 等 JSR-250 注解
internalEventListenerProcessor EventListenerMethodProcessor 处理 @EventListener 标注的方法

这些对象本身也是容器中的 Bean,因此也可以通过依赖查找获取。不过,日常开发中通常不需要直接使用它们。

了解一下得了,面试里还没有遇到过这些问题。

手动注册的单例对象

除了让 Spring 根据 BeanDefinition 创建 Bean,也可以把一个已经创建好的对象直接注册到容器中:

1
singletonBeanRegistry.registerSingleton("user", user);

注册完成后,就可以通过 getBean("user") 查找到这个对象。由于对象是在注册前就已经创建好的,Spring 不负责它的创建过程,也不会完整管理它的生命周期。

Spring 内置的单例对象

Spring 也会向容器中注册一些常用的单例对象,例如:

Bean 名称 Bean 实例 作用
environment Environment 读取外部化配置和 Profiles
systemProperties java.util.Properties 读取 Java 系统属性
systemEnvironment java.util.Map 读取操作系统环境变量
messageSource MessageSource 提供国际化消息
applicationEventMulticaster ApplicationEventMulticaster 发布 Spring 应用事件

因此,像 Environment 这样的对象,也可以直接从 Spring 容器中查找:

1
Environment environment = applicationContext.getBean(Environment.class);

Spring BeanDefinition 作为依赖来源

  • 元数据:BeanDefinition
  • 注册:BeanDefinitionRegistry#registerBeanDefinition
  • 类型:延迟和非延迟
  • 顺序:Bean 生命周期顺序按照注册顺序

单例对象作为依赖来源

  • 要素
    • 来源:外部普通 Java 对象(不一定是 POJO)
    • 注册:SingletonBeanRegistry#registerSingleton
  • 限制
    • 无生命周期管理
    • 无法实现延迟初始化 Bean

非 Spring 对象容器管理对象作为依赖来源

  • 要素
    • 注册:ConfigurableListableBeanFactory#registerResolvableDependency
  • 限制
    • 无生命周期管理
    • 无法实现延迟初始化 Bean
    • 无法通过依赖查找

外部化配置作为依赖来源

  • 要素
    • 类型:非常规 Spring 对象依赖来源
  • 限制
    • 无生命周期管理
    • 无法实现延迟初始化 Bean
    • 无法通过依赖查找

问题

注入和查找的依赖来源是否相同?

  • 否,依赖查找的来源仅限于 Spring BeanDefinition 以及单例对象,而依赖注入的来源还包括 Resolvable Dependency 以及 @Value 所标注的外部化配置

单例对象能在 IoC 容器启动后注册吗?

  • 可以的,单例对象的注册与 BeanDefinition 不同,BeanDefinition 会被 ConfigurableListableBeanFactory#freezeConfiguration() 方法影响,从而冻结注册,单例对象则没有这个限制。

Spring 依赖注入的来源有哪些?

  • Spring BeanDefinition
  • 单例对象
  • Resolvable Dependency
  • @Value 外部化配置

参考资料