【小家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!
        }
    }


相关文章
|
1月前
|
XML 安全 Java
|
2月前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
70 1
|
15天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
2月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
42 1
|
2月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
38 1
|
3月前
|
缓存 JavaScript Java
Spring之FactoryBean的处理底层源码分析
本文介绍了Spring框架中FactoryBean的重要作用及其使用方法。通过一个简单的示例展示了如何通过FactoryBean返回一个User对象,并解释了在调用`getBean()`方法时,传入名称前添加`&`符号会改变返回对象类型的原因。进一步深入源码分析,详细说明了`getBean()`方法内部对FactoryBean的处理逻辑,解释了为何添加`&`符号会导致不同的行为。最后,通过具体代码片段展示了这一过程的关键步骤。
Spring之FactoryBean的处理底层源码分析
|
2月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
38 0
|
3月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
268 2
|
1天前
|
Java 测试技术 应用服务中间件
Spring Boot 如何测试打包部署
本文介绍了 Spring Boot 项目的开发、调试、打包及投产上线的全流程。主要内容包括: 1. **单元测试**:通过添加 `spring-boot-starter-test` 包,使用 `@RunWith(SpringRunner.class)` 和 `@SpringBootTest` 注解进行测试类开发。 2. **集成测试**:支持热部署,通过添加 `spring-boot-devtools` 实现代码修改后自动重启。 3. **投产上线**:提供两种部署方案,一是打包成 jar 包直接运行,二是打包成 war 包部署到 Tomcat 服务器。
23 10
|
15天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
下一篇
开通oss服务