Spring源码01-Resource&ResourceLoader

139 阅读4分钟

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

Resource

需要处理不同类型的外部资源(URL、File、ClassPath等等),而且处理这些资源步骤都是类似的(打开资源,读取资源,关闭资源)。Spring提供Resource接口来统一这些底层资源一致的访问,作为所有资源的统一抽象

Resource体系

Resource.png

Resource

public interface Resource extends InputStreamSource {

	/**
	 * 当前Resource资源是否存在
	 */
	boolean exists();

	/**
	 * 当前Resource是否可读
	 */
	default boolean isReadable() {
		return exists();
	}

	/**
	 * 当前Resource是否已经打开,如果返回true,则只能被读取一次然后关闭以避免资源泄露;
	 * 常见的Resource实现一般返回false
	 */
	default boolean isOpen() {
		return false;
	}

	/**
	 * 是否为文件资源
	 */
	default boolean isFile() {
		return false;
	}

	/**
	 * 如果当前Resource代表的资源能由java.util.URL代表,则返回该URL,否则抛出IOException
	 */
	URL getURL() throws IOException;

	/**
	 * 如果当前Resource代表的资源能由java.util.URI代表,则返回该URL,否则抛出IOException
	 */
	URI getURI() throws IOException;

	/**
	 * 如果当前Resource代表的底层资源能由java.io.File代表,则返回该File,否则抛出IOException
	 */
	File getFile() throws IOException;

	default ReadableByteChannel readableChannel() throws IOException {
		return Channels.newChannel(getInputStream());
	}

	/**
	 * 返回当前Resource代表的底层文件资源的长度,一般是值代表的文件资源的长度
	 */
	long contentLength() throws IOException;

	/**
	 * 返回当前Resource代表的底层资源的最后修改时间
	 */
	long lastModified() throws IOException;

	Resource createRelative(String relativePath) throws IOException;

	/**
	 * 返回当前Resource代表的底层文件资源的文件路径,比如File资源“file://d:/test.txt”将返回“d:/test.txt”;
	 * 而URL资源http://www.baidu.cn将返回null,因为只返回文件路径
	 */
	@Nullable
	String getFilename();

	/**
	 * 返回当前Resource代表的底层资源的描述符,通常就是资源的全路径(实际文件名或实际URL地址)
	 */
	String getDescription();
}
  • ByteArrayResource:对于字节数组的实现,为其构造一个ByteArrayInputStream;
  • ClassPathResource:ClassPath类型资源的实现,使用指定的Class或ClassLoader加载资源。用来访问类加载路径下的资源,该种类型在Web应用中可以自动搜索位于WEB-INF/classes下的资源文件,而无需使用绝对路径访问;
  • InputStreamResource:InputStream的实现,如果需要将资源描述符保存在某处,或者如果需要从流中多次读取,不要使用InputStreamResource;
  • UrlResource:对于java.net.URL类型资源的实现,支持File和URL的形式,既可以访问网络资源,也可以访问本地资源,加不同的前缀即可;
  • FileSystemResource:对java.io.File类型和java.nio.file.Path资源的封装,支持File和URL的形式。实现了WritableResource接口,支持对资源的写操作;
  • EncodedResource:实现对文件编码类型的处理。

它们都继承抽象类AbstractResource,AbstractResource实现了Resource接口。自定义Resource只需继承AbstractResource抽象类,它已经实现Resource接口的大部分公共实现,再根据自定义的资源特性覆盖相应的方法。

ResourceLoader

Resource定义统一的资源,ResourceLoader加载资源。

ResourceLoader体系

ResourceLoader.png

DefaultResourceLoader

public class DefaultResourceLoader implements ResourceLoader {
    ......
	@Override
	public Resource getResource(String location) {
		Assert.notNull(location, "Location must not be null");
		// 是否有自定义协议策略
		for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
			Resource resource = protocolResolver.resolve(location, this);
			if (resource != null) {
				return resource;
			}
		}
		// 以“/”开头,构造返回ClassPathContextResource
		if (location.startsWith("/")) {
			return getResourceByPath(location);
		}
		else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
			// 以"classpath:"开头,构造返回ClassPathResource
			return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
		}
		else {
			// 否则构造URL地址
			try {
				URL url = new URL(location);
				// 如果是FileURL,构造返回FileUrlResource,否则构造返回UrlResource
				return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
			}
			catch (MalformedURLException ex) {
				//构造URL失败(不符合URL规范),当作普通路径处理
				return getResourceByPath(location);
			}
		}
	}
    ......
}

ProtocolResolver

用户自定义协议资源解决策略,作为DefaultResourceLoader的SPI,允许用户自定义资源加载协议,而不需要继承ResourceLoader的子类。实现ProtocolResolver接口。

public class MyProtocolResolver implements ProtocolResolver {

	@Override
	public Resource resolve(String location,
			ResourceLoader resourceLoader) {
		System.out.println("自定义加载资源......");
		FileSystemResourceLoader fileSystemResourceLoader = new FileSystemResourceLoader();
		return fileSystemResourceLoader.getResource("D:\\IDEAProbject\\openSources\\spring-framework\\gradle\\docs.gradle");
	}
}

public class Demo {
	public static void main(String[] args) {
		DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
		resourceLoader.addProtocolResolver(new MyProtocolResolver());
		Resource resource = resourceLoader.getResource("/");
		System.out.println(resource.getFilename()); //docs.gradle
		System.out.println(resource.getDescription()); //file [D:\IDEAProbject\openSources\spring-framework\gradle\docs.gradle]
	}
}

ResourcePatternResolver——ResourceLoader接口的增强

public interface ResourcePatternResolver extends ResourceLoader {
	/**
	 * 支持classpath*:形式路径匹配,即Ant风格;允许使用通配符来对路径进行匹配。"classpath*:"可以返回路径下所有满足条件的资源实例
	 */
	String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

	/**
	 * 支持根据路径匹配模式返回多个 Resource 实例
	 */
	Resource[] getResources(String locationPattern) throws IOException;
}

PatchMatchingResourcePatternResolver

ResourcePatternResolver最常用的子类

public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
    // 负责对基于字符串的路径和指定的模式符号进行匹配
	private PathMatcher pathMatcher = new AntPathMatcher();
    /**
	 * 实例化一个DefaultResourceLoader,继承自ResourceLoader的方法委托给内部的DefaultResourceLoader实现
	 * PathMatchingResourcePatternResolver只负责处理实现ResourcePatternResolver中的方法
	 */
	public PathMatchingResourcePatternResolver() {
		this.resourceLoader = new DefaultResourceLoader();
	}

}

参考文章: blog.csdn.net/qq_42764725…

www.cnblogs.com/jthr/p/1590…