Spring源码深度解析01-详解debug走加载xml配置文件
序言
这是一个一起学习的笔记,也是一个留给自己的笔记,希望之后无论在哪里,能回来看看自己曾经的笔记,看看曾经的自己的。ヾ(≧▽≦*)o
快速了解spring,(spring5.x)
我这边围绕着大纲来一步步走:
- 加载配置文件和创建应用上下文
- 解析和注册Bean定义
- 实例化和依赖注入
- 处理Bean的生命周期
- 启动应用
- 处理AOP和事务
加载xml配置文件
mian()入口
//读取xx.xml文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("xx.xml");
ClassPathXmlApplicationContext
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//1.调用父类构造方法,初始化一些成员属性
super(parent);
//2.设置配置路径
setConfigLocations(configLocations);
if (refresh) {
//3.大名鼎鼎的refresh方法
refresh();
}
}
点击diagrams图 先挂着待会有用
设置配置路径
AbstractRefreshableConfigApplicationContext
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
//resolvePath 解析路径
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
此时,configLocations已经放在 AbstractRefreshableConfigApplicationContext
待会用到
resolvePath里面的
getEnvironment().resolveRequiredPlaceholders(path)
,这里会寻找Environment map中替换字符,如${name}如果environment为空,则创建environment。
return new StandardEnvironment();
读取BeanDefinitions
refresh();
先跳过prepareRefresh
,
进去obtainFreshBeanFactory()
再
进去refreshBeanFactory()
看diagrams图
看AbstractApplicationContext
的子类
AbstractRefreshableConfigApplicationContext
/**
* 省略的注解,记得看
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建BeanFactory DefaultListableBeanFactory重点,之后会经常看到
DefaultListableBeanFactory beanFactory = createBeanFactory();
//设置序列化id
beanFactory.setSerializationId(getId());
//自定义或定制beanFactory,设置相关Property
customizeBeanFactory(beanFactory);
//这节重点 读取BeanDefinitions
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
看diagrams图
AbstractRefreshableConfigApplicationContext
的子类 AbstractXmlApplicationContext
别走散了
/**
* 省略的注解,记得看
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
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.
// 初始化BeanDefinitionReader
initBeanDefinitionReader(beanDefinitionReader);
重点 读取BeanDefinitions
loadBeanDefinitions(beanDefinitionReader);
}
/**
* 省略的注解,记得看
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//getConfigLocations() 点进去会发现跳到父类,这个就是刚刚setConfigLocations()时候放在父类
String[] configLocations = getConfigLocations();
if (configLocations != null) {
加载loadBeanDefinitions
reader.loadBeanDefinitions(configLocations);
}
}
这里要小心点,会容易乱,需要连续点击loadBeanDefinitions,
记住一开始是在AbstractXmlApplicationContext
我贴出来的代码上,记得看入参,
搜索loadBeanDefinitions 让他持续高亮,准备,开始
AbstractBeanDefinitionReader
类,循环把location list 提取单个location
return loadBeanDefinitions(location, null);
- 点击,通过debug发现是第一个,进去(后面解释) 关于第2点解释
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);
-
又是循环,艹,别急,看入参Resource… resources,
-
这里会有多个子类,选子类
XmlBeanDefinitionReader
进去,不懂的先跟我点进去(后面解释) 关于第4点解释 -
继续点击,恭喜你,进入到真正的xml读取类
XmlBeanDefinitionReader
读取类 XmlBeanDefinitionReader
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
...
// 省略的代码
...
熟悉吧 读取io流 这里一定要会啊
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
读取完还需要解析xml文件
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
...
// 省略的代码
...
}
以防有人不懂,其实怕我之后自己不记得的ヾ(≧▽≦*)o,
关于第4点
loadBeanDefinitions(XmlBeanDefinitionReader reader)
开局第一句写的就是,如果没有debug第一次看绝对会晕。
关于第2点
建议怼着源码看 防止走散,第2点的
AbstractBeanDefinitionReader
类中loadBeanDefinitions()
,建议打个断点,一行行看
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException
- 首先,该方法获取资源加载器(
ResourceLoader
)对象,如果资源加载器为null
,则抛出BeanDefinitionStoreException
异常,提示无法从指定位置加载Bean定义。 - 接下来,通过判断资源加载器是否实现了
ResourcePatternResolver
接口来确定是否支持资源模式匹配。如果支持资源模式匹配,则使用资源加载器的getResources
方法获取符合指定位置模式的所有资源。 - 如果资源加载器不支持资源模式匹配,则假定指定位置为单个资源的绝对URL,并通过资源加载器的
getResource
方法获取该资源。 - 在获取资源后,调用
loadBeanDefinitions
方法重载的形式进行具体的Bean定义加载操作。该方法将返回加载的Bean定义数量。 - 如果提供了
actualResources
参数(一个可选的Set<Resource>
集合),则将加载的资源添加到该集合中。 - 最后,根据需要输出日志,表示从指定位置成功加载了多少个Bean定义,并返回加载的Bean定义数量。
总体而言,该方法根据指定的位置加载Bean定义。如果资源加载器支持资源模式匹配,则可以加载符合指定模式的多个资源;否则,仅加载指定位置的单个资源。加载的Bean定义将在后续的处理中用于创建和管理相应的Bean实例。
求求大哥们给个三连吧,真的是一行行怼的 >︿<