赞
踩
参考文章:
- public interface Resource extends InputStreamSource {
-
- /**
- * 资源是否存在
- */
- boolean exists();
-
- /**
- * 资源是否可读
- */
- default boolean isReadable() {
- return exists();
- }
-
- /**
- * 是否打开
- */
- default boolean isOpen() {
- return false;
- }
-
- /**
- * 是否是文件
- */
- default boolean isFile() {
- return false;
- }
-
- /**
- * 返回资源的URL地址
- */
- URL getURL() throws IOException;
-
- /**
- * 返回资源的URI地址
- */
- URI getURI() throws IOException;
-
- /**
- * 获得资源的文件标识
- */
- File getFile() throws IOException;
-
- /**
- * byte读取通道
- */
- 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();
-
- }
org.springframework.core.io.AbstractResource
,为 Resource 接口的默认抽象实现。它实现了 Resource 接口的大部分的公共实现,自定义资源的时候,最好是继承AbstractResource而不是实现Resource
实现类
java.io.File
类型资源的封装,只要是跟 File 打交道的,基本上与 FileSystemResource 也可以打交道。支持文件和 URL 的形式,实现 WritableResource 接口,且从 Spring Framework 5.0 开始,FileSystemResource 使用 NIO2 API进行读/写交互。java.net.URL
类型资源的封装。内部委派 URL 进行具体的资源操作。
- public interface ResourceLoader {
-
- /** Pseudo URL prefix for loading from the class path: "classpath:". */
- // CLASSPATH URL 前缀。默认为:"classpath:"
- String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
-
- //根据所提供资源的路径 location 返回 Resource 实例
- Resource getResource(String location);
- @Nullable
- ClassLoader getClassLoader();
-
- }
org.springframework.core.io.DefaultResourceLoader
是 ResourceLoader 的默认实现#getResourceByPath(String)
方法,使之从文件系统加载资源并以 FileSystemResource 类型返回。#getResourceByPath(String path)
方法,并返回其对应的 ClassRelativeContextResource 的资源类型"classpath*:"
前缀外,还支持 Ant 风格的路径匹配模式
org.springframework.core.io.DefaultResourceLoader
是 ResourceLoader 的默认实现
构造函数
- /**
- * 使用的 ClassLoader 为默认的 ClassLoader(一般 Thread.currentThread()#getContextClassLoader() )
- */
- public DefaultResourceLoader() {
- this.classLoader = ClassUtils.getDefaultClassLoader();
- }
-
- /**
- * 在使用带参数的构造函数时,可以通过 ClassUtils#getDefaultClassLoader()获取。
- */
- public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
- this.classLoader = classLoader;
- }
getResource 方法
ResourceLoader 中最核心的方法为
#getResource(String location)
,它根据提供的 location 返回相应的 Resource 。而 DefaultResourceLoader 对该方法提供了核心实现
- @Override
- public Resource getResource(String location) {
- Assert.notNull(location, "Location must not be null");
-
- // 首先,通过 ProtocolResolver 来加载资源
- for (ProtocolResolver protocolResolver : this.protocolResolvers) {
- Resource resource = protocolResolver.resolve(location, this);
- if (resource != null) {
- return resource;
- }
- }
- // 其次,以 / 开头,返回 ClassPathContextResource 类型的资源
- 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());
- }
- // 然后,根据是否为文件 URL ,是则返回 FileUrlResource 类型的资源,否则返回 UrlResource 类型的资源
- else {
- try {
- // Try to parse the location as a URL...
- URL url = new URL(location);
- return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
- }
- catch (MalformedURLException ex) {
- // 最后,返回 ClassPathContextResource 类型的资源
- // No URL -> resolve as resource path.
- // location.startsWith("/") 其实又回到最初的判断了
- return getResourceByPath(location);
- }
- }
- }
ProtocolResolver
用户自定义协议资源解决策略,作为 DefaultResourceLoader 的 SPI:它允许用户自定义资源加载协议,而不需要继承 ResourceLoader 的子类
ProtocolResolver 接口,仅有一个方法 Resource resolve(String location, ResourceLoader resourceLoader)
- @Nullable
- Resource resolve(String location, ResourceLoader resourceLoader);
它继承 DefaultResourceLoader ,且覆写了
#getResourceByPath(String)
方法,使之从文件系统加载资源并以 FileSystemResource 类型返回,这样我们就可以得到想要的资源类型
- @Override
- protected Resource getResourceByPath(String path) {
- //截取第一个/分界线
- if (path.startsWith("/")) {
- path = path.substring(1);
- }
- 创建 FileSystemContextResource 类型的资源
- return new FileSystemContextResource(path);
- }
FileSystemContextResource
- /**
- * 实现 ContextResource 接口,并实现对应的 #getPathWithinContext() 接口方法
- */
- private static class FileSystemContextResource extends FileSystemResource implements ContextResource {
-
- public FileSystemContextResource(String path) {
- super(path);
- }
-
- @Override
- public String getPathWithinContext() {
- return getPath();
- }
- }
DefaultResourceLoader 的另一个子类的实现。和 FileSystemResourceLoader 类似,在实现代码的结构上类似,也是覆写
#getResourceByPath(String path)
方法,并返回其对应的 ClassRelativeContextResource 的资源类型ClassRelativeResourceLoader 扩展的功能是,可以根据给定的
class
所在包或者所在包的子包下加载资源。
ResourceLoader 的扩展,它支持根据指定的资源路径匹配模式每次返回多个 Resource 实例
- public interface ResourcePatternResolver extends ResourceLoader {
-
- /**
- * 新的路径前缀
- */
- String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
-
- /**
- * ResourcePatternResolver 在 ResourceLoader 的基础上增加了 #getResources(String locationPattern) 方法,
- * 以支持根据路径匹配模式返回多个 Resource 实例
- * @param locationPattern
- * @return
- * @throws IOException
- */
- Resource[] getResources(String locationPattern) throws IOException;
-
- }
除了支持 ResourceLoader 和 ResourcePatternResolver 新增的
"classpath*:"
前缀外,还支持 Ant 风格的路径匹配模式
构造函数
- /**
- * 可以指定一个 ResourceLoader,如果不指定的话,它会在内部构造一个 DefaultResourceLoader
- */
- private final ResourceLoader resourceLoader;
-
- /**
- * Ant 路径匹配器
- */
- private PathMatcher pathMatcher = new AntPathMatcher();
-
-
- /**
- * 使用默认资源加载
- */
- public PathMatchingResourcePatternResolver() {
- this.resourceLoader = new DefaultResourceLoader();
- }
-
- /**
- * 资源加载器
- */
- public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
- Assert.notNull(resourceLoader, "ResourceLoader must not be null");
- this.resourceLoader = resourceLoader;
- }
-
- /**
- * 类加载器
- */
- public PathMatchingResourcePatternResolver(@Nullable ClassLoader classLoader) {
- this.resourceLoader = new DefaultResourceLoader(classLoader);
- }
getResource 方法
- @Override
- public Resource getResource(String location) {
- return getResourceLoader().getResource(location);
- }
getResources 方法
- @Override
- public Resource[] getResources(String locationPattern) throws IOException {
- Assert.notNull(locationPattern, "Location pattern must not be null");
- //以classpath*: 开头
- if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
- // 路径包含通配符
- // a class path resource (multiple resources for same name possible)
- if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
- // a class path resource pattern
- return findPathMatchingResources(locationPattern);
- }
- // 路径不包含通配符
- else {
- // all class path resources with the given name
- return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
- }
- }
- // 不以 "classpath*:" 开头
- else {
- // 通常只在这里的前缀后面查找模式
- // Generally only look for a pattern after a prefix here,
- // Tomcat 上只有在 “*/ ”分隔符之后才为其 “war:” 协议
- // and on Tomcat only after the "*/" separator for its "war:" protocol.
- int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
- locationPattern.indexOf(':') + 1);
- // 路径包含通配符
- if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
- // a file pattern
- return findPathMatchingResources(locationPattern);
- }
- // 路径不包含通配符
- else {
- // a single resource with the given name
- return new Resource[] {getResourceLoader().getResource(locationPattern)};
- }
- }
- }
findAllClassPathResources
地址不含通配符的时候
- /**
- * 该方法返回 classes 路径下和所有 jar 包中的所有相匹配的资源。
- * "classpath*:" 开头但是不包含通配符
- */
- protected Resource[] findAllClassPathResources(String location) throws IOException {
- String path = location;
- if (path.startsWith("/")) {
- path = path.substring(1);
- }
- //真正执行加载的地方
- Set<Resource> result = doFindAllClassPathResources(path);
- if (logger.isTraceEnabled()) {
- logger.trace("Resolved classpath location [" + location + "] to resources " + result);
- }
- return result.toArray(new Resource[0]);
- }
-
- /**
- * Find all class location resources with the given path via the ClassLoader.
- * Called by {@link #findAllClassPathResources(String)}.
- * @param path the absolute path within the classpath (never a leading slash)
- * @return a mutable Set of matching Resource instances
- * @since 4.1.1
- */
- protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
- Set<Resource> result = new LinkedHashSet<>(16);
- //根据 ClassLoader 加载路径下的所有资源
- ClassLoader cl = getClassLoader();
- //如果当前父类加载器不为 null ,则通过父类向上迭代获取资源,否则调用 #getBootstrapResources()
- Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
- while (resourceUrls.hasMoreElements()) {
- URL url = resourceUrls.nextElement();
- // 将 URL 转换成 UrlResource
- result.add(convertClassLoaderURL(url));
- }
- //加载所有jar包 传入的为/的时候
- if ("".equals(path)) {
- addAllClassLoaderJarRoots(cl, result);
- }
- return result;
- }
findPathMatchingResources
地址包含通配符的时候
- /**
- * 确定目录,获取该目录下得所有资源。
- * 在所获得的所有资源后,进行迭代匹配获取我们想要的资源。
- */
- protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
- // 确定根路径、子路径
- String rootDirPath = determineRootDir(locationPattern);
- String subPattern = locationPattern.substring(rootDirPath.length());
- //获取根路径下的资源
- Resource[] rootDirResources = getResources(rootDirPath);
- Set<Resource> result = new LinkedHashSet<>(16);
- //遍历
- for (Resource rootDirResource : rootDirResources) {
- rootDirResource = resolveRootDirResource(rootDirResource);
- URL rootDirUrl = rootDirResource.getURL();
- //bundle 资源类型
- if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
- URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
- if (resolvedUrl != null) {
- rootDirUrl = resolvedUrl;
- }
- rootDirResource = new UrlResource(rootDirUrl);
- }
- // vfs 资源类型
- if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
- result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
- }
- //jar资源类型
- else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
- result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
- }
- //其他资源类型
- else {
- result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
- }
- }
- if (logger.isTraceEnabled()) {
- logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
- }
- // 转换成 Resource 数组返回
- return result.toArray(new Resource[0]);
- }
determineRootDir
主要是用于确定根路径
- protected String determineRootDir(String location) {
- // 找到冒号的后一位
- int prefixEnd = location.indexOf(':') + 1;
- //根目录结束位置
- int rootDirEnd = location.length();
- // 在从冒号开始到最后的字符串中,循环判断是否包含通配符,如果包含,则截断最后一个由”/”分割的部分。
- // 例如:在我们路径中,就是最后的ap?-context.xml这一段。再循环判断剩下的部分,直到剩下的路径中都不包含通配符。
- while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
- rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
- }
- // 如果查找完成后,rootDirEnd = 0 了,则将之前赋值的 prefixEnd 的值赋给 rootDirEnd ,也就是冒号的后一位
- if (rootDirEnd == 0) {
- rootDirEnd = prefixEnd;
- }
- //截图更目录
- return location.substring(0, rootDirEnd);
- }
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。