源码调试:Spring中配置文件ApplicationContext中classpath配置通配符“*”与不配置的区别


昨天在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】的关键代码



在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值