1、spring之Resource加載

一、對資源的抽象

Spring把其資源做了一個抽象,底層使用統一的資源訪問接口來訪問Spring的所有資源。也就是說,不管什麼格式的文件,也不管文件在哪裏,到Spring 底層,都只有一個訪問接口,Resource。

1.1 類結構圖

在這裏插入圖片描述

1.2 類和接口分析

1、可以看到有四個比較重要的接口 InputStreamSource、Resource、WritableResource、ContextResource。
a 、InputStreamSource接口

public interface InputStreamSource {
	/**
	 * Return an {@link InputStream}.
	 * <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 IOException if the stream could not be opened
	 * @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource)
	 */
	InputStream getInputStream() throws IOException;
}

b、Resource接口
接口中定義了對於資源的判斷、對資源的獲取、對資源描述的獲取。通過該接口可以對資源進行有效的操作。但是Resource接口注重於對資源的讀取。

public interface Resource extends InputStreamSource {

	/**
	 * 判斷是否存在
	 */
	boolean exists();

	/**
	 * 判斷是否可讀
	 */
	boolean isReadable();

	/**
	 * Return whether this resource represents a handle with an open
	 * stream. If 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.
	 * 判斷流是否可以重複讀取,如果爲true的話表示不可以重複讀取,在讀取完成後需要關閉流
	 */
	boolean isOpen();

	/**
	 * 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
	 */
	URI getURI() throws IOException;

	/**
	 * Return a File handle for this resource.
	 * @throws IOException if the resource cannot be resolved as absolute
	 * file path, i.e. if the resource is not available in a file system
	 */
	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.
	 */
	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();

}

C、WritableResource
因爲Resource接口主要是注重對資源的讀取,當我們對資源進行寫入的時候,需要獲取對應的判斷和輸出流。WritableResource接口主要定義了對寫入的支持。

public interface WritableResource extends Resource {

	/**
	 * 返回資源是否可以被寫入
	 */
	boolean isWritable();

	/**
	 * 獲取資源的寫入流
	 */
	OutputStream getOutputStream() throws IOException;

}

D、ContextResource
有些資源是相對於當前的容器的,用來獲取容器中的資源。

public interface ContextResource extends Resource {

	/**
	 * Return the path within the enclosing 'context'.
	 * <p>This is typically path relative to a context-specific root directory,
	 * e.g. a ServletContext root or a PortletContext root.
	 */
	String getPathWithinContext();

}

2、存在一個AbstractResource的抽象類,所有的對於資源獲取都繼承自AbstractResource抽象類。

3、其餘的都是具體的實現類
用來加載指定的資源

二、對資源的加載

Spring框架爲了更方便的獲取資源,儘量弱化程序員對各個Resource接口的實現類的感知,定義了另一個ResourceLoader接口。 接口有一個特別重要的方法:Resource getResource(String location),返回Resource實例。因此程序員在使用Spring容器時,可以不去過於計較底層Resource的實現,也不需要自己創建Resource實現類,而是直接使用ReourceLoader,獲取到bean容器本身的Resource,進而取到相關的資源信息。

2.1 類繼承圖

在這裏插入圖片描述

2.2 類和接口分析

  1. 接口ResourceLoader和ResourcePatternResolver
    a、ResourceLoader接口
    只能對classpath路徑下面的資源進行加載,並且只會加載指定的文件的
public interface ResourceLoader {

	/** Pseudo URL prefix for loading from the class path: "classpath:" */
	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;


	/**
	 * 用來根據location來獲取對應的資源
	 */
	Resource getResource(String location);

	/**
	 * 獲取類加載器
	 */
	ClassLoader getClassLoader();

}

b、ResourcePatternResolver接口
表示會加載所有路徑下面的文件,包括jar包中的文件。同時locationPattern可以設置爲表達式來加載對應的文件。

public interface ResourcePatternResolver extends ResourceLoader {

	/**
	 * 表示會加載所有路徑下面的文件,包括jar包中
	 */
	String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

	/**
	 * 根據
	 */
	Resource[] getResources(String locationPattern) throws IOException;

}

區別
classpath: :表示從類路徑中加載資源,classpath:和classpath:/是等價的,都是相對於類的根路徑。資源文件庫標準的在文件系統中,也可以在JAR或ZIP的類包中。

classpath:*:假設多個JAR包或文件系統類路徑都有一個相同的配置文件,classpath:只會在第一個加載的類路徑下查找,而classpath*:會掃描所有這些JAR包及類路徑下出現的同名文件。

  1. DefaultResourceLoader
    spring實現的默認的加載器,一般其他的加載器會繼承該類,並重寫getResourceByPath方法
public Resource getResource(String location) {
		Assert.notNull(location, "Location must not be null");
		// 以/開頭,那麼根據path去查找
		if (location.startsWith("/")) {
			return getResourceByPath(location);
		}
		// 以classpath開頭,那麼抽象爲ClassPathResource
		else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
			return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
		}
		else {
			try {
				// 其他情況採用UrlResource來進行加載
				URL url = new URL(location);
				return new UrlResource(url);
			}
			catch (MalformedURLException ex) {
				// No URL -> resolve as resource path.
				return getResourceByPath(location);
			}
		}
	}
  1. PathMatchingResourcePatternResolver
    Spring提供了一個ResourcePatternResolver實現PathMatchingResourcePatternResolver,它是基於模式匹配的,默認使用AntPathMatcher進行路徑匹配,它除了支持ResourceLoader支持的前綴外,還額外支持“classpath*:”用於加載所有匹配的類路徑Resource,ResourceLoader不支持前綴“classpath*:”:

三、Resource的一些工具類

3.1 工具類截圖

在這裏插入圖片描述

3.2 詳解

1、EncodedResource
當您使用 Resource 實現類加載文件資源時,它默認採用操作系統的編碼格式。
如果文件資源採用了特殊的編碼格式(如 UTF-8),則在讀取資源內容時必須事先通過 EncodedResource 指定編碼格式,否則將會產生中文亂碼的問題。

public class EncodedResource {

	private final Resource resource;

	private final String encoding;

	private final Charset charset;

	/**
	 * 根據encoding和charset是否存在來判斷是否可以獲取Reader
	 */
	public boolean requiresReader() {
		return (this.encoding != null || this.charset != null);
	}

	/**
	 * 根據EncodedResource信息獲取Reader信息
	 */
	public Reader getReader() throws IOException {
		if (this.charset != null) {
			return new InputStreamReader(this.resource.getInputStream(), this.charset);
		}
		else if (this.encoding != null) {
			return new InputStreamReader(this.resource.getInputStream(), this.encoding);
		}
		else {
			return new InputStreamReader(this.resource.getInputStream());
		}
	}

	/**
	 * Open a {@code java.io.InputStream} for the specified resource, ignoring any
	 * specified {@link #getCharset() Charset} or {@linkplain #getEncoding() encoding}.
	 * @throws IOException if opening the InputStream failed
	 * @see #requiresReader()
	 * @see #getReader()
	 */
	public InputStream getInputStream() throws IOException {
		return this.resource.getInputStream();
	}

}

2、ResourcePatternUtils

/**
	 * Return whether the given resource location is a URL: either a
	 * special "classpath" or "classpath*" pseudo URL or a standard URL
	 */
	public static boolean isUrl(String resourceLocation) {
		return (resourceLocation != null &&
				(resourceLocation.startsWith(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX) ||
						ResourceUtils.isUrl(resourceLocation)));
	}

	/**
	 * 根據ResourceLoader構建一個ResourcePatternResolver
	 */
	public static ResourcePatternResolver getResourcePatternResolver(ResourceLoader resourceLoader) {
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");
		if (resourceLoader instanceof ResourcePatternResolver) {
			return (ResourcePatternResolver) resourceLoader;
		}
		else if (resourceLoader != null) {
			return new PathMatchingResourcePatternResolver(resourceLoader);
		}
		else {
			return new PathMatchingResourcePatternResolver();
		}
	}

3、PropertiesLoaderUtils
根據提供的Resource或者EncodedResource,將其中的內容轉換爲Property內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章