调用过程复现:
在DefaultListableBeanFactory的registerBeanDefinition
里打上断点,运行Entrance,观察调用栈:
Entrance源码:阅读Spring源码第一步:源码编译与创建调试入口
注册是从主函数开始的:
之后进入到FileSystemXmlApplicationContext的构造函数:
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
复制代码
又调用了另外一个构造函数:
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
复制代码
对应变量的值:
其中configLocations
是个数组,所以猜测最后调用了加载多个资源的方法。
又调用refresh()方法进行容器的初始化,此时又来到AbstractApplicationContext的refresh方法里:
又调用了obtainFreshBeanFactory
方法,跳进去会发现它是调用了子类的refreshBeanFactory
方法,以刷新子类的容器
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
复制代码
再往前进会发现实现refreshBeanFactory方法的是AbstractRefreshableApplicationContext类:
先创建了容器实例,再将实例传入到loadBeanDefinitions
里
进入到loadBeanDefinitions,该方法是由AbstractXmlApplicationContext实现的:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 为给定的BeanFactory创建一个新的XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 将容器本身"献祭"
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
复制代码
先是定义了XmlBeanDefinitionReader
的实例变量beanDefinitionReader
,变量的构造函数便是传入的DefaultListableBeanFactory
实例;
此外beanDefinitionReader
还给自己的ResourceLoader
设置上FileSystemXmlApplicationContext;
因此FileSystemXmlApplicationContext实例和DefaultListableBeanFactory实例便是通过beanDefinitionReader对象串联到了一起,这也就表明了打通了资源加载和BeanDefinition之间的通路。
再前进一步就是加载资源的配置流程了(AbstractXmlApplicationContext.java):
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations(); // 从这里继续进
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
复制代码
进入到getConfigLocations方法里:
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
for (String location : locations) {
count += loadBeanDefinitions(location);
}
return count;
}
复制代码
这里就是根据location来逐个调用loadBeanDefinitions去加载。
继续进到loadBeanDefinitions
方法里:
可以看到确实是使用了ResourcePatternResolver
来进行加载的,和上面的推断一样是用可以同时加载多个资源的。
再进入两层到loadBeanDefinitions
方法里:
发现到了对resource包裹上EncodedResource
这一步
再进入一层,此时就是对资源进行编码处理了:
进入到实际干活的doLoadBeanDefinitions
中:
这里面主要就是将xml解析成document对象了,解析完成之后就进入到注册阶段
注册阶段:
先是将Document对象解析成一个个的BeanDifinitions
继续进入到doRegisterBeanDefinitions
里:
就会发现这里开始解析BeanDefinition了,并且此处的delegate对象是BeanDefinitionParserDelegate对象
之后就会进入解析逻辑里面进行BeanDefinition的解析了:
因为我们遵循的是xml定义的规范,所以会执行parseDefaultElement
方法,方法之前已经分析过了,这一步会递归执行多次。
再往深一点就会看到调用了delegate的parseBeanDefinitionElement
方法区解析出包含了BeanDefinition
实例的BeanDefinitionHolder
包装实例
之后就来到了SpringIOC源码解析(5)—— BeanDefinition的注册中的BeanDefinition的注册环节了,最终将BeanDefinition实例注册到容器里,同时,如果BeanDefinition先前已经注册过了,就需要清空先前的注册信息,如果是单例,将先前已经创建出来的bean实例给清除掉,因为bean实例赖以生存的BeanDefinition已经改变了。
近期评论