spring core io包原始碼解析
阿新 • • 發佈:2018-12-11
一、
一、Resource介面和WritableResource介面
InputStreamSource介面是Resource介面的父介面,Resource介面表示一個可讀的資源物件的公共方法,包含的方法如下圖所示。其中readableChannel方法返回的是NIO裡面的ReadableByteChannel物件,其中createRelative(String path)方法表示建立一個基於當前路徑的相對路徑的資源物件,參考具體實現的測試用例。
AbstractResource是Resource介面的抽象實現類,提供了部分方法的預設實現,注意該類覆寫了equals(Object other)和hashCode()方法,依據getDescription()返回值判斷。 DescriptiveResource是一個Resource介面的空實現 ByteArrayResource表示位元組陣列資源 VfsResource表示Jboss VFS檔案的資源,通過VfsUtils實現,由於Jboss VFS相容性較差,在非jboss應用不推薦使用InputStreamResource 表示輸入流的的資源 AbstractFileResolvingResource 提供了網路或者本地檔案資源的抽象實現類,對網路檔案資源通過設定請求方法為HEAD來獲取檔案是否可以訪問,大小和最後一次修改時間 UrlResource繼承自AbstractFileResolvingResource,重點重寫了equals(Object other)方法和hashCode()方法,通過比較每個原始URL字串對應的cleanUrl判斷,該方法可以將windowns中的\\轉換成/,將../ 和 ./ 翻譯成實際的目錄。參考如下測試用例:
@Test public void testUrlResource3() throws IOException { System.out.println(StringUtils.cleanPath("file:org/springframework/core/../core/io/./Resource.class")); System.out.println(StringUtils.cleanPath("file:org/springframework/core/../asm/./Edge.class")); System.out.println(StringUtils.cleanPath("file:org/springframework/core/../../../asm/./Edge.class")); System.out.println(StringUtils.cleanPath("file:\\dir\\test.txt?argh")); }
該用例的結果如下:
file:org/springframework/core/io/Resource.class
file:org/springframework/asm/Edge.class
file:asm/Edge.class
file:/dir/test.txt?argh
該方法的原始碼說明:
public static String cleanPath(String path) { if (!hasLength(path)) { return path; } //替換windows中的\\ String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR); int prefixIndex = pathToUse.indexOf(':'); String prefix = ""; if (prefixIndex != -1) { prefix = pathToUse.substring(0, prefixIndex + 1); if (prefix.contains(FOLDER_SEPARATOR)) { prefix = ""; } else { pathToUse = pathToUse.substring(prefixIndex + 1); } } if (pathToUse.startsWith(FOLDER_SEPARATOR)) { prefix = prefix + FOLDER_SEPARATOR; pathToUse = pathToUse.substring(1); } // 按路徑分隔符/拆分成字串組數 String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR); // 採用LinkedList是因為該List實現支援更靈活的插入和刪除某個index的元素的操作 LinkedList<String> pathElements = new LinkedList<>(); int tops = 0; //此處從陣列中最後一個元素開始遍歷 for (int i = pathArray.length - 1; i >= 0; i--) { String element = pathArray[i]; //當前路徑元素./ if (CURRENT_PATH.equals(element)) { // Points to current directory - drop it. } //上一級路徑元素 ../ else if (TOP_PATH.equals(element)) { // Registering top path found. tops++; } else { if (tops > 0) { // Merging path element with element corresponding to top path. //上一個元素是上一級路徑,則該元素被合併了 tops--; } else { // Normal path element found. //每次新增都是新增到index為0的位置 pathElements.add(0, element); } } } // Remaining top paths need to be retained. for (int i = 0; i < tops; i++) { pathElements.add(0, TOP_PATH); } //個人提供的另一種更簡潔的實現,正向遍歷 // for (int i = 0; i <pathArray.length;i++) { // String element = pathArray[i]; //如果是上一級元素且元素陣列中最後一個元素不是上一級元素,則移除該元素 // if (TOP_PATH.equals(element) && !pathElements.getLast().equals(TOP_PATH)) { // pathElements.removeLast(); // }else if(!element.equals(CURRENT_PATH)){ // pathElements.addLast(element); // } // } // If nothing else left, at least explicitly point to current path. if (pathElements.size() == 1 && "".equals(pathElements.getLast()) && !prefix.endsWith(FOLDER_SEPARATOR)) { pathElements.add(0, CURRENT_PATH); } //將字首和路徑元素合併 return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR); }
ClassPathResource類繼承自AbstractFileResolvingResource抽象類,提供了基於Class物件和ClassLoader物件獲取資源的方法,都不為空的情況下優先使用Class物件獲取資源,兩者都為空則使用SystemClassLoader獲取資源,需要注意基於Class獲取資源時如果沒有帶/是基於該Class所在的類路徑,如果帶/是基於應用的根類路徑,基於ClassLoader獲取資源不能帶/,只能基於應用的根類路徑,參考如下測試用例,test.txt檔案在ResourceTests.java同目錄下有一個,在resources資料夾下也有一個。
@Test
public void testClassPath() throws IOException {
System.out.println(ResourceTests.class.getResource("test.txt"));
System.out.println(ResourceTests.class.getResource("/test.txt"));
System.out.println(ResourceTests.class.getClassLoader().getResource("test.txt"));
System.out.println(ResourceTests.class.getClassLoader().getResource("/test.txt"));
}
該用例的輸出如下:
file:/D:/git/spring-framework/spring-core/out/test/classes/org/springframework/core/io/test.txt
file:/D:/git/spring-framework/spring-core/out/test/resources/test.txt
file:/D:/git/spring-framework/spring-core/out/test/resources/test.txt
null
二、WritableResource介面
WritableResource介面繼承自Resource介面,表示一個可寫的資源。
PathResource繼承自AbstractResource類並實現了WritableResource介面,是將檔案路徑或者URI轉化成NIO的Path物件,藉助NIO相關方法實現介面的。
FileSystemResource繼承自AbstractResource類並實現了WritableResource介面,藉助Path物件和File物件實現介面。
FileUrlResource繼承自UrlResource並實現了WritableResource介面,藉助Resource介面getFile()方法,通過File物件實現介面。