• ResouceUtils.getFile()取不到Jar中资源文件源码小结


    Spring提供了一个工具类可以加载classpath下的文件,一般情况下无任何问题,但是当它作为公共的jar包中的工具来加载jar包中的文件时则报出找不到文件的错误.

    点开看了一下这个工具类ResouceUtils.getFile()方法的源码:

    public static File getFile(String resourceLocation) throws FileNotFoundException {
            Assert.notNull(resourceLocation, "Resource location must not be null");
            if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
                String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
                String description = "class path resource [" + path + "]";
                ClassLoader cl = ClassUtils.getDefaultClassLoader();
                URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
                if (url == null) {
                    throw new FileNotFoundException(description +
                            " cannot be resolved to absolute file path because it does not exist");
                }
                return getFile(url, description);
            }
            try {
                // try URL
                return getFile(new URL(resourceLocation));
            }
            catch (MalformedURLException ex) {
                // no URL -> treat as file path
                return new File(resourceLocation);
            }
        }

    看了一下代码结构简单逻辑清晰,可能有问题的也就是上图标红的2处.这里我第一印象是类加载器加载资源的时候没加载到.Debug了一下cl.getResource(path)用的类加载器是

    WebAppClassLoader,想看一下内部实现,但是到这里就跟不进去了,然后百度了一下发现这个是Jetty实现的自己的ClassLoader,截取部分关键的加载源码:

    public void addJars(Resource lib)  {  
        if (lib.exists() && lib.isDirectory())  
        {  
            String[] files=lib.list();
            for (int f=0;files!=null && f<files.length;f++)  {  
                try {  
                    Resource fn=lib.addPath(files[f]);
                    String fnlc=fn.getName().toLowerCase();  
                    if (fnlc.endsWith(".jar") || fnlc.endsWith(".zip"))  {  
                        String jar=fn.toString();    
                        jar=StringUtil.replace(jar, ",", "%2C");  
                        jar=StringUtil.replace(jar, ";", "%3B");  
                        addClassPath(jar);  
                    }  
                }  catch (Exception ex) {  
                    Log.warn(Log.EXCEPTION,ex);  
                }  
            }  
        }  
    } 

    上面这块是把jar和zip的path加到类加载器路径中的部分源码.继续debug得到

    URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));

    上面的url结果:

    实际上取到了要加载的文件路径,由于在jar包中,协议字段被标识为jar.到这里看来并非类加载器导致的加载文件失败.那只好继续debug往下看getFile()的源码:

    public static File getFile(URL resourceUrl, String description) throws FileNotFoundException {
            Assert.notNull(resourceUrl, "Resource URL must not be null");
            if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) {  //URL_PROTOCOL_FILE="file"
                throw new FileNotFoundException(
                        description + " cannot be resolved to absolute file path " +
                        "because it does not reside in the file system: " + resourceUrl);
            }
            try {
                return new File(toURI(resourceUrl).getSchemeSpecificPart());
            }
            catch (URISyntaxException ex) {
                // Fallback for URLs that are not valid URIs (should hardly ever happen).
                return new File(resourceUrl.getFile());
            }
        }

    看到这里有点无语了,上面红色字体的部分实际判断资源路径的协议是否为file,由于在jar包中,协议是jar,故到此处直接抛出文件未找到的异常,

    顿时觉得自己好傻,debug这么久,又理了一遍类加载的过程,其实问题是很简单的一个问题,ResouceUtils.getFile()是专门用来加载非压缩和Jar包文件类型的资源,所以它根本不会

    去尝试加载Jar中的文件,要想加载Jar中的文件,只要用可以读取jar中文件的方式加载即可,比如 xx.class.getClassLoader().getResouceAsStream()这种以流的形式读取文件的方式.

  • 相关阅读:
    python核心编程2 第八章 练习
    python核心编程2 第六章 练习
    python核心编程2 第五章 练习
    Redis
    CENTOS7错误:Cannot find a valid baseurl for repo: base/7/x86_6
    HTTP协议
    计算机网络知识点
    好记性不如烂笔头~
    一些算法题
    解决mysql插入数据时出现Incorrect string value: 'xF0x9F...' 的异常
  • 原文地址:https://www.cnblogs.com/chyu/p/8407541.html
Copyright © 2020-2023  润新知