2023-09-12  阅读(1)
原文作者:一直不懂 原文地址: https://blog.csdn.net/shenchaohao12321/article/details/80236800

Spring的Resource接口为资源访问提供了统一的接口,不同的实现类实现了从不同上下文获取资源。下面是该接口的方法:

    public interface Resource extends InputStreamSource {
    	boolean exists();
    	default boolean isReadable() {
    		return true;
    	}
    	default boolean isOpen() {
    		return false;
    	}
    	default boolean isFile() {
    		return false;
    	}
    	URL getURL() throws IOException;
    	URI getURI() throws IOException;
    	File getFile() throws IOException;
    	default ReadableByteChannel readableChannel() throws IOException {
    		return Channels.newChannel(getInputStream());
    	}
    	long contentLength() throws IOException;
    	long lastModified() throws IOException;
    	Resource createRelative(String relativePath) throws IOException;
    	@Nullable
    	String getFilename();
    	String getDescription();
    }
    public interface InputStreamSource {
        InputStream getInputStream() throws IOException;
    }

202309122022488731.png

下面分析几个常用的Resource实现类。

1.AbstractResource提供了Resource的默认实现。

    public abstract class AbstractResource implements Resource {
       @Override
       public boolean exists() {
          try {
             return getFile().exists();
          }
          catch (IOException ex) {
             try {
                InputStream is = getInputStream();
                is.close();
                return true;
             }
             catch (Throwable isEx) {
                return false;
             }
          }
       }
       @Override
       public boolean isReadable() {
          return true;
       }
       @Override
       public boolean isOpen() {
          return false;
       }
       @Override
       public boolean isFile() {
          return false;
       }
       @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());
       }
       @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;
       }
       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();
       }
       @Override
       public boolean equals(Object obj) {
          return (obj == this ||
             (obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
       }
       @Override
       public int hashCode() {
          return getDescription().hashCode();
       }
    }

2.UrlResource 封装了一个 java.net.URL 对象,用来访问 URL 可以正常访问的任意对象,比如文件。

    public class UrlResource extends AbstractFileResolvingResource {
       @Nullable
       private final URI uri;
       private final URL url;
       private final URL cleanedUrl;
       public UrlResource(URI uri) throws MalformedURLException {
          Assert.notNull(uri, "URI must not be null");
          this.uri = uri;
          this.url = uri.toURL();
          this.cleanedUrl = getCleanedUrl(this.url, uri.toString());
       }
       public UrlResource(URL url) {
          Assert.notNull(url, "URL must not be null");
          this.url = url;
          this.cleanedUrl = getCleanedUrl(this.url, url.toString());
          this.uri = null;
       }
       public UrlResource(String path) throws MalformedURLException {
          Assert.notNull(path, "Path must not be null");
          this.uri = null;
          this.url = new URL(path);
          this.cleanedUrl = getCleanedUrl(this.url, path);
       }
       public UrlResource(String protocol, String location) throws MalformedURLException  {
          this(protocol, location, null);
       }
       public UrlResource(String protocol, String location, @Nullable String fragment) throws MalformedURLException  {
          try {
             this.uri = new URI(protocol, location, fragment);
             this.url = this.uri.toURL();
             this.cleanedUrl = getCleanedUrl(this.url, this.uri.toString());
          }
          catch (URISyntaxException ex) {
             MalformedURLException exToThrow = new MalformedURLException(ex.getMessage());
             exToThrow.initCause(ex);
             throw exToThrow;
          }
       }
       private URL getCleanedUrl(URL originalUrl, String originalPath) {
          try {
             return new URL(StringUtils.cleanPath(originalPath));
          }
          catch (MalformedURLException ex) {
             // Cleaned URL path cannot be converted to URL
             // -> take original URL.
             return originalUrl;
          }
       }
       @Override
       public InputStream getInputStream() throws IOException {
          URLConnection con = this.url.openConnection();
          ResourceUtils.useCachesIfNecessary(con);
          try {
             return con.getInputStream();
          }
          catch (IOException ex) {
             // Close the HTTP connection (if applicable).
             if (con instanceof HttpURLConnection) {
                ((HttpURLConnection) con).disconnect();
             }
             throw ex;
          }
       }
       @Override
       public URL getURL() {
          return this.url;
       }
       @Override
       public URI getURI() throws IOException {
          if (this.uri != null) {
             return this.uri;
          }
          else {
             return super.getURI();
          }
       }
       @Override
       public boolean isFile() {
          if (this.uri != null) {
             return super.isFile(this.uri);
          }
          else {
             return super.isFile();
          }
       }
       @Override
       public File getFile() throws IOException {
          if (this.uri != null) {
             return super.getFile(this.uri);
          }
          else {
             return super.getFile();
          }
       }
       @Override
       public Resource createRelative(String relativePath) throws MalformedURLException {
          if (relativePath.startsWith("/")) {
             relativePath = relativePath.substring(1);
          }
          return new UrlResource(new URL(this.url, relativePath));
       }
       @Override
       public String getFilename() {
          return StringUtils.getFilename(this.cleanedUrl.getPath());
       }
       @Override
       public String getDescription() {
          return "URL [" + this.url + "]";
       }
       @Override
       public boolean equals(Object obj) {
          return (obj == this ||
             (obj instanceof UrlResource && this.cleanedUrl.equals(((UrlResource) obj).cleanedUrl)));
       }
       @Override
       public int hashCode() {
          return this.cleanedUrl.hashCode();
       }
    }

3.可以使用 ClassPathResource 来获取类路径上的资源。ClassPathResource 可以使用线程上下文的加载器、调用者提供的加载器或指定的类中的任意一个来加载资源。

    public class ClassPathResource extends AbstractFileResolvingResource {
       private final String path;
       @Nullable
       private ClassLoader classLoader;
       @Nullable
       private Class<?> clazz;
       public ClassPathResource(String path) {
          this(path, (ClassLoader) null);
       }
       public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
          Assert.notNull(path, "Path must not be null");
          String pathToUse = StringUtils.cleanPath(path);
          if (pathToUse.startsWith("/")) {
             pathToUse = pathToUse.substring(1);
          }
          this.path = pathToUse;
          this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
       }
       public ClassPathResource(String path, @Nullable Class<?> clazz) {
          Assert.notNull(path, "Path must not be null");
          this.path = StringUtils.cleanPath(path);
          this.clazz = clazz;
       }
       @Deprecated
       protected ClassPathResource(String path, @Nullable ClassLoader classLoader, @Nullable Class<?> clazz) {
          this.path = StringUtils.cleanPath(path);
          this.classLoader = classLoader;
          this.clazz = clazz;
       }
       public final String getPath() {
          return this.path;
       }
       @Nullable
       public final ClassLoader getClassLoader() {
          return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader);
       }
       @Override
       public boolean exists() {
          return (resolveURL() != null);
       }
       @Nullable
       protected URL resolveURL() {
          if (this.clazz != null) {
             return this.clazz.getResource(this.path);
          }
          else if (this.classLoader != null) {
             return this.classLoader.getResource(this.path);
          }
          else {
             return ClassLoader.getSystemResource(this.path);
          }
       }
       @Override
       public InputStream getInputStream() throws IOException {
          InputStream is;
          if (this.clazz != null) {
             is = this.clazz.getResourceAsStream(this.path);
          }
          else if (this.classLoader != null) {
             is = this.classLoader.getResourceAsStream(this.path);
          }
          else {
             is = ClassLoader.getSystemResourceAsStream(this.path);
          }
          if (is == null) {
             throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
          }
          return is;
       }
       @Override
       public URL getURL() throws IOException {
          URL url = resolveURL();
          if (url == null) {
             throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
          }
          return url;
       }
       @Override
       public Resource createRelative(String relativePath) {
          String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
          return (this.clazz != null ? new ClassPathResource(pathToUse, this.clazz) :
                new ClassPathResource(pathToUse, this.classLoader));
       }
       @Override
       @Nullable
       public String getFilename() {
          return StringUtils.getFilename(this.path);
       }
       @Override
       public String getDescription() {
          StringBuilder builder = new StringBuilder("class path resource [");
          String pathToUse = path;
          if (this.clazz != null && !pathToUse.startsWith("/")) {
             builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
             builder.append('/');
          }
          if (pathToUse.startsWith("/")) {
             pathToUse = pathToUse.substring(1);
          }
          builder.append(pathToUse);
          builder.append(']');
          return builder.toString();
       }
       @Override
       public boolean equals(Object obj) {
          if (obj == this) {
             return true;
          }
          if (obj instanceof ClassPathResource) {
             ClassPathResource otherRes = (ClassPathResource) obj;
             return (this.path.equals(otherRes.path) &&
                   ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) &&
                   ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz));
          }
          return false;
       }
       @Override
       public int hashCode() {
          return this.path.hashCode();
       }
    }

4.FileSystemResource是针对 java.io.File 提供的 Resource 实现。

    public class FileSystemResource extends AbstractResource implements WritableResource {
       private final File file;
       private final String path;
       public FileSystemResource(File file) {
          Assert.notNull(file, "File must not be null");
          this.file = file;
          this.path = StringUtils.cleanPath(file.getPath());
       }
       public FileSystemResource(String path) {
          Assert.notNull(path, "Path must not be null");
          this.file = new File(path);
          this.path = StringUtils.cleanPath(path);
       }
       public final String getPath() {
          return this.path;
       }
       @Override
       public boolean exists() {
          return this.file.exists();
       }
       @Override
       public boolean isReadable() {
          return (this.file.canRead() && !this.file.isDirectory());
       }
       @Override
       public InputStream getInputStream() throws IOException {
          try {
             return Files.newInputStream(this.file.toPath());
          }
          catch (NoSuchFileException ex) {
             throw new FileNotFoundException(ex.getMessage());
          }
       }
       @Override
       public boolean isWritable() {
          return (this.file.canWrite() && !this.file.isDirectory());
       }
       @Override
       public OutputStream getOutputStream() throws IOException {
          return Files.newOutputStream(this.file.toPath());
       }
       @Override
       public URL getURL() throws IOException {
          return this.file.toURI().toURL();
       }
       @Override
       public URI getURI() throws IOException {
          return this.file.toURI();
       }
       @Override
       public boolean isFile() {
          return true;
       }
       @Override
       public File getFile() {
          return this.file;
       }
       @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);
       }
       @Override
       public long contentLength() throws IOException {
          return this.file.length();
       }
       @Override
       public Resource createRelative(String relativePath) {
          String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
          return new FileSystemResource(pathToUse);
       }
       @Override
       public String getFilename() {
          return this.file.getName();
       }
       @Override
       public String getDescription() {
          return "file [" + this.file.getAbsolutePath() + "]";
       }
       @Override
       public boolean equals(Object obj) {
          return (obj == this ||
             (obj instanceof FileSystemResource && this.path.equals(((FileSystemResource) obj).path)));
       }
       @Override
       public int hashCode() {
          return this.path.hashCode();
       }
    }

5.ServletContextResource为了获取 web 根路径的 ServletContext 资源而提供的 Resource 实现。

    public class ServletContextResource extends AbstractFileResolvingResource implements ContextResource {
       private final ServletContext servletContext;
       private final String path;
       public ServletContextResource(ServletContext servletContext, String path) {
          // check ServletContext
          Assert.notNull(servletContext, "Cannot resolve ServletContextResource without ServletContext");
          this.servletContext = servletContext;
    
          // check path
          Assert.notNull(path, "Path is required");
          String pathToUse = StringUtils.cleanPath(path);
          if (!pathToUse.startsWith("/")) {
             pathToUse = "/" + pathToUse;
          }
          this.path = pathToUse;
       }
       public final ServletContext getServletContext() {
          return this.servletContext;
       }
       public final String getPath() {
          return this.path;
       }
       @Override
       public boolean exists() {
          try {
             URL url = this.servletContext.getResource(this.path);
             return (url != null);
          }
          catch (MalformedURLException ex) {
             return false;
          }
       }
       @Override
       public boolean isReadable() {
          InputStream is = this.servletContext.getResourceAsStream(this.path);
          if (is != null) {
             try {
                is.close();
             }
             catch (IOException ex) {
                // ignore
             }
             return true;
          }
          else {
             return false;
          }
       }
       @Override
       public boolean isFile() {
          try {
             URL url = this.servletContext.getResource(this.path);
             if (url != null && ResourceUtils.isFileURL(url)) {
                return true;
             }
             else {
                return (this.servletContext.getRealPath(this.path) != null);
             }
          }
          catch (MalformedURLException ex) {
             return false;
          }
       }
       @Override
       public InputStream getInputStream() throws IOException {
          InputStream is = this.servletContext.getResourceAsStream(this.path);
          if (is == null) {
             throw new FileNotFoundException("Could not open " + getDescription());
          }
          return is;
       }
       @Override
       public URL getURL() throws IOException {
          URL url = this.servletContext.getResource(this.path);
          if (url == null) {
             throw new FileNotFoundException(
                   getDescription() + " cannot be resolved to URL because it does not exist");
          }
          return url;
       }
       @Override
       public File getFile() throws IOException {
          URL url = this.servletContext.getResource(this.path);
          if (url != null && ResourceUtils.isFileURL(url)) {
             // Proceed with file system resolution...
             return super.getFile();
          }
          else {
             String realPath = WebUtils.getRealPath(this.servletContext, this.path);
             return new File(realPath);
          }
       }
       @Override
       public Resource createRelative(String relativePath) {
          String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
          return new ServletContextResource(this.servletContext, pathToUse);
       }
       @Override
       @Nullable
       public String getFilename() {
          return StringUtils.getFilename(this.path);
       }
       @Override
       public String getDescription() {
          return "ServletContext resource [" + this.path + "]";
       }
       @Override
       public String getPathWithinContext() {
          return this.path;
       }
       @Override
       public boolean equals(Object obj) {
          if (obj == this) {
             return true;
          }
          if (obj instanceof ServletContextResource) {
             ServletContextResource otherRes = (ServletContextResource) obj;
             return (this.servletContext.equals(otherRes.servletContext) && this.path.equals(otherRes.path));
          }
          return false;
       }
       @Override
       public int hashCode() {
          return this.path.hashCode();
       }
    }

6.在确实没有找到其他合适的 Resource 实现时,才使用 InputSteamResource。

    public class InputStreamResource extends AbstractResource {
       private final InputStream inputStream;
       private final String description;
       private boolean read = false;
       public InputStreamResource(InputStream inputStream) {
          this(inputStream, "resource loaded through InputStream");
       }
       public InputStreamResource(InputStream inputStream, @Nullable String description) {
          Assert.notNull(inputStream, "InputStream must not be null");
          this.inputStream = inputStream;
          this.description = (description != null ? description : "");
       }
       @Override
       public boolean exists() {
          return true;
       }
       @Override
       public boolean isOpen() {
          return true;
       }
       @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;
       }
       @Override
       public String getDescription() {
          return "InputStream resource [" + this.description + "]";
       }
       @Override
       public boolean equals(Object obj) {
          return (obj == this ||
             (obj instanceof InputStreamResource && ((InputStreamResource) obj).inputStream.equals(this.inputStream)));
       }
       @Override
       public int hashCode() {
          return this.inputStream.hashCode();
       }
    }
  1. 当需要从字节数组加载内容时,ByteArrayResource 是一个不错的选择,使用 ByteArrayResource 可以不用求助于 InputStreamResource。
    public class ByteArrayResource extends AbstractResource {
       private final byte[] byteArray;
       private final String description;
       public ByteArrayResource(byte[] byteArray) {
          this(byteArray, "resource loaded from byte array");
       }
       public ByteArrayResource(byte[] byteArray, @Nullable String description) {
          Assert.notNull(byteArray, "Byte array must not be null");
          this.byteArray = byteArray;
          this.description = (description != null ? description : "");
       }
       public final byte[] getByteArray() {
          return this.byteArray;
       }
       @Override
       public boolean exists() {
          return true;
       }
       @Override
       public long contentLength() {
          return this.byteArray.length;
       }
       @Override
       public InputStream getInputStream() throws IOException {
          return new ByteArrayInputStream(this.byteArray);
       }
       @Override
       public String getDescription() {
          return "Byte array resource [" + this.description + "]";
       }
       @Override
       public boolean equals(Object obj) {
          return (obj == this ||
             (obj instanceof ByteArrayResource && Arrays.equals(((ByteArrayResource) obj).byteArray, this.byteArray)));
       }
       @Override
       public int hashCode() {
          return (byte[].class.hashCode() * 29 * this.byteArray.length);
       }
    }

Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文