spring-core io包Resource接口和WritableResource接口源码解析

一、接口关系图如下:

《spring-core io包Resource接口和WritableResource接口源码解析》
二、接口类功能描述和实现     

InputStreamSource接口是Resource接口的父接口,Resource接口表示一个可读的资源对象的公共方法,包含的方法如下图所示。其中readableChannel方法返回的是NIO里面的ReadableByteChannel对象,其中createRelative(String path)方法表示创建一个基于当前路径的相对路径的资源对象,参考具体实现的测试用例。

《spring-core io包Resource接口和WritableResource接口源码解析》

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接口继承自Resource接口,表示一个可写的资源,接口包含的方法如下:

《spring-core io包Resource接口和WritableResource接口源码解析》

PathResource继承自AbstractResource类并实现了WritableResource接口,是将文件路径或者URI转化成NIO的Path对象,借助NIO相关方法实现接口的。
FileSystemResource继承自AbstractResource类并实现了WritableResource接口,借助Path对象和File对象实现接口。
FileUrlResource继承自UrlResource并实现了WritableResource接口,借助Resource接口getFile()方法,通过File对象实现接口。 

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qq_31865983/article/details/82810086
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞