悟透Spring之Xml配置路径查找的秘密

字符串路径怎么查找XML资源

Spring中使用Xml配置时会使用ResourceLoader查找,官网文档里提到了很重要的一句:

NOTE:After you learn about Spring’s IoC container, you may want to know more about Spring’s Resource abstraction (as described in Resources), which provides a convenient mechanism for reading an InputStream from locations defined in a URI syntax. In particular, Resource paths are used to construct applications contexts, as described in Application Contexts and Resource Paths.

这边我们先直接定位到最主要的一个实现类PathMatchingResourcePatternResolver,文章最后会解释为什么会是这个类以及它的作用。

classpath: 查找资源

我们通过spring-bean*.xml查找Xml配置文件的时候,流程如下:

PathMatchingResourcePatternResolver

PathMatchingResourcePatternResolver

PathMatchingResourcePatternResolver

DefaultResourceLoader

ClassPathResource

ClassPathResource

ClassLoader

也就是classLoader.getResource返回一个classpath路径,所以如果spring-bean.xml不存在该路径下那么就找不到了。

classpath* 带通配符查找资源

而我们通过classpath*:spring-bean*.xml来查找的时候,流程如下:

image-20210825105623631

image-20210825105702416

findAllClasspathResources

image-20210825105812891

image-20210825110030410

addAllClassLoaderJarRoots

所以区别还是发生在PathMatchingResourcePatternResolver类的getResources方法内部

1
2
@Override
public Resource[] getResources(String locationPattern) throws IOException {}

ClasspathXmlApplicationContext加载BeanDefinition会调用AbstractBeanDefinitionReader类的这个方法,方法内部会去获取当前的ResourceLoader:

AbstractBeanDefinitionReader#loadBeanDefinitions

AbstractBeanDefinitionReader

那么当前的ResourceLoader到底是哪个呢?我们又要回到AbstractXmlApplicationContext去加载BeanDefinition的时候:XmlBeanDefinitionReader实例化后会把当前ApplicationContext设置为它的ResourceLoader

AbstractXmlApplicationContext#loadBeanDefinitions

AbstractXmlApplicationContext

为啥能设置呢?因为当前ApplicationContext的父类AbstractApplicationContext继承了DefaultResouceLoader。

1
public abstract class AbstractApplicationContext extends DefaultResourceLoader{}

且因为AbstractApplicationContext实现了ApplicationContext接口,ApplicationContext接口我们知道是继承自ResourcePatternResolver接口,从上篇文章的类图中我们可以一窥全貌。

所以从location获取资源就会调用AbstractApplicationContext的getResources方法:

1
2
3
4
5
6
7
8
//---------------------------------------------------------------------
// Implementation of ResourcePatternResolver interface
//---------------------------------------------------------------------
@Override
public Resource[] getResources(String locationPattern) throws IOException {
//todo 调用 resourcePatternResolver去 getResources 默认是 2020-09-05
return this.resourcePatternResolver.getResources(locationPattern);
}

这边就很重要了,resourcePatternResolver就是在构造函数里实例化的

1
2
3
4
5
6
7
8
9
10
/**
* Create a new AbstractApplicationContext with no parent.
*/

public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}

protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}

到这里我们就看到了本文一开始提到的PathMatchingResourcePatternResolver,需要注意的是在实例化该ResourcePatternResolver的时候把当前ResourceLoader当作构造函数参数传了进去。ResourcePatternResolver最终返回一个Resource数组给到AbstractBeanDefinitionReader。

欢迎关注我的公众号:沉迷Spring
显示 Gitment 评论
0%