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()这种以流的形式读取文件的方式.

 

posted on 2018-02-03 00:06 蓝萝卜blu 阅读(...) 评论(...) 编辑 收藏

导航

公告