【小家Spring】资源访问利器---Spring提供的Resource接口以及它的常用子类源码分析(中)

简介: 【小家Spring】资源访问利器---Spring提供的Resource接口以及它的常用子类源码分析(中)

子接口ContextResource和WritableResource


这两个接口继承于Resource,拥有Resource的全部方法。其中,ContextResource接口增加了一个方法:

  String getPathWithinContext(); //  返回上下文内的路径  


这个方法使得它的实现类有了返回当前上下文路径的能力。


WritableResource接口增加了2个方法:


    boolean isWritable();  //  是否可写
    OutputStream getOutputStream() throws IOException; //返回资源的写入流


这个方法使得它的实现类拥有了写资源的能力。


可以看到Spring为我们提供了非常多的实现类。


  • ByteArrayResource
  • InputStreamResource
  • FileSystemResource
  • UrlResource
  • ClassPathResource
  • ServletContextResource
  • VfsResource(这个和Jboss有关,本文不讨论)


抽象类AbstractResource


对于任何的接口而言,这个直接抽象类是重中之重,里面浓缩了接口的大部分公共实现,所以这里直接拿源码开刀:


public abstract class AbstractResource implements Resource {
  // File或者流  都从此处判断
  // 这里属于通用实现,子类大都会重写这个方法的~~~~~~
  @Override
  public boolean exists() {
    // Try file existence: can we find the file in the file system?
    try {
      return getFile().exists();
    } catch (IOException ex) {
      // Fall back to stream existence: can we open the stream?
      try {
        InputStream is = getInputStream();
        is.close();
        return true;
      } catch (Throwable isEx) {
        return false;
      }
    }
  }
  // 默认都是可读得。大多数子类都会复写
  @Override
  public boolean isReadable() {
    return true;
  }
  // 默认不是打开的。 比如InputStreamResource就会让他return true
  @Override
  public boolean isOpen() {
    return false;
  }
  // 默认不是一个File
  @Override
  public boolean isFile() {
    return false;
  }
  // 可议看到getURI方法一般都是依赖于getURL的
  @Override
  public URL getURL() throws IOException {
    throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
  }
  @Override
  public URI getURI() throws IOException {
    URL url = getURL();
    try {
      return ResourceUtils.toURI(url);
    } catch (URISyntaxException ex) {
      throw new NestedIOException("Invalid URI [" + url + "]", ex);
    }
  }
  @Override
  public File getFile() throws IOException {
    throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
  }
  @Override
  public ReadableByteChannel readableChannel() throws IOException {
    return Channels.newChannel(getInputStream());
  }
  // 调用此方法,也相当于吧流的read了一遍,请务必注意
  @Override
  public long contentLength() throws IOException {
    InputStream is = getInputStream();
    try {
      long size = 0;
      byte[] buf = new byte[255];
      int read;
      while ((read = is.read(buf)) != -1) {
        size += read;
      }
      return size;
    } finally {
      try {
        is.close();
      } catch (IOException ex) {
      }
    }
  }
  @Override
  public long lastModified() throws IOException {
    long lastModified = getFileForLastModifiedCheck().lastModified();
    if (lastModified == 0L) {
      throw new FileNotFoundException(getDescription() +
          " cannot be resolved in the file system for resolving its last-modified timestamp");
    }
    return lastModified;
  }
  // 只有一个子类:`AbstractFileResolvingResource`覆盖了此方法
  protected File getFileForLastModifiedCheck() throws IOException {
    return getFile();
  }
  @Override
  public Resource createRelative(String relativePath) throws IOException {
    throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
  }
  @Override
  @Nullable
  public String getFilename() {
    return null;
  }
  // 这些基础方法,很多子类也都有重写~~~~ 但是一般来说关系不大
  @Override
  public String toString() {
    return getDescription();
  }
  // 比较的就是getDescription()
  @Override
  public boolean equals(Object obj) {
    return (obj == this ||
      (obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
  }
  // getDescription()的hashCode
  @Override
  public int hashCode() {
    return getDescription().hashCode();
  }
}


那么接下来就以AbstractResource为主要分支,分析它的实现类们:

PathResource


它是基于@since 4.0,也是基于JDK7提供的java.nio.file.Path的。实现原理也非常的简单,更像是对java.nio.file.Path进行了包装(java.nio.file.Files)


另外它还实现了WritableResource,证明它拥有对资源写的能力~~~


ByteArrayResource:获取字节数组封装的资源


ByteArrayResource代表byte[]数组资源,对于“getInputStream”操作将返回一个ByteArrayInputStream。


// @since 1.2.3
public class ByteArrayResource extends AbstractResource {
  private final byte[] byteArray;
  private final String description;
  ... 
  @Override
  public InputStream getInputStream() throws IOException {
    return new ByteArrayInputStream(this.byteArray);
  }
}


它可多次读取数组资源,即isOpen()永远返回false

ByteArrayResource因为入参可以是byte[]类型,所以用途比较广泛,可以把从网络或者本地资源都转换为byte[]类型,然后用ByteArrayResource转化为资源


Demo:


    public static void main(String[] args) {
        Resource resource = new ByteArrayResource("Hello!Spring!你好!".getBytes());
        if (resource.exists()) {
            dumpStream(resource); //Hello!Spring!你好!
        }
    }
    // 这个其实可以把这个resource写到本地文件,本处就不麻烦了,直接sout输出看一看即可~~~~
    private static void dumpStream(Resource resource) {
        InputStream is = null;
        try {
            //1.获取文件资源
            is = resource.getInputStream();
            //2.读取资源
            byte[] descBytes = new byte[is.available()];
            is.read(descBytes);
            System.out.println(new String(descBytes));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //3.关闭资源
                is.close();
            } catch (IOException e) {
            }
        }
    }


TransformedResource是继承此类的一个扩展。在web中使用较多,实现非常简单,就是多了两个参数:filename和lastModified


FileSystemResource:通过文件系统获取资源


代表java.io.File资源,对于getInputStream操作将返回底层文件的字节流,isOpen将永远返回false,从而表示可多次读取底层文件的字节流。


这个实现类就大名鼎鼎了。此类在Spring5以后,就使用NIO.2的API比如ReadableByteChannel等来操作读写了。提高了效率。这点就和PathResource特别的像了


public class FileSystemResource extends AbstractResource implements WritableResource {
  private final File file;
  private final String path;
  ...
  // 使用了Files 基于JDK1.7了
  @Override
  public InputStream getInputStream() throws IOException {
    try {
      return Files.newInputStream(this.file.toPath());
    }
    catch (NoSuchFileException ex) {
      throw new FileNotFoundException(ex.getMessage());
    }
  }
  @Override
  public boolean isReadable() {
    return (this.file.canRead() && !this.file.isDirectory());
  }
  // 读写
  @Override
  public ReadableByteChannel readableChannel() throws IOException {
    try {
      return FileChannel.open(this.file.toPath(), StandardOpenOption.READ);
    }
    catch (NoSuchFileException ex) {
      throw new FileNotFoundException(ex.getMessage());
    }
  }
  @Override
  public WritableByteChannel writableChannel() throws IOException {
    return FileChannel.open(this.file.toPath(), StandardOpenOption.WRITE);
  }
}


它的工作主要是构建一个File对象出来,此处我就省略了~~~


InputStreamResource:获取输入流封装的资源


InputStreamResource代表java.io.InputStream字节流,对于“getInputStream ”操作将直接返回该字节流,因此只能读取一次该字节流,即“isOpen”永远返回true。

public class InputStreamResource extends AbstractResource {
  private final InputStream inputStream;
  private final String description;
  private boolean read = false;
  @Override
  public InputStream getInputStream() throws IOException, IllegalStateException {
    if (this.read) {
      throw new IllegalStateException("InputStream has already been read - " +
          "do not use InputStreamResource if a stream needs to be read multiple times");
    }
    this.read = true;
    return this.inputStream;
  }
}


Demo:


  // dumpStream方法请参照上文
    public static void main(String[] args) {
        ByteArrayInputStream bis = new ByteArrayInputStream("Hello World!".getBytes());
        Resource resource = new InputStreamResource(bis);
        if (resource.exists()) {
            dumpStream(resource); //Hello World!
        }
    }


相关文章
|
11月前
|
NoSQL Java 数据库连接
《深入理解Spring》Spring Data——数据访问的统一抽象与极致简化
Spring Data通过Repository抽象和方法名派生查询,简化数据访问层开发,告别冗余CRUD代码。支持JPA、MongoDB、Redis等多种存储,统一编程模型,提升开发效率与架构灵活性,是Java开发者必备利器。(238字)
|
XML Java 数据格式
探索Spring之利剑:ApplicationContext接口
本文深入介绍了Spring框架中的核心接口ApplicationContext,解释了其作为应用容器的功能,包括事件发布、国际化支持等,并通过基于XML和注解的配置示例展示了如何使用ApplicationContext管理Bean实例。
|
智能设计 Java 测试技术
Spring中最大化@Lazy注解,实现资源高效利用
本文深入探讨了 Spring 框架中的 `@Lazy` 注解,介绍了其在资源管理和性能优化中的作用。通过延迟初始化 Bean,`@Lazy` 可显著提升应用启动速度,合理利用系统资源,并增强对 Bean 生命周期的控制。文章还分析了 `@Lazy` 的工作机制、使用场景、最佳实践以及常见陷阱与解决方案,帮助开发者更高效地构建可扩展、高性能的 Spring 应用程序。
470 0
Spring中最大化@Lazy注解,实现资源高效利用
|
安全 数据可视化 Java
AiPy开发的 Spring 漏洞检测神器,未授权访问无所遁形
针对Spring站点未授权访问问题,现有工具难以检测如Swagger、Actuator等组件漏洞,且缺乏修复建议。全新AI工具基于Aipy开发,具备图形界面,支持一键扫描常见Spring组件,自动识别未授权访问风险,按漏洞类型标注并提供修复方案,扫描结果可视化展示,支持导出报告,大幅提升渗透测试与漏洞定位效率。
|
XML Java 数据格式
深入理解Spring中的Resource资源管理
深入理解Spring中的Resource资源管理
452 0
|
Java Spring 容器
Spring 资源管理 (Resource)
Spring 为什么引入资源管理? Java 中有各种各样的资源,资源的位置包括本地文件系统、网络、类路径等,资源的形式可以包括文件、二进制流、字节流等,针对不同的资源又有不同的加载形式。
292 0
Spring 资源管理 (Resource)
|
XML Java 数据格式
Spring IoC资源管理之ResourceLoader
Spring IoC资源管理之ResourceLoader
465 0
Spring IoC资源管理之ResourceLoader
|
XML 算法 Java
Spring IoC资源管理之Resource
Spring IoC资源管理之Resource
629 0
Spring IoC资源管理之Resource