文章目录
  1. 1. Spring加载classpath与classpath*的过程
  2. 2. 关于classpath与classpath*的一些测试
  3. 3. 对于classpath与classpath*的一些理解
    1. 3.1. 结论1
    2. 3.2. 结论2
    3. 3.3. 结论3
      1. 3.3.1. 问题
      2. 3.3.2. 分析
      3. 3.3.3. 结论

Spring加载classpath与classpath*的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1.无论是classpath还是classpath*都可以加载整个classpath下(包括jar包里面)的资源文件。
2.classpath只会返回第一个匹配的资源,查找路径是优先在项目中存在资源文件,再查找jar包。
3.文件名字包含通配符资源(如果spring-.xml,spring.xml), 如果根目录为"", classpath加载不到任何资源
而classpath*则可以加载到classpath中可以匹配的目录中的资源,但是不能加载到jar包中的资源
第1,2点比较好表理解,大家可以自行测试,第三点表述有点绕,举个例,现在有资源文件结构如下:
src/main/resources
META-INF
aaa
notice.txt
notice.txt
notice.txt

classpath:notice*.txt 加载不到资源
classpath*:notice*.txt 加载到resource根目录下notice.txt
classpath:META-INF/notice*.txt 加载到META-INF下的一个资源(classpath是加载到匹配的第一个资源,就算删除classpath下的notice.txt,他仍然可以加载jar包中的notice.txt)
classpath:META-*/notice*.txt 加载不到任何资源
classpath*:META-INF/notice*.txt 加载到classpath以及所有jar包中META-INF目录下以notice开头的txt文件
classpath*:META-*/notice*.txt 只能加载到classpath下 META-INF目录的notice.txt

有一些错误:

1
2
3
classpath:META-INF/notice*.txt        可能什么也加载不到

classpath*:META-*/notice*.txt 加载到classpath以及所有jar包中META-INF目录下以notice开头的txt文件

原文链接:https://blog.csdn.net/n447194252/article/details/76664053

关于classpath与classpath*的一些测试

1
2
3
4
测试结果:
1、classpath在使用时,第一级路径无法使用通配符,如classpath:general*.properties或者classpath:general*/general*.properties是无法获取到的
2、在我将general.properties拆成两个文件后,classpath同样可以获取两个文件,读取到全部内容,不知道是不是我测的方法有问题,这方面没感觉出来说是只能获取到第一个文件的限制
3、classpath比classpath*快,明确位置比通配符快。

对于2的说明:

应该是相同类路径下的相同名称的资源只能获取第一个,这个测试4中general.properties一个放在general下,一个放在general1下,虽然名称相同,但是不是同一路径了

原文链接:https://blog.csdn.net/n447194252/article/details/76664053

对于classpath与classpath*的一些理解

一般由PathMatchingResourcePatternResolver类的getResources解析

1
new PathMatchingResourcePatternResolver().getResources(locationPattern);

getResources()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
// classpath*:前缀
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// 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()));
}
}
// classpath:前缀
else {
// 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)};
}
}
}

findPathMatchingResources()方法匹配模式路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
//根路径资源
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
if (resolvedUrl != null) {
rootDirUrl = resolvedUrl;
}
rootDirResource = new UrlResource(rootDirUrl);
}
if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
}
else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isTraceEnabled()) {
logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}

结论1

classpath*:可以获取同一路径同一名字的多个资源

classpath:可以获取同一路径同一名字的一个资源

结论2

当使用classpath:时,后面的路径不能紧接通配符路径

例如:

1
classpath:**/*.xml        classpath:spri*/*.xml

spring的解析类在解析时会将路径其分成两部分(根路径+包含通配符路径)

1
classpath: 和 **/*.xml     classpath: 和 spri*/*.xml

并且getResources()方法中的

return new Resource[] {getResourceLoader().getResource(locationPattern)};

getResourceLoader().getResource(locationPattern)

进入DefaultResourceLoader类的getResource()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");

for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}

if (location.startsWith("/")) {
return getResourceByPath(location);
}
// 截取了classpath:
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}

classpath:被截取,返回的根路径为””, 这样就获取不到任何资源

结论3

问题

关于类路径下META-INF文件夹,使用前缀classpath:以及通配符获取不到资源

例如路径:

1
classpath:META-INF/*.xml

获取不到META-INF下的mapper.xml

解决:可以在META-INF中添加一个目录,例如classpath:META-INF/mapper/*.xml就可以了

调试分析:

findPathMatchingResources()方法中的有一句

1
URL rootDirUrl = rootDirResource.getURL();

遇到rootDirResource是META-INF时,会获取一个根路径下的一个jar包路径

(file:/C:Program%20Files/Java/jdk1.8.0_161/jre/lib/ext/jfxrt.jar!/META-INF/)

在该jar路径下META-INF下查找mapper.xml当然查找不到

分析

classpath:使用路径匹配,获取的资源只有一个。

getResources()方法中的classpath:分支获取根路径资源的方法

1
return new Resource[] {getResourceLoader().getResource(locationPattern)};

可以看出,虽然new了一个数组Resource,但是实际数组中只有一个根路径资源,

因此,相同目录下使用classpath:与路径匹配获取的资源只有一个

并且findPathMatchingResources()方法中

1
2
3
4
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
...

根据rootDirResource.getURL()获取一个URL路径

Resource实现类ClassPathResource中getURL()方法,

执行了return this.classLoader.getResource(this.path);返回了一个URL

示例:

1
2
3
4
5
6
7
@Test
public void test5(){
URL mapper = this.getClass().getClassLoader().getResource("META-INF/mappers/");
System.out.println(mapper.getPath());
URL metainf = this.getClass().getClassLoader().getResource("META-INF/");
System.out.println(metainf.getPath());
}

打印:

1
2
file:/E:/maven/maven-repository/com/szxy/szxy-spring-boot-autoconfigure/1.0-SNAPSHOT/szxy-spring-boot-autoconfigure-1.0-SNAPSHOT.jar!/META-INF/mappers/
file:/C:/Program%20Files/Java/jdk1.8.0_161/jre/lib/ext/jfxrt.jar!/META-INF/

然而,这两个jar下虽然有对应的目录URL,但并不是我们想要匹配的jar,因此,匹配不到任何资源

结论

​ 1)使用classpath:获取资源,注意匹配目录不要与其他jar包中的重复,因为其根路径资源只加载

一个,如果通过根路径获取了另一个不含有资源的jar包(只是具有相同的类路径),则无法获取资源。

1
2
3
例: classpath:META-INF/*.txt
路径分割成根路径META-INF和模式路径*.txt
许多jar包下都有META-INF目录,但是只会加载一个jar包去匹配资源而忽略其他jar包,因此可能导致需要的资源没有加载

2)获取单一资源,可以直接写完整的路径名进行匹配

3 ) classpath:如果具有相同路径相同名称的资源,则只加载一个

文章目录
  1. 1. Spring加载classpath与classpath*的过程
  2. 2. 关于classpath与classpath*的一些测试
  3. 3. 对于classpath与classpath*的一些理解
    1. 3.1. 结论1
    2. 3.2. 结论2
    3. 3.3. 结论3
      1. 3.3.1. 问题
      2. 3.3.2. 分析
      3. 3.3.3. 结论