前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring入门:Resource

Spring入门:Resource

作者头像
WEBJ2EE
发布2019-08-14 20:25:22
8390
发布2019-08-14 20:25:22
举报
文章被收录于专栏:WebJ2EEWebJ2EE

1. Resource ?

Spring 把所有能记录信息的载体,如各种类型的文件、二进制流等都称为资源,对 Spring 开发者来说,最常用的资源就是 Spring 配置文件(通常是一份 XML 格式的文件)。 Spring 资源访问剖析和策略模式应用(李刚)

2. 为什么 Spring 要搞 Resource ?

一方面:增强 Java 原生的资源访问能力

Spring 需要与各式各样的资源打交道:

  • Url 资源(网络资源)
  • classpath 资源(类加载路径里的资源)
  • File 资源(文件系统)
  • SerlvetContext 资源(相对于 ServletContext 路径里的资源)
  • 自定义资源(开发通过 ByteArray、InputStream 自由构造)

然而,用 Java 的 File、URL 访问这些底层资源的步骤过于繁琐。Spring 为资源访问提供了一个 Resource 接口,该接口提供了更强的资源访问能力。

图:Resource 接口

另一方面:对资源进行统一抽象,屏蔽资源访问细节

Resource 接口就是策略模式的典型应用,应用只和 Resource 接口耦合,并不知道底层采用何种资源访问策略,这样应用可以在不同的资源访问策略之间自由切换。

Spring 为 Resource 接口提供了如下实现类:

  • UrlResource:用于访问网络资源
  • ClassPathResource:用于访问类加载路径中资源
  • FileSystemResource:用于访问文件系统中资源
  • ServletContextResource:用于 ServletContext 路径中的资源
  • InputStreamResource:用于自定义资源来源
  • ByteArrayResource:用于自定义资源来源

3. Resource 应用示例

Resource 不仅可在 Spring 的项目中使用,也可直接作为资源访问的工具类使用。意思是说:即使不使用 Spring 框架,也可以使用 Resource 作为工具类,用来代替 URL。当然,使用 Resource 接口会让代码与 Spring 的接口耦合在一起,但这种耦合只是部分工具集的耦合,不会造成太大的代码污染。

示例1:FileSystemResource

代码语言:javascript
复制
import org.apache.commons.io.IOUtils;import org.springframework.core.io.FileSystemResource;
import java.io.IOException;import java.util.Date;
public class ResourceDemo {    public static void main(String[] args) throws IOException {        FileSystemResource fsr = new FileSystemResource("E:/springdemo/src/main/resources/webj2ee.txt");        System.out.println("exists: "+fsr.exists());        System.out.println("readable: "+fsr.isReadable());        System.out.println("writable: "+fsr.isWritable());        System.out.println("path: "+fsr.getPath());        System.out.println("fileName: "+fsr.getFilename());        System.out.println("lastModified: "+new Date(fsr.lastModified()));        System.out.println("description: "+fsr.getDescription());        System.out.println("content: "+IOUtils.toString(fsr.getInputStream(), "UTF-8"));    }}

示例2:ClassPathResource

代码语言:javascript
复制
import org.apache.commons.io.IOUtils;import org.springframework.core.io.ClassPathResource;
import java.io.IOException;import java.util.Date;
public class ResourceDemo {    public static void main(String[] args) throws IOException {
        ClassPathResource fsr = new ClassPathResource("webj2ee/webj2ee.txt");        System.out.println("exists: "+fsr.exists());        System.out.println("readable: "+fsr.isReadable());        System.out.println("isFile: "+fsr.isFile());        System.out.println("path: "+fsr.getPath());        System.out.println("fileName: "+fsr.getFilename());        System.out.println("lastModified: "+new Date(fsr.lastModified()));        System.out.println("description: "+fsr.getDescription());        System.out.println("content: "+IOUtils.toString(fsr.getInputStream(), "UTF-8"));    }}

示例3:ServletContextResource

代码语言:javascript
复制
package webj2ee;
import org.apache.commons.io.IOUtils;import org.springframework.web.context.support.ServletContextResource;
import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.Date;
@WebServlet(name = "SCRDemo",urlPatterns = {"/SCRDemo"})public class SCRDemo extends HttpServlet {    @Override    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        ServletContextResource fsr = new ServletContextResource(request.getServletContext(), "webj2ee-servletcontext/webj2ee.txt");        System.out.println("exists: " + fsr.exists());        System.out.println("readable: " + fsr.isReadable());        System.out.println("path: " + fsr.getPath());        System.out.println("URL: "+fsr.getURL());        System.out.println("URI: "+fsr.getURI());        System.out.println("fileName: " + fsr.getFilename());        System.out.println("lastModified: " + new Date(fsr.lastModified()));        System.out.println("description: " + fsr.getDescription());        System.out.println("content: " + IOUtils.toString(fsr.getInputStream(), "UTF-8"));    }}

总结:Spring 提供的各种 Resource 实现类非常实用,避免了直接使用 URL、File 的繁琐、复杂,建议在项目中直接使用。

4. Resource 基本原理

Resource 的各实现类

涉及IO、NIO、JAR、File、Classpath 众多技术细节

留待后续文章详细分析

Resource 总体架构:

InputStreamSource.java 源码:

代码语言:javascript
复制
package org.springframework.core.io;
import java.io.IOException;import java.io.InputStream;
/** * Simple interface for objects that are sources for an {@link InputStream}. * * <p>This is the base interface for Spring's more extensive {@link Resource} interface. * * <p>For single-use streams, {@link InputStreamResource} can be used for any * given {@code InputStream}. Spring's {@link ByteArrayResource} or any * file-based {@code Resource} implementation can be used as a concrete * instance, allowing one to read the underlying content stream multiple times. * This makes this interface useful as an abstract content source for mail * attachments, for example. */public interface InputStreamSource {  /**   * Return an {@link InputStream} for the content of an underlying resource.   * <p>It is expected that each call creates a <i>fresh</i> stream.   * <p>This requirement is particularly important when you consider an API such   * as JavaMail, which needs to be able to read the stream multiple times when   * creating mail attachments. For such a use case, it is <i>required</i>   * that each {@code getInputStream()} call returns a fresh stream.   * @return the input stream for the underlying resource (must not be {@code null})   * @throws java.io.FileNotFoundException if the underlying resource doesn't exist   * @throws IOException if the content stream could not be opened   */  InputStream getInputStream() throws IOException;}

Resource.java 源码节选:

代码语言:javascript
复制
package org.springframework.core.io;
import java.io.File;import java.io.IOException;import java.io.InputStream;import java.net.URI;import java.net.URL;import java.nio.channels.Channels;import java.nio.channels.ReadableByteChannel;
import org.springframework.lang.Nullable;
/** * Interface for a resource descriptor that abstracts from the actual * type of underlying resource, such as a file or class path resource. * * <p>An InputStream can be opened for every resource if it exists in * physical form, but a URL or File handle can just be returned for * certain resources. The actual behavior is implementation-specific. */public interface Resource extends InputStreamSource {
  /**   * Determine whether this resource actually exists in physical form.   * <p>This method performs a definitive existence check, whereas the   * existence of a {@code Resource} handle only guarantees a valid   * descriptor handle.   */  boolean exists();
  /**   * Indicate whether non-empty contents of this resource can be read via   * {@link #getInputStream()}.   * <p>Will be {@code true} for typical resource descriptors that exist   * since it strictly implies {@link #exists()} semantics as of 5.1.   * Note that actual content reading may still fail when attempted.   * However, a value of {@code false} is a definitive indication   * that the resource content cannot be read.   * @see #getInputStream()   * @see #exists()   */  default boolean isReadable() {    return exists();  }
  /**   * Indicate whether this resource represents a handle with an open stream.   * If {@code true}, the InputStream cannot be read multiple times,   * and must be read and closed to avoid resource leaks.   * <p>Will be {@code false} for typical resource descriptors.   */  default boolean isOpen() {    return false;  }
  /**   * Determine whether this resource represents a file in a file system.   * A value of {@code true} strongly suggests (but does not guarantee)   * that a {@link #getFile()} call will succeed.   * <p>This is conservatively {@code false} by default.   * @since 5.0   * @see #getFile()   */  default boolean isFile() {    return false;  }
  /**   * Return a URL handle for this resource.   * @throws IOException if the resource cannot be resolved as URL,   * i.e. if the resource is not available as descriptor   */  URL getURL() throws IOException;
  /**   * Return a URI handle for this resource.   * @throws IOException if the resource cannot be resolved as URI,   * i.e. if the resource is not available as descriptor   * @since 2.5   */  URI getURI() throws IOException;
  /**   * Return a File handle for this resource.   * @throws java.io.FileNotFoundException if the resource cannot be resolved as   * absolute file path, i.e. if the resource is not available in a file system   * @throws IOException in case of general resolution/reading failures   * @see #getInputStream()   */  File getFile() throws IOException;  /**   * Determine the content length for this resource.   * @throws IOException if the resource cannot be resolved   * (in the file system or as some other known physical resource type)   */  long contentLength() throws IOException;
  /**   * Determine the last-modified timestamp for this resource.   * @throws IOException if the resource cannot be resolved   * (in the file system or as some other known physical resource type)   */  long lastModified() throws IOException;
  /**   * Create a resource relative to this resource.   * @param relativePath the relative path (relative to this resource)   * @return the resource handle for the relative resource   * @throws IOException if the relative resource cannot be determined   */  Resource createRelative(String relativePath) throws IOException;
  /**   * Determine a filename for this resource, i.e. typically the last   * part of the path: for example, "myfile.txt".   * <p>Returns {@code null} if this type of resource does not   * have a filename.   */  @Nullable  String getFilename();
  /**   * Return a description for this resource,   * to be used for error output when working with the resource.   * <p>Implementations are also encouraged to return this value   * from their {@code toString} method.   * @see Object#toString()   */  String getDescription();}

AbstractResource.java 源码节选:

代码语言:javascript
复制
package org.springframework.core.io;
import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.net.URI;import java.net.URISyntaxException;import java.net.URL;import java.nio.channels.Channels;import java.nio.channels.ReadableByteChannel;
import org.springframework.core.NestedIOException;import org.springframework.lang.Nullable;import org.springframework.util.ResourceUtils;
/** * Convenience base class for {@link Resource} implementations, * pre-implementing typical behavior. * * <p>The "exists" method will check whether a File or InputStream can * be opened; "isOpen" will always return false; "getURL" and "getFile" * throw an exception; and "toString" will return the description. * * @author Juergen Hoeller * @since 28.12.2003 */public abstract class AbstractResource implements Resource {
  /**   * This implementation checks whether a File can be opened,   * falling back to whether an InputStream can be opened.   * This will cover both directories and content resources.   */  @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 {        getInputStream().close();        return true;      }      catch (Throwable isEx) {        return false;      }    }  }
  /**   * This implementation always returns {@code true} for a resource   * that {@link #exists() exists} (revised as of 5.1).   */  @Override  public boolean isReadable() {    return exists();  }
  /**   * This implementation always returns {@code false}.   */  @Override  public boolean isOpen() {    return false;  }
  /**   * This implementation always returns {@code false}.   */  @Override  public boolean isFile() {    return false;  }
  /**   * This implementation throws a FileNotFoundException, assuming   * that the resource cannot be resolved to a URL.   */  @Override  public URL getURL() throws IOException {    throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");  }
  /**   * This implementation builds a URI based on the URL returned   * by {@link #getURL()}.   */  @Override  public URI getURI() throws IOException {    URL url = getURL();    try {      return ResourceUtils.toURI(url);    }    catch (URISyntaxException ex) {      throw new NestedIOException("Invalid URI [" + url + "]", ex);    }  }
  /**   * This implementation throws a FileNotFoundException, assuming   * that the resource cannot be resolved to an absolute file path.   */  @Override  public File getFile() throws IOException {    throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");  }
  /**   * This implementation returns {@link Channels#newChannel(InputStream)}   * with the result of {@link #getInputStream()}.   * <p>This is the same as in {@link Resource}'s corresponding default method   * but mirrored here for efficient JVM-level dispatching in a class hierarchy.   */  @Override  public ReadableByteChannel readableChannel() throws IOException {    return Channels.newChannel(getInputStream());  }
  /**   * This implementation reads the entire InputStream to calculate the   * content length. Subclasses will almost always be able to provide   * a more optimal version of this, e.g. checking a File length.   * @see #getInputStream()   */  @Override  public long contentLength() throws IOException {    InputStream is = getInputStream();    try {      long size = 0;      byte[] buf = new byte[256];      int read;      while ((read = is.read(buf)) != -1) {        size += read;      }      return size;    }    finally {      try {        is.close();      }      catch (IOException ex) {      }    }  }
  /**   * This implementation checks the timestamp of the underlying File,   * if available.   * @see #getFileForLastModifiedCheck()   */  @Override  public long lastModified() throws IOException {    File fileToCheck = getFileForLastModifiedCheck();    long lastModified = fileToCheck.lastModified();    if (lastModified == 0L && !fileToCheck.exists()) {      throw new FileNotFoundException(getDescription() +          " cannot be resolved in the file system for checking its last-modified timestamp");    }    return lastModified;  }
  /**   * Determine the File to use for timestamp checking.   * <p>The default implementation delegates to {@link #getFile()}.   * @return the File to use for timestamp checking (never {@code null})   * @throws FileNotFoundException if the resource cannot be resolved as   * an absolute file path, i.e. is not available in a file system   * @throws IOException in case of general resolution/reading failures   */  protected File getFileForLastModifiedCheck() throws IOException {    return getFile();  }
  /**   * This implementation throws a FileNotFoundException, assuming   * that relative resources cannot be created for this resource.   */  @Override  public Resource createRelative(String relativePath) throws IOException {    throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());  }
  /**   * This implementation always returns {@code null},   * assuming that this resource type does not have a filename.   */  @Override  @Nullable  public String getFilename() {    return null;  }

  /**   * This implementation compares description strings.   * @see #getDescription()   */  @Override  public boolean equals(Object other) {    return (this == other || (other instanceof Resource &&        ((Resource) other).getDescription().equals(getDescription())));  }
  /**   * This implementation returns the description's hash code.   * @see #getDescription()   */  @Override  public int hashCode() {    return getDescription().hashCode();  }
  /**   * This implementation returns the description of this resource.   * @see #getDescription()   */  @Override  public String toString() {    return getDescription();  }}

参考:

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#resources https://www.ibm.com/developerworks/cn/java/j-lo-spring-resource/index.html https://tools.ietf.org/html/rfc3986

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档