1. Spring版本:5.2.5.RELEASE
2. 引入依赖
直接下载spring的源码看的比较累,这是实话,spring的源码量实在是太大了,看了好多大神写的spring源码的博客,如果一行行的啃源码还是蛮辛苦,虽然源码的目录结构非常的清晰,但是还是想通过引入spring的依赖,通过单元测试走debug的模式,阅读起来相对轻松一点,也免得枯燥、也更有成就感。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springframework.version}</version>
</dependency>
3.第一印象
3.1. spring-bean.xml配置
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">
<bean id="myTestBean1" class="com.demo.bean.MyTestBean1"/>
<bean id="myTestBean2" class="com.demo.bean.MyTestBean2"/>
</beans>
3.2. 测试类
package com.demo.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestBean {
// 直接通过main函数来测试
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-bean.xml");
System.out.println(classPathXmlApplicationContext);
}
}
3.3 ClassPathXmlApplicationContext的一个整体印象
ClassPathXmlApplicationContext这个类主要是定义了几个构造函数,可以理解为是xml方式定义spring的bean的一个主要入口,
其中还定义了一个成员变量是数组形式的成员变量,如下:
@Nullable
private Resource[] configResources;
对于Resource这个接口类具体如下,通过它定义的接口方法我想能理解它的作用了,主要是对xml的文件读取等操作。
3.4 以ClassPathXmlApplicationContext.java类的一个构造函数为入口进行debug分析源码
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-bean.xml");
以上代码执行会调用另外一个构造函数,如下:
/**
* Create a new ClassPathXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of resource locations
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
// set 配置文件的文件名数组
setConfigLocations(configLocations);
// 传入的refresh是true
if (refresh) {
// 可以理解为刷新所有的配置但是这样理解有点牵强
refresh();
}
}
因为主要是要追踪spring是如何加载配置文件的过程,所以,对AbstractApplicationContext.refresh()方法,其他的操作就不看了,先看spring怎么加载xml的过程。refresh()方法调用是如下:
抽象类AbstractApplicationContext.refres()方法如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
其中,调用的方法是开始加载bean做准备,其实可以从方法名也可以看得出来
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
在这个方法执行前,其实xml文件已经解析完成,一下方法是解析xml文件的一个入口
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
4. 1 xml中document大致加载过程
bean的加载流程大致是如上,其中,加载的主要实现是通过XmlBeanDefinitionReader来实现的,XmlBeanDefinitionReader的UML图如下:
4.2 xml加载的主要实现
4.3 XmlBeanDefinitionReader bean加载内部执行
参考资料