[转]Spring maven 项目中的classpath路径问题

在配置项目的web.xml时, 不小心将context-param配置成classpath:applicationContext.xml ,然后遇到找不applicationContext.xml的问题。 然后找到一篇关于加载资源文件很好的文章,故转到这里备忘。

错误配置:

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:applicationContext.xml</param-value>
</context-param>

正确配置:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext.xml</param-value>
  </context-param>

===============================================================

转自:https://blog.csdn.net/zhuqiuhui/article/details/65936889

Spring中的classpath与classpath*一直是开发中的心病,没有时间静下心来研究下,终于一气呵成!网上总结的也比较多,各种各样的说法,还不如自己亲自解读一下spring中的源码,这样下次再次使用心里就安心多了,欢迎支持!

一、问题描述

使用spring时import资源文件时路径查找顺序不明(开发中的疑惑),或者加载资源失败(不知道怎么更改路径)?

二、classpath代码解析

    直接上代码,这是代码中的主类 PathMatchingResourcePatternResolver 类,位于包 org.springframework.core.io.support 下,该类中的 getResources 函数是逻辑的核心,如下: 


public Resource[] getResources(String locationPattern)throws IOException {   
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {  
        if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {  
            // case 1:如果以classpath*开头且包含?或者* ,例如查找: classpath*: applicationContext-*.xml
            return findPathMatchingResources(locationPattern);  
        }  
        else {  
            // case 2: 不包含?或者*,直接全名查找,例如查找: classpath*: applicationContext-test.xml
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));  
        }  
    }  
    else {
        // 以 classpath:开头
        int prefixEnd = locationPattern.indexOf(":") +1;  
        if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {  
            // case 3: 如果不是以classpath*开头且包含?或者* ,例如查找: classpath: applicationContext-*.xml
            return findPathMatchingResources(locationPattern);  
        }  
        else {  
            // case 4: 如果不是以classpath*开头且不包含?或者* ,例如查找: classpath: applicationContext-test.xml  
            returnnew Resource[] {getResourceLoader().getResource(locationPattern)};  
        }  
    }  


2.1 如果以classpath*开头且包含?或者*

例如查找: classpath*: applicationContext-*.xml ,使用findPathMatchingResources函数,看下该函数:

                   
protected Resource[] findPathMatchingResources(String locationPattern)throws IOException {
    /*函数determineRootDir函数是拿到能够确定的目录,如 classpath*:/aaa/bbb/applicationContext-*.xml 则返回classpath*:/aaa/bbb/
                                                   classpath*:/aaa/*/applicationContext-*.xml,则返回 classpath*:/aaa/ 
    (代码就不再粘贴了,有兴趣的可以看下源码)*/
    String rootDirPath = determineRootDir(locationPattern);
    // 获取字符串locationPattern中后面不确定的内容
    String subPattern = locationPattern.substring(rootDirPath.length());
    // 递归加载已经确定的内容
    Resource[] rootDirResources = getResources(rootDirPath);
    Set result = new LinkedHashSet<>(16);
    for (Resource rootDirResource : rootDirResources) {
        rootDirResource = resolveRootDirResource(rootDirResource);
        URL rootDirURL = rootDirResource.getURL();
        if (equinoxResolveMethod !=null) {
            if (rootDirURL.getProtocol().startsWith("bundle")) {
                rootDirURL = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod,null, rootDirURL);
                rootDirResource = new UrlResource(rootDirURL);
            }
        }
        if (rootDirURL.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
            // is general JBoss VFS resource: "vfs" 
            result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirURL, subPattern, getPathMatcher()));
        }
        elseif (ResourceUtils.isJarURL(rootDirURL) || isJarResource(rootDirResource)) {
            // is zip, jar, wsjar or vfszip的一种就加载
            result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirURL, subPattern));
        }
        else {
            result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
        }
    }
    if (logger.isDebugEnabled()) {
        logger.debug("Resolved location pattern [" + locationPattern +"] to resources " + result);
    }
    return result.toArray(new Resource[result.size()]);
}

                 
总体来说:该函数把locationPattern拆分成两部分:rootDirPath 和subPattern,rootDirPath是根目录路径,subPattern是子目录路径匹配规则字符串。遍历根目录下的所有子目录、并得到所有的子目录在doFindPathMatchingFileResources(rootDirResource, subPattern)方法中,再根据子目录逐个逐个去匹配subPattern。

2.2 如果以classpath*开头且不包含?或者*,直接全名查找

例如查找: classpath*: applicationContext-test.xml,使用findAllClassPathResources函数,看下该函数:

protected Resource[] findAllClassPathResources(String location)throws IOException {
    String path = location;
    if (path.startsWith("/")) {
        path = path.substring(1);
    }
    Set result = doFindAllClassPathResources(path);
    if (logger.isDebugEnabled()) {
        logger.debug("Resolved classpath location [" + location +"] to resources " + result);
    }
    return result.toArray(new Resource[result.size()]);
}
                    
其实核心在doFindAllClassPathResources,该函数扫描该路径下的所有资料,其函数体如下:

protected Set doFindAllClassPathResources(String path) throws IOException {
    Set result= new LinkedHashSet<>(16);
    ClassLoader cl = getClassLoader();
    Enumeration resourceUrls = (cl!= null? cl.getResources(path) : ClassLoader.getSystemResources(path));
    while (resourceUrls.hasMoreElements()) {
        URL url = resourceUrls.nextElement();
        result.add(convertClassLoaderURL(url));
    }
    if ("".equals(path)) {
        // 如果路径为空的话,就找到所有jar包,加到result中(该函数不再深入)
        addAllClassLoaderJarRoots(cl, result);
    }
    return result;
}

                          
在该函数中重要:Enumeration resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path)); 即如果当前类加载器cl不为空的话,就调用cl.getResources(path),cl为空的话就使用系统类加载器去加载,事实上cl.getResources(path)内容使用的即是双亲委派模型(当前类加载器加载资源,如果存在父加载器,则用父加载器进行加载)。

2.3 如果不是以classpath*开头且包含?或者*

例如查找: classpath: applicationContext-*.xml,使用函数findPathMatchingResources,类似2.1情况。

2.4 如果不是以classpath*开头且不包含?或者*

例如查找: classpath: applicationContext-test.xml,直接“getResourceLoader().getResource(locationPattern)”,即直接使用当然的资源加载器去加载,这里默认使用的是DefaultResourceLoader()。

三、总结及测试

写了个小测试,测试代码如下:

private staticvoid output(String location)throws Exception {
    ResourcePatternResolver resourceLoader = new PathMatchingResourcePatternResolver();
    Resource[] source = resourceLoader.getResources(location);
//        System.out.println("source.size: " + source.length);
    for (int i =0; i < source.length; i++) {
        Resource resource = source[i];
        System.out.println(resource);
 
    }
}                 
很多个测试结果如下图:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值