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:如果具有相同路径相同名称的资源,则只加载一个