依赖查找是 Spring 控制反转(IoC)容器的基础概念之一。与依赖注入的被动方式不同,依赖查找是一种主动或手动获取依赖的方式,通常依赖于容器或标准 API 的实现。在这篇博客中,我们将深入探讨 Spring 中的各种依赖查找类型、相关 API、使用场景,以及安全性与异常处理等注意事项。
什么是依赖查找?
在 Spring 中,依赖查找指的是通过编程方式从 IoC 容器中主动获取 Bean(由容器管理的对象)的过程。与依赖注入(由容器自动提供依赖)不同,依赖查找允许开发者在需要时手动控制 Bean 的检索。这种方式在需要动态确定依赖或处理可选 Bean 的场景中尤为实用。
Spring 的依赖查找大致可以分为以下几类:
- 根据 Bean 名称查找
- 根据 Bean 类型查找
- 根据 Bean 名称 + 类型查找
- 根据 Java 注解查找
此外,根据查找的 Bean 是单一对象还是集合对象,以及是否需要延迟查找,Spring 提供了不同的 API 支持。
单一类型依赖查找
单一类型依赖查找主要通过 BeanFactory
接口实现。以下是具体的查找方式和相关 API:
根据 Bean 名称查找
getBean(String)
: 通过 Bean 名称获取实例。- Spring 2.5 增强:
getBean(String, Object...)
,支持覆盖默认构造参数。
根据 Bean 类型查找
- 实时查找:
- Spring 3.0:
getBean(Class)
,根据类型获取 Bean。 - Spring 4.1:
getBean(Class, Object...)
,支持构造参数。
- Spring 3.0:
- 延迟查找(Spring 5.1):
getBeanProvider(Class)
: 返回一个延迟加载的 Bean 提供者。getBeanProvider(ResolvableType)
: 支持更复杂的类型解析。
根据 Bean 名称 + 类型查找
getBean(String, Class)
: 结合名称和类型,确保类型安全。
集合类型依赖查找
集合类型依赖查找通过 ListableBeanFactory
接口实现,适用于获取某一类型的所有 Bean。
根据 Bean 类型查找
- 获取 Bean 名称列表:
getBeanNamesForType(Class)
: 返回匹配类型的 Bean 名称。- Spring 4.2:
getBeanNamesForType(ResolvableType)
,支持更精确的类型匹配。
- 获取 Bean 实例列表:
getBeansOfType(Class)
: 返回类型匹配的 Bean 实例集合及其重载方法。
根据注解查找
- Spring 3.0:
getBeanNamesForAnnotation(Class<? extends Annotation>)
: 获取带有指定注解的 Bean 名称。getBeansWithAnnotation(Class<? extends Annotation>)
: 获取带有指定注解的 Bean 实例。findAnnotationOnBean(String, Class<? extends Annotation>)
: 获取特定 Bean 上的注解实例。
层次性依赖查找
层次性依赖查找通过 HierarchicalBeanFactory
接口实现,适用于具有父子关系的容器。
- 获取父容器:
getParentBeanFactory()
。 - 层次性查找:
- 根据名称: 通过
containsLocalBean
方法判断 Bean 是否存在于当前容器。 - 根据类型:
- 单一类型:
BeanFactoryUtils#beanOfType
。 - 集合类型:
BeanFactoryUtils#beansOfTypeIncludingAncestors
。
- 单一类型:
- 根据注解:
BeanFactoryUtils#beanNamesForTypeIncludingAncestors
。
- 根据名称: 通过
延迟依赖查找
延迟依赖查找通过以下接口实现,特别适用于避免立即加载 Bean 的场景:
ObjectFactory
: 基础延迟加载接口。ObjectProvider
(Spring 5 扩展):getIfAvailable(Supplier)
: 如果 Bean 可用则返回,否则使用默认值。ifAvailable(Consumer)
: 如果 Bean 存在则执行操作。stream()
: 支持流式操作,处理集合类型。
安全依赖查找
并非所有依赖查找方式都是安全的。以下是常见查找方式的安全性对比:
依赖查找类型 | 代表实现 | 是否安全 |
---|---|---|
单一类型查找 | BeanFactory#getBean | 否 |
ObjectFactory#getObject | 否 | |
ObjectProvider#getIfAvailable | 是 | |
集合类型查找 | ListableBeanFactory#getBeansOfType | 是 |
ObjectProvider#stream | 是 |
注意:层次性查找的安全性取决于其底层单一或集合类型的实现。
内建可查找的依赖
Spring 上下文(AbstractApplicationContext
)内置了一些可直接查找的依赖:
Bean 名称 | 实例类型 | 使用场景 |
---|---|---|
environment | Environment | 外部化配置和 Profiles |
systemProperties | java.util.Properties | Java 系统属性 |
systemEnvironment | java.util.Map | 操作系统环境变量 |
messageSource | MessageSource | 国际化文案 |
lifecycleProcessor | LifecycleProcessor | Lifecycle Bean 处理 |
applicationEventMulticaster | ApplicationEventMulticaster | Spring 事件广播 |
此外,在注解驱动的 Spring 应用中,还包括以下内置 Bean(部分示例):
Bean 名称 | 实例类型 | 使用场景 |
---|---|---|
internalConfigurationAnnotationProcessor | ConfigurationClassPostProcessor | 处理 Spring 配置类 |
internalAutowiredAnnotationProcessor | AutowiredAnnotationBeanPostProcessor | 处理 @Autowired 和 @Value 注解 |
internalEventListenerProcessor | EventListenerMethodProcessor | 处理 @EventListener 事件监听方法 |
依赖查找中的常见异常
依赖查找可能触发 BeansException
的子类异常,以下是典型场景:
异常类型 | 触发条件 | 场景举例 |
---|---|---|
NoSuchBeanDefinitionException | Bean 不存在于容器中 | BeanFactory#getBean |
NoUniqueBeanDefinitionException | 类型查找时存在多个 Bean 实例 | BeanFactory#getBean(Class) |
BeanInstantiationException | Bean 类型为抽象类或接口 | BeanFactory#getBean |
BeanCreationException | Bean 初始化失败 | 初始化方法抛出异常 |
BeanDefinitionStoreException | Bean 定义元信息非法 | XML 配置资源无法解析 |
总结
Spring 的依赖查找提供了灵活的方式来获取 IoC 容器中的 Bean,从单一类型到集合类型,从实时查找至延迟加载,涵盖了多种开发场景。理解这些 API 的功能和限制(如安全性、异常处理)对于编写健壮的 Spring 应用至关重要。无论是通过名称、类型还是注解查找,Spring 都为开发者提供了强大的工具来管理依赖。