细数 Spring 依赖查找的那些方式

简介: 前言依赖查找和依赖注入是 Spring IOC 容器提供的核心特性,Spring 建议尽量使用依赖注入的方式,另外 Spring 也提供了相关的 API 供用户进行依赖查找,Spring 依赖注入的能力同样依赖于依赖查找,这篇我们将关注 Spring 有哪些依赖查找的方法。

前言


依赖查找和依赖注入是 Spring IOC 容器提供的核心特性,Spring 建议尽量使用依赖注入的方式,另外 Spring 也提供了相关的 API 供用户进行依赖查找,Spring 依赖注入的能力同样依赖于依赖查找,这篇我们将关注 Spring 有哪些依赖查找的方法。


单一类型查找


BeanFactory 做为基础容器,依赖查找的方法都由其或其子类提供。关于单一类型的查找,如果 Spring 查不到对应的 bean 或查到多个 bean 并且无法确认使用哪个 bean 则会抛出异常。有关单一类型查找的方法如下。


public interface BeanFactory {
  // 按照名称进行类型
  Object getBean(String name) throws BeansException;
  // 按照名称和类型进行查找
  <T> T getBean(String name, Class<T> requiredType) throws BeansException;
  // 按照名称和实例化参数进行查找
  Object getBean(String name, Object... args) throws BeansException;
  // 按照类型进行查找
  <T> T getBean(Class<T> requiredType) throws BeansException;
  // 按照类型和实例化参数查找
  <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;


集合类型查找


BeanFactory 只能用于获取单一类型的 bean,获取集合类型的 bean 的能力由其子类 ListableBeanFactory 提供。


public interface ListableBeanFactory extends BeanFactory {
  // 获取给定类型的 bean,map 中的 key 为 bean 的名称,值为 bean 的实例,不会考虑 BeanFactory 层次
  <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException;
  // 获取给定类型的 bean,map 中的 key 为 bean 的名称,值为 bean 的实例,不会考虑 BeanFactory 层次
  <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
      throws BeansException;
  // 获取存在指定注解的 bean 的名称
  String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
  // 获取存在指定注解的 bean,map 中的 key 为 bean 的名称,值为 bean 的实例
  Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) throws BeansException;
}


层次性依赖查找


Spring 对 BeanFactory 进行分层设计,不同的子接口分别具有不同的特性。HierarchicalBeanFactory 则被设计用来获取父 BeanFactory,其定义如下。


public interface HierarchicalBeanFactory extends BeanFactory {
  // 获取父 BeanFactory
  @Nullable
  BeanFactory getParentBeanFactory();
  // 当前 BeanFactory 是否包含给定名称的 bean
  boolean containsLocalBean(String name);
}


BeanFactory 接口中 getBean 方法的实现已经考虑到了父 BeanFactory,而 ListableBeanFacoty 接口中的 getBeansOfType 方法则未考虑父 BeanFactory,如果想利用 ListableBeanFacoty 中的方法 getBeansOfType 获取父 BeanFactory 中的 bean 则需要先调用 getParentBeanFactory 方法获取父 BeanFactory,然后调用父 BeanFactory 中的 getBeansOfType 方法进行查找。


延迟查找


Spring 提供了一个 ObjectFactory 接口,其封装了 BeanFactory 获取 bean 的能力,我们拿到 ObjectFactory 再调用其获取 bean 的方法就可以做到延迟查找 (关于 BeanFactory、FactoryBean、ObjectFactory 的区别见 Spring BeanFactory、FactoryBean、ObjectFactory 有何不同?)。ObjectFactory 只能用于获取单个 bean 对象,而它的子类 ObjectProvider 则可以用于获取多个 bean,获取 ObjectProvider 的方法如下。


public interface BeanFactory {
  // 根据类型获取 ObjectProvider
  <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
  // 根据类型获取 ObjectProvider
  <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
}


再看如何从 ObjectProvider 中获取 bean ?


public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
  // 根据参数实例化并获取 ObjectProvider 管理的 bean
  T getObject(Object... args) throws BeansException;
  // 安全的获取 bean,如果 bean 不存在不会抛出异常
  @Nullable
  T getIfAvailable() throws BeansException;
  // 安全的获取 bean,如果 bean 不存在或存在多个返回 null
  @Nullable
  T getIfUnique() throws BeansException;
  // 安全的获取 bean 的流
  default Stream<T> stream() {
    throw new UnsupportedOperationException("Multi element access not supported");
  }
}


ObjectProvider 提供了安全的获取 bean 和包含 bean 的 Stream 的方法。其接口 ObjectFactory 则提供了获取单一 bean 的方法,如下。


public interface ObjectFactory<T> {
  // 获取当前工厂管理的 bean 的实例
  T getObject() throws BeansException;
}


依赖查找中的那些异常


依赖查找中,多数方法会抛出 BeansException,其表示通用的依赖查找的异常,具体异常由其子类进行表示。依赖查找中 BeansException 常见的子类如下。


image.png


依赖查找的 bean 的来源


通过 BeanFactory 接口及其子接口提供的方法,我们可以查询到依赖,那么肯定是有一个地方存储 Spring 的依赖,具体如下。


BeanDeifnition:BeanDefinition 是 bean 的定义,在容器内注册的 BeanDefinition 最终将成为 Spring 容器管理的 bean,因此可以通过依赖查找获取。BeanDefinition 的更多信息,可参见我的另一篇博客 掌握 Spring 必须知道的 BeanDefinition。

容器内注册的单例对象:Spring 容器管理的对象不仅有 bean,还包括一些单例对象。注册单例对象的方法为 SingletonBeanRegistry#registerSingleton,ConfigurableBeanFactory 继承 SingletonBeanRegistry,因此可以直接通过 ConfigurableBeanFactory 注册单例对象。BeanFactory#getBean(…) 方法的实现也会考虑到单例对象。


总结


本篇对BeanFactory 单一依赖类型、集合依赖类型、层次依赖、延迟查找依赖等查找依赖的方法进行介绍,并介绍了依赖查找中常见的异常以及依赖查找的来源。理解依赖查找对依赖注入也会有所帮助,下篇将介绍 Spring 的依赖注入。


目录
相关文章
|
6月前
|
人工智能 Java Spring
Spring Boot循环依赖的症状和解决方案
Spring Boot循环依赖的症状和解决方案
|
2月前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
42 4
|
6月前
|
设计模式 Java 开发者
解密Spring:优雅解决依赖循环的神兵利器
解密Spring:优雅解决依赖循环的神兵利器
514 57
|
4月前
|
缓存 Java 开发者
Spring循环依赖问题之Spring循环依赖如何解决
Spring循环依赖问题之Spring循环依赖如何解决
|
4月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
4月前
|
Java Spring
Spring循环依赖问题之构造器内的循环依赖如何解决
Spring循环依赖问题之构造器内的循环依赖如何解决
|
3月前
|
前端开发 Java 测试技术
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
单元测试问题之在Spring MVC项目中添加JUnit的Maven依赖,如何操作
|
6月前
|
缓存 Java 开发工具
【spring】如何解决循环依赖
【spring】如何解决循环依赖
210 56
|
4月前
|
JavaScript Java Maven
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
理解固化的Maven依赖:spring-boot-starter-parent 与 spring-boot-dependencies
2034 1
|
4月前
|
Java Spring 容器
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
Spring循环依赖问题之两个不同的Bean A,导致抛出异常如何解决
下一篇
无影云桌面