Spring源码解析(一):认识统一资源Resource

文章目录

        • 背景
        • 统一资源:Resource接口
        • Resource的子类
        • Resource的默认实现:AbstractResource
        • Resource的具体实现类
        • 后续

背景

JDK库有一个标准类 java.net.URL,该类在 Java SE 中的定位为统一资源定位器(Uniform Resource Locator)。

  • 该类将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源的资源的读取逻辑,一般handler的类型使用不同前缀(协议,Protocol)来识别,如“file:”、“http:”、“jar:”等
  • 但实际上资源的定义比较广泛,除了网络形式的资源,还有以二进制形式存在的、以文件形式存在的、以字节流形式存在的等等。而且它可以存在于任何场所,比如网络、文件系统、应用程序中。

统一资源:Resource接口

  • Spring对其内部使用到的资源实现了自己的抽象结构:使用Resource接口来封装底层资源:
  • 引入Resource接口是为了统一资源的定义,同时资源加载后要返回统一的资源抽象给客户端,客户端才能简化对这些资源的处理。
/**
 * org.springframework.core.io.InputStreamSource 
*/
public interface InputStreamSource {
	InputStream getInputStream() throws IOException;
}
/**
 * org.springframework.core.io.Resource
*/
public interface Resource extends InputStreamSource {
	// 是否存在
	boolean exists();
	// 是否可读
	default boolean isReadable() {
		return exists();
	}
	// 是否已经打开
	default boolean isOpen() {
		return false;
	}
	// 是否是文件
	default boolean isFile() {
		return false;
	}
	// 返回URL handle
	URL getURL() throws IOException;
	// 返回URI handle
	URI getURI() throws IOException;
	// 返回文件 handle
	File getFile() throws IOException;
	// 上次修改时间
	long lastModified() throws IOException;
	// 基于当前资源创建一个相对资源
	Resource createRelative(String relativePath) throws IOException;
	// 返回带路径信息的文件名
	String getFilename();
	// 返回资源描述信息
	String getDescription();
}

Resource的子类

Spring源码解析(一):认识统一资源Resource_第1张图片

  • Resource 根据资源的不同类型提供不同的具体实现
    • 文件(FileSystemResource)、
    • Classpath资源(ClassPathResource)、
    • URL资源(UrlResource)、
    • InputStream资源(InputStreamResource)、
    • Byte数组(ByteArrayResource)等

Resource的默认实现:AbstractResource

  • AbstractResource 是Resource 接口的默认实现抽象类。它实现了 Resource的大部分的接口方法。
/**
* org.springframework.core.io.AbstractResource
*/
public abstract class AbstractResource implements Resource {

	// 实现是否存在方法
	@Override
	public boolean exists() {
		try {
		  // 基于 File 进行判断
			return getFile().exists();
		}
		catch (IOException ex) {
			// 基于 InputStream 进行判断
			try {
				InputStream is = getInputStream();
				is.close();
				return true;
			} catch (Throwable isEx) {
				return false;
			}
		}
	}

	// 实现是否可读方法,直接返回true
	@Override
	public boolean isReadable() {
		return true;
	}

	// 实现是否打开方法,直接返回false
	@Override
	public boolean isOpen() {
		return false;
	}

	// 实现是否为文件方法,直接返回false
	@Override
	public boolean isFile() {
		return false;
	}

	// 抛出 FileNotFoundException 异常,交给具体子类实现
	@Override
	public URL getURL() throws IOException {
		throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");

	}

	// 基于 getURL() 返回的 URL 构建 URI
	@Override
	public URI getURI() throws IOException {
		URL url = getURL();
		try {
			return ResourceUtils.toURI(url);
		} catch (URISyntaxException ex) {
			throw new NestedIOException("Invalid URI [" + url + "]", ex);
		}
	}

	 // 抛出 FileNotFoundException 异常,交给具体子类实现
	@Override
	public File getFile() throws IOException {
		throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
	}

	// 获取资源的长度
	@Override
	public long contentLength() throws IOException {
		InputStream is = getInputStream();
		try {
			long size = 0;
			byte[] buf = new byte[255]; // 每次最多读取 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();
	}

	// 抛出 FileNotFoundException 异常,交给具体子类实现
	@Override
	public Resource createRelative(String relativePath) throws IOException {
		throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
	}

	// 获取文件名称,默认返回 null ,交给子类实现
	@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())));
	}
	// 重写hashcode方法
	@Override
	public int hashCode() {
		return getDescription().hashCode();
	}

}

Resource的具体实现类

  • 有了Resource接口便可以对所有资源文件进行统一处理。下面我们以ClassPathResource类及FileSystemResource类对getInputStream进行举例说明:
  • ClassPathResource中的实现方式便是通过class或者classLoader提供的底层方法进行调用
/**
* ClassPathResource.java
*/
	@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;
	}
  • FileSystemResource直接使用FileInputStream对文件进行实例化
/**
 * FileSystemResource.java
*/
@Override
	public InputStream getInputStream() throws IOException {
		try {
			return Files.newInputStream(this.filePath);
		}
		catch (NoSuchFileException ex) {
			throw new FileNotFoundException(ex.getMessage());
		}
	}

后续

  • 当我们认识统一资源Resource在Spring中抽象及实现后,我们就可以继续往下了解Spring 的XmlBeanDefinitionReader的初始化过程了,比如XmlBeanDefinitionReader使用Resource作为参数的办法:
/**
* org.springframework.beans.factory.xml.XmlBeanDefinitionReader
*/
@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

你可能感兴趣的:(spring,java)