Spring提供了一個工具類可以載入classpath下的文件,一般情況下無任何問題,但是當它作為公共的jar包中的工具來載入jar包中的文件時則報出找不到文件的錯誤. 點開看了一下這個工具類ResouceUtils.getFile()方法的源碼: 看了一下代碼結構簡單邏輯清晰,可能有問題的也就是上 ...
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()這種以流的形式讀取文件的方式.