思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。
作者:毅航😜
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
的相关源码也就会变的豁然开朗。
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
的方式。其内部相关源码如下:
public interface ListableBeanFactory extends BeanFactory {
// 省略一些同名重载方法
/**
* 按类型获取 Bean 实例
***/
String[] getBeanNamesForType(ResolvableType type);
/**
* 获取所有 Bean 的名称
***/
String[] getBeanDefinitionNames();
/**
* 按注解获取 Bean 实例
***/
String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType);
例如,如果想获取容器中所有类型为UserService
类型的Bean
的名称,代码可写成如下形式:
// 构建上下文环境
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
可以有一个父工厂,同时也可以作为其他工厂的父工厂。其内部方法也比较简洁:
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
进行自定义的前后处理。
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
两个类,其依赖关系如下所示:
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
的配置信息)
// 加载 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
。