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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

一、接口关系图如下:

20191017100415\_1.png
二、接口类功能描述和实现

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

20191017100415\_2.png

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

20191017100415\_3.png

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


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring-core io包Resource接口和WritableResource接口源码解析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏