思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。
作者:毅航😜
IOC(Inverse of Control,控制反转)
可以说是Spring
中的核心设计理念之一,其出现的目的在于降低组件之间的耦合度,从而提高代码的可维护性和可测试性。
而IOC
容器作为Spring
框架的核心组件之一,其负责管理应用程序中bean
的对象生命周期和依赖关系。为此Spring
内部提供复杂的容器架构来完成这一功能,这也就导致了Spring
中容器
有下图所示的错综复杂的体系结构。
注:本篇重点对
Spring
容器中的顶层接口:HierarchicalBeanFactory
、ListableBeanFactory
、AutowireCapableBeanFactory
进行分析介绍。
面对如此复杂的结构初学者可能会感到茫然无措。如果你也有相同的疑惑,不妨跟着笔者的思路来重新梳理Spring
中IOC
容器间关系,理清Spring
中的容器体系,体会Spring
中IOC
的设计精髓。
(注:不了解IOC
相关知识的读者可参考笔者之前文章:深入探究控制反转(IOC)与依赖注入(DI)间的关系) 进行了解 )
万物之始------BeanFactory
结合一开始的UML
类图不难看出,在Spring
中所有的容器都会扩展于BeanFactory
这个顶层接口。依据设计模式
的中的单一职责原则
来看:一个类或模块应该有且仅有一个引起它变化的原因。
换句话说,一个类或模块应该只负责一种类型的任务或功能。这也就意味着一个类或模块应该专注于完成特定的功能,而不应该承担过多的责任。
因此BeanFactory
作为IOC
容器的顶层接口,其所负责实现的功能应该是最简单。明确了这点后,我们来看BeanFactory
的相关源码也就会变的豁然开朗。
java
public interface BeanFactory {
/**
* 获取bean信息
**/
Object getBean(String name) throws BeansException;
// ... 省略getBean各类重载方法
/**
* 检测bean是否存在于容器
**/
boolean containsBean(String name);
/**
* 判断bean的类型信息
**/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
/**
* 获取bean的别名信息
**/
String[] getAliases(String name);
}
不难发现,BeanFactory
接口中定义的IOC
容器的基本行为包括:Bean
实例获取、判断Bean
是否存在、Bean
别名获取等行为。总的来看,BeanFactory
接口是Spring
框架中IOC
容器的核心接口之一,它负责管理和控制应用程序中的对象,从而定义了IOC
和DI
的核心功能。
进一步,通过BeanFactory
开发人员可以轻松地管理和维护应用程序中的各种组件,从而提高了代码的可维护性和可测试性。
枚举Bean
实例------ListableBeanFactory
在Spring
中ListableBeanFactory
继承自 BeanFactory
接口,因此其除了扩展了BeanFactory
基本的功能外,其还提供了更强大的检索和操作Bean
的功能。
换句话,ListableBeanFactory
提供了检索Bean
对象更多方式,让我们在获取Bean
时,不再拘泥于BeanFactroy
中getBean(String name)
通过beanName
获取Bean
实例的方式,而是提供了按类型、名称模式、注解等条件批量检索 Bean
的方式。其内部相关源码如下:
java
public interface ListableBeanFactory extends BeanFactory {
// 省略一些同名重载方法
/**
* 按类型获取 Bean 实例
***/
String[] getBeanNamesForType(ResolvableType type);
/**
* 获取所有 Bean 的名称
***/
String[] getBeanDefinitionNames();
/**
* 按注解获取 Bean 实例
***/
String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
例如,如果想获取容器中所有类型为UserService
类型的Bean
的名称,代码可写成如下形式:
java
// 构建上下文环境
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 获取工厂
ListableBeanFactory factory = (ListableBeanFactory) context;
// 调用ListableBeanFactory中的getBeanNamesForType方法
String[] serviceBeanNames = factory.getBeanNamesForType(UserService.class);
// 循环打印
for (String beanName : allBeanNames) {
System.out.println("Bean name: " + beanName);
}
容器间的继承------HierarchicalBeanFactory
在google
翻译中Hierarchical
一词被解释为:"分层的、等级的"的含义。而在Spring
框架中,HierarchicalBeanFactory
提供了一种维护Bean
工厂间父子关系的机制。即每个 HierarchicalBeanFactory
可以有一个父工厂,同时也可以作为其他工厂的父工厂。其内部方法也比较简洁:
java
public interface HierarchicalBeanFactory extends BeanFactory {
@Nullable
BeanFactory getParentBeanFactory();
boolean containsLocalBean(String name);
}
其中,getParentBeanFactory()
的作用在于获取当前容器的父BeanFactory
对象;而方法 containsLocalBean(String name)
则用于检查当前本地的容器中是否有指定名称的 Bean
,而不会往上找父 BeanFactory
。该方法要与我们熟知的getBean
进行区分,对于getBean
而言,其会首先从当前 BeanFactory
开始查找是否存在指定的 Bean
,如果当前找不到就依次向上找父 BeanFactory
直到找到为止返回,如果找不到则会抛出 NoSuchBeanDefinitionException
的相关异常。
这里不妨思考一个问题:
HierarchicalBeanFactory
可以使得容器间产生继承关系,假如现在有两个容器A和B,其中容器A为B的父容器,那么容器A和容器B中是否可以有重名的Bean
实例呢?欢迎评论区留下你的想法~~~
总之,HierarchicalBeanFactory
提供了一种灵活的机制来管理 bean
工厂之间的父子关系,并允许在这种分层结构中共享配置、资源和bean
定义。
自动装配的基石------AutowireCapableBeanFactory
在Spring
中,AutowireCapableBeanFactory
提供了 Bean
实例自动装配(Autowiring
)的核心功能。其允许对现有的Bean
实例进行自动装配,注入其依赖。
此外,它还支持调用 Bean
的初始化和销毁方法,以及应用 BeanPostProcessor
进行自定义的前后处理。
java
public interface AutowireCapableBeanFactory extends BeanFactory {
// 省略其他内部属性即重名重载方法
// 对现有的 Bean 实例进行自动装配。
void autowireBean(Object existingBean)
// 创建一个新的 Bean 实例,并自动装配其依赖。
<T> T createBean(Class<T> beanClass)
// 配置现有的 Bean 实例,包括自动装配和应用后置处理器。
Object configureBean(Object existingBean, String beanName)
// 初始化现有的 Bean 实例,包括调用初始化方法和后置处理器
void initializeBean(Object existingBean, String beanName)
// 销毁现有的 Bean 实例,包括调用销毁方法和后置处理器。
void destroyBean(Object existingBean, String beanName)
}
接下来,我们便通过一个例子来演示如何通过AutowireCapableBeanFactory
来完成自动装配,开始之前我们先定义如下UserMapper
、UserService
两个类,其依赖关系如下所示:
java
public class UserMapper {
public void doSomething() {
System.out.println("Doing something in MyRepository");
}
}
public class UserService {
@Autowired
private UserMapper userMapper;
public void performAction() {
userMapper.doSomething();
}
}
(注:此时省略UserMapper
、UserService
的beans.xml
的配置信息)
java
// 加载 Spring 配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 获取 AutowireCapableBeanFactory
AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
// 对现有 Bean 实例进行自动装配
UserService userService = new UserService();
factory.autowireBean(existingBean);
在上述代码中,通过 autowireBean
方法,UserService
实例的 userMapper
字段将会被自动注入一个 UserMapper
实例,从而可以正确调用 performAction
方法。
总的来看,AutowireCapableBeanFactory
提供了灵活的自动装配和Bean
管理功能,使你可以在运行时动态地创建和配置 Bean
实例,并自动注入它们的依赖。
总结
本文从BeanFactory
入手,详细的对Spring
容器中的顶层接口:HierarchicalBeanFactory
、ListableBeanFactory
、AutowireCapableBeanFactory
进行分析介绍。具体来看
HierarchicalBeanFactory
:支持父子Bean
工厂的层次结构,允许在父工厂中查找Bean
。ListableBeanFactory
:提供批量检索和查询Bean
的功能,支持按类型和注解查找Bean
。AutowireCapableBeanFactory
:提供自动装配和Bean
生命周期管理功能,支持动态创建和配置Bean
。