前言
接着上一篇的 BeanDefinition 资源定位开始讲。
Spring IoC 容器 BeanDefinition 解析过程就是把用户在配置文件中配置的 bean,解析并封装成 IoC 容器可以装载的 BeanDefinition 对象,BeanDefinition 是 Spring 定义的基本数据结构,其中的属性与配置文件中 bean 的属性相对应。
正文
首先看一下 AbstractRefreshableApplicationContext 的 refreshBeanFactory() 方法,这是一个模板方法,其中调用的 loadBeanDefinitions() 方法是一个抽象方法,交由子类实现。
/**
* 在这里完成了容器的初始化,并赋值给自己私有的 beanFactory 属性,为下一步调用做准备
* 从父类 AbstractApplicationContext 继承的抽象方法,自己做了实现
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果已经建立了 IoC 容器,则销毁并关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建 IoC 容器,DefaultListableBeanFactory 实现了 ConfigurableListableBeanFactory 接口
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 对 IoC 容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
// 载入 BeanDefinition,当前类中只定义了抽象的 loadBeanDefinitions() 方法,具体的实现调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
// 给自己的属性赋值
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
下面看一下 AbstractRefreshableApplicationContext 的子类 AbstractXmlApplicationContext 对 loadBeanDefinitions() 方法的实现。
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// DefaultListableBeanFactory 实现了 BeanDefinitionRegistry 接口,所以在初始化 XmlBeanDefinitionReader 时
// 将该 beanFactory 传入 XmlBeanDefinitionReader 的构造方法中。
// 从名字也能看出来它的功能,这是一个用于从 .xml文件 中读取 BeanDefinition 的读取器
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 为 beanDefinition 读取器设置 资源加载器,由于本类的基类 AbstractApplicationContext
// 继承了 DefaultResourceLoader,因此,本容器自身也是一个资源加载器
beanDefinitionReader.setResourceLoader(this);
// 为 beanDefinitionReader 设置用于解析的 SAX 实例解析器,SAX(simple API for XML)是另一种XML解析方法。
// 相比于DOM,SAX速度更快,占用内存更小。它逐行扫描文档,一边扫描一边解析。相比于先将整个XML文件扫描进内存,
// 再进行解析的DOM,SAX可以在解析文档的任意时刻停止解析,但操作也比DOM复杂。
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化 beanDefinition 读取器,该方法同时启用了 XML 的校验机制
initBeanDefinitionReader(beanDefinitionReader);
// 用传进来的 XmlBeanDefinitionReader 读取器读取 .xml 文件中配置的 bean
loadBeanDefinitions(beanDefinitionReader);
}
接着看一下上面最后一个调用的方法 loadBeanDefinitions(XmlBeanDefinitionReader reader)。
/**
* 读取并解析 .xml 文件中配置的 bean,然后封装成 BeanDefinition 对象
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
/**
* ClassPathXmlApplicationContext 与 FileSystemXmlApplicationContext
* 在这里的调用出现分歧,各自按不同的方式加载解析 Resource 资源
* 最后在具体的解析和 BeanDefinition 定位上又会殊途同归
*/
// 获取存放了 BeanDefinition 的所有 Resource,FileSystemXmlApplicationContext 中未对
// getConfigResources() 进行重写,所以调用父类的,return null。
// 而 ClassPathXmlApplicationContext 对该方法进行了重写,返回设置的值
Resource[] configResources = getConfigResources();
if (configResources != null) {
// 这里调用的是其父类 AbstractBeanDefinitionReader 中的方法,解析加载 BeanDefinition对象
reader.loadBeanDefinitions(configResources);
}
// 调用其父类 AbstractRefreshableConfigApplicationContext 中的实现,优先返回
// FileSystemXmlApplicationContext 构造方法中调用 setConfigLocations() 方法设置的资源路径
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// 这里调用其父类 AbstractBeanDefinitionReader 的方法从配置位置加载 BeanDefinition
reader.loadBeanDefinitions(configLocations);
}
}
AbstractBeanDefinitionReader 对 loadBeanDefinitions() 方法的三重重载。
/**
* loadBeanDefinitions() 方法的重载方法之一,调用了另一个重载方法 loadBeanDefinitions(String location)
*/
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
// 计数器,统计加载了多少个配置文件
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
/**
* 重载方法之一,调用了下面的 loadBeanDefinitions(String location, Set<Resource> actualResources) 方法
*/
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
/**
* 获取在 IoC 容器初始化过程中设置的资源加载器
*/
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 在实例化 XmlBeanDefinitionReader 时 曾将 IoC 容器注入该对象,作为 resourceLoader 属性
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
try {
// 将指定位置的 bean 配置文件解析为 BeanDefinition 对象
// 加载多个指定位置的 BeanDefinition 资源
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
/**
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* AbstractApplicationContext 继承了 DefaultResourceLoader,所以 AbstractApplicationContext
* 及其子类都可以调用 DefaultResourceLoader 中的方法,将指定位置的资源文件解析为 Resource,
* 至此完成了对 BeanDefinition 的资源定位
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
Resource resource = resourceLoader.getResource(location);
// 从 resource 中加载 BeanDefinition,loadCount 为加载的 BeanDefinition 个数
// 该 loadBeanDefinitions() 方法来自其 implements 的 BeanDefinitionReader 接口,
// 且本类是一个抽象类,并未对该方法进行实现。而是交由子类进行实现,如果是用 xml 文件进行
// IoC 容器初始化的,则调用 XmlBeanDefinitionReader 中的实现
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
小结
这种源码的直接 crtl+CV,什么都体会不到。
还是自己从头到尾看比较有效果。
参考资料
https://github.com/doocs/source-code-hunter/blob/main/docs/Spring/IoC/2%E3%80%81%E5%B0%86bean%E8%A7%A3%E6%9E%90%E5%B0%81%E8%A3%85%E6%88%90BeanDefinition.md