文章目录
昨天在Spring的ApplicationContext.xml中配置Mybatis的XxxMapper.xml文件的时候,报错信息一直提示找不到XxxMapper.xml文件,仔细检查SQLSessionFactory的配置后才发现是因为没有在“classPath”后面加统配符号“*”,所以才导致一直找不到。
所以就是说classPath需要在后面加通配符“ *
”才能在后面的路径中使用统配符号“ *
”?
这一点把我坑了好一会,有点不爽,所以想去调试源码
看看怎么回事?
先结论:
在Spring中的配置ApplicationContext中用classPath指定路径时,如果想使用通配符“
*
”确实是需要加符号“*
”的
使用通配符的例子:(下面的mapperLocations的取值就是用了通配符)【配置1】
<!-- 创建SQLSessionFactory对象-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:config/mybatis-config.xml"/>
<property name="mapperLocations" value="classpath*:mapper/*.xml"/>
</bean>
不使用通配符的例子:
<!-- 创建SQLSessionFactory对象-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:config/mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/UserMapper.xml"/>
</bean>
调试目的:
找到源码关于判断有没有通配符“ *
”的那个if语句【目的1】
提升调试能力
能精准的在源码大山中找到自己想要看到的那一部分结构
先看调试结果:
关于【目的1】的关键语句在下面这个类里面
org.springframework.core.io.support.PathMatchingResourcePatternResolver
具体位置如下:
(当我使用上面的【配置1】去配置SQLSessionFactory时,可调试得到如下结果,该函数形参locationPattern的值就是我在SQLSessionFactory里面配置的值“classpath*:mapper/*.xml”)
(如果你想快速的将断点运行到这里,请看下面一个小点)
关于【目的1】的关键代码就在这个函数getResources()里面
@Override
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
//这个CLASSPATH_ALL_URL_PREFIX是个常量,值为“classpath*:”
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
//【逻辑1】
// a class path resource (multiple resources for same name possible)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
//【逻辑2】
// Generally only look for a pattern after a prefix here,
// and on Tomcat only after the "*/" separator for its "war:" protocol.
int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
locationPattern.indexOf(':') + 1);
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
在上面这个函数里面可以看到,它先判断了这个路径有没有以classpath*:
开头,有的话就执行【逻辑1】,没有的话就执行【逻辑2】
对于【逻辑1】,它再次判断了classpath*:
后面的路径有没有带*
有的话,再次去解析这个路径,没有的话就直接把对应路径下的文件拿出来,封装到Resource数组里面向上返回给前面的调用者了。
对于【逻辑2】则是直接将文件提取出来放回给调用者(这里还考虑了当locationPattern以“war:”开头的情况,我们可以先不用去管它)
所以在Spring中配置文件ApplicationContext中classpath配置通配符“*”与不配置的区别就在这个函数里面体现了
快速的将断点运行到上面那个函数那里(除去其他不需要的情况),省去寻找过程
1.首先找到对应类,对应方法
org.springframework.core.io.support.PathMatchingResourcePatternResolver
public Resource[] getResources(String locationPattern) throws IOException
2.然后在方法内的第一行加上一个条件断点
设置locationPattern的值就是你刚刚在【配置1】里面mapperLocations设置的值,比如我这里的值就是“classpath*:mapper/*.xml”
然后Debug运行,然后就是就静待断点到来就行了
下面是调试过程
首先这个【配置1】中的mapperLocations配置的是SQLSessionFactory这个接口的一个实现类org.mybatis.spring.SqlSessionFactoryBean的mapperLocations属性,而我们知道在Spring配置文件里面用property标签为bean设置属性的时候最终会通过调用该属性对应的set方法把值赋值该该属性,所以我们直接去找对应的set方法
方式一:直接打监视断点
(这种方式比较快)
上面说到set方法,所以我们可以从set入手。
注意到这个set方法的形参为Resource类型,我们可以想到大概的过程【过程1】:Spring会去解析我们在【配置1】中配置的路径,然后拿到路径下对应的文件,并将文件封装到Resource对象中
因为【过程1】中有封装成Resource对象的过程,所以肯定会调用Resource类的构造器,属性的set方法等。那我们可以从它在封装成Resource对象的过程入手【过程2】。
所以我们只需要去Resource类内对一些属性设置监视断点
,或是一些构造器等设置断点,马上就可以捕获到【过程2】了,然后从这里开始回溯,去寻找Spring在【过程1】中解析classPath
的步骤
(干饭先,待补
方式二:从栈帧往回追溯
注:
调试过程会有一些源码不对应,或是错位的情况注意换一下源码版本
上面说到set方法,所以我们可以从set入手,从这个set方法开始,通过栈帧进行回溯
我们给这个set方法加一个断点,然后可以看到,他根据我的【配置1】,把通配符
*
成功转化,把我的mapper文件夹下的文件都拿了出来准备set进去,所以我们要找的是他是怎么把这个通配符“转化”的
我们可以依照左边的栈帧去追寻这个过程,具体的就不说了
我的断点记录
然后我们可以找到,从“classpath*:mapper/*.xml”到Resource类的封装发生在这个类的这个方法里面
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
applyPropertyValues
然后对这个方法内的过程进行分析
就可以找到调试结果里面的那个类org.springframework.core.io.support.PathMatchingResourcePatternResolver
然后在那个类里面就可以找到关于【目的1】的关键代码
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/16bd62575e0e163771fb152493ff8464.png)