diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java index de327d7e1c12c590293de656eb85c57e80fd5d10..2bee113bf790026f5aa6843fa73f5a1d8bacb9af 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java @@ -44,6 +44,8 @@ import org.springframework.util.ResourceUtils; * @author Juergen Hoeller * @author Sam Brannen * @since 28.12.2003 + * + * 如果想要自定义Resource类 只需要集成AbstractResource */ public abstract class AbstractResource implements Resource { @@ -51,12 +53,16 @@ public abstract class AbstractResource implements Resource { * This implementation checks whether a File can be opened, * falling back to whether an InputStream can be opened. * This will cover both directories and content resources. + * + * 判断文件是否存在,若判断过程产生异常(因为会调用SecurityManager来判断),就关闭对应的流 */ @Override public boolean exists() { // Try file existence: can we find the file in the file system? + //尝试判断文件是否存在:我们可以在文件系统中找到文件吗? if (isFile()) { try { + //基于File进行判断 return getFile().exists(); } catch (IOException ex) { @@ -67,6 +73,7 @@ public abstract class AbstractResource implements Resource { } } // Fall back to stream existence: can we open the stream? + // 判断流是否存在 基于inputStream判断 try { getInputStream().close(); return true; @@ -83,6 +90,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation always returns {@code true} for a resource * that {@link #exists() exists} (revised as of 5.1). + * + * 判断文件是否可读 如果文件存在 则可读 */ @Override public boolean isReadable() { @@ -91,6 +100,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation always returns {@code false}. + * + * 直接返回false 表示未打开 */ @Override public boolean isOpen() { @@ -99,6 +110,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation always returns {@code false}. + * + * 总是返回false 表示不是File */ @Override public boolean isFile() { @@ -108,6 +121,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation throws a FileNotFoundException, assuming * that the resource cannot be resolved to a URL. + * + * 抛出 FileNotFoundException 异常,交给子类实现 */ @Override public URL getURL() throws IOException { @@ -117,6 +132,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation builds a URI based on the URL returned * by {@link #getURL()}. + * + * 基于GetUrl返回的Url构建 Uri */ @Override public URI getURI() throws IOException { @@ -132,6 +149,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation throws a FileNotFoundException, assuming * that the resource cannot be resolved to an absolute file path. + * + * 抛出 FileNotFoundException 异常,交给子类实现 */ @Override public File getFile() throws IOException { @@ -143,6 +162,8 @@ public abstract class AbstractResource implements Resource { * with the result of {@link #getInputStream()}. *

This is the same as in {@link Resource}'s corresponding default method * but mirrored here for efficient JVM-level dispatching in a class hierarchy. + * + * 根据 getInputStream() 的返回结果构建 ReadableByteChannel */ @Override public ReadableByteChannel readableChannel() throws IOException { @@ -156,12 +177,16 @@ public abstract class AbstractResource implements Resource { * checking File length, or possibly simply returning -1 if the stream can * only be read once. * @see #getInputStream() + * + *这个资源内容长度实际就是资源的字节长度,通过全部读取一遍来判断 */ @Override public long contentLength() throws IOException { + //获取流 InputStream is = getInputStream(); try { long size = 0; + //每次最多读取256字符 byte[] buf = new byte[256]; int read; while ((read = is.read(buf)) != -1) { @@ -186,10 +211,14 @@ public abstract class AbstractResource implements Resource { * This implementation checks the timestamp of the underlying File, * if available. * @see #getFileForLastModifiedCheck() + * + * 返回资源最后一次修改时间 */ @Override public long lastModified() throws IOException { + //获取到文件 File fileToCheck = getFileForLastModifiedCheck(); + //获取最后一次修改时间 long lastModified = fileToCheck.lastModified(); if (lastModified == 0L && !fileToCheck.exists()) { throw new FileNotFoundException(getDescription() + @@ -205,6 +234,8 @@ public abstract class AbstractResource implements Resource { * @throws FileNotFoundException if the resource cannot be resolved as * an absolute file path, i.e. is not available in a file system * @throws IOException in case of general resolution/reading failures + * + * 通过getFile 获取文件 最后一次检查 */ protected File getFileForLastModifiedCheck() throws IOException { return getFile(); @@ -213,6 +244,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation throws a FileNotFoundException, assuming * that relative resources cannot be created for this resource. + * + * 抛出 FileNotFoundException 异常,交给子类实现 */ @Override public Resource createRelative(String relativePath) throws IOException { @@ -222,6 +255,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation always returns {@code null}, * assuming that this resource type does not have a filename. + * + * 获取资源名称,默认返回 null ,交给子类实现 */ @Override @Nullable @@ -233,6 +268,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation compares description strings. * @see #getDescription() + * + * 如果对象相等 或者 属于Resource类 并且描述一致 */ @Override public boolean equals(@Nullable Object other) { @@ -252,6 +289,8 @@ public abstract class AbstractResource implements Resource { /** * This implementation returns the description of this resource. * @see #getDescription() + * + * 返回资源的描述 */ @Override public String toString() { diff --git a/spring-core/src/main/java/org/springframework/core/io/ByteArrayResource.java b/spring-core/src/main/java/org/springframework/core/io/ByteArrayResource.java index c5989b8330dcdd7fd6b96d3e4c6b319a23437df0..84dbc4f2784749aee669b941b69a399e7f691d23 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ByteArrayResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/ByteArrayResource.java @@ -39,6 +39,8 @@ import org.springframework.util.Assert; * @see java.io.ByteArrayInputStream * @see InputStreamResource * @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource) + * + * 对字节数组提供的数据的封装。如果通过 InputStream 形式访问该类型的资源,该实现会根据字节数组的数据构造一个相应的 ByteArrayInputStream。 */ public class ByteArrayResource extends AbstractResource { diff --git a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java index 2ba871bbc1e80ad693f562eb831904964efc23b8..b955bf4a4ecddb3a034ef34fc54df45517e6e02c 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java @@ -40,6 +40,8 @@ import org.springframework.util.StringUtils; * @since 28.12.2003 * @see ClassLoader#getResourceAsStream(String) * @see Class#getResourceAsStream(String) + * + * ClassPathResource :class path 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。 */ public class ClassPathResource extends AbstractFileResolvingResource { diff --git a/spring-core/src/main/java/org/springframework/core/io/ClassRelativeResourceLoader.java b/spring-core/src/main/java/org/springframework/core/io/ClassRelativeResourceLoader.java index ddda6d4e120d9f8607789346e954af109ea78d11..d3a753f42e50139b474ed064d3a7d38934280b5b 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ClassRelativeResourceLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/ClassRelativeResourceLoader.java @@ -27,6 +27,8 @@ import org.springframework.util.StringUtils; * @since 3.0 * @see Class#getResource(String) * @see ClassPathResource#ClassPathResource(String, Class) + * + * 实现将普通资源路径解释为相对于给定的java.lang.Class */ public class ClassRelativeResourceLoader extends DefaultResourceLoader { diff --git a/spring-core/src/main/java/org/springframework/core/io/ContextResource.java b/spring-core/src/main/java/org/springframework/core/io/ContextResource.java index 30f75e00c334033fad78a816b712812310374e94..3d5a4222d42fcca0e81b7711c0e6ad0743c49b28 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ContextResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/ContextResource.java @@ -33,6 +33,8 @@ public interface ContextResource extends Resource { * Return the path within the enclosing 'context'. *

This is typically path relative to a context-specific root directory, * e.g. a ServletContext root or a PortletContext root. + * + * 返回封闭的上下文环境 目的就是为了保存上下文环境 */ String getPathWithinContext(); diff --git a/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java b/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java index 27ed3bf1a088a2b147a3abce917e6998596c6dd6..0a206d0f6d1188925f5eb5a0a896a79cd5d92373 100644 --- a/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java @@ -44,6 +44,8 @@ import org.springframework.util.StringUtils; * @since 10.03.2004 * @see FileSystemResourceLoader * @see org.springframework.context.support.ClassPathXmlApplicationContext + * + * 资源加载器的默认实现 */ public class DefaultResourceLoader implements ResourceLoader { @@ -69,6 +71,8 @@ public class DefaultResourceLoader implements ResourceLoader { * Create a new DefaultResourceLoader. * @param classLoader the ClassLoader to load class path resources with, or {@code null} * for using the thread context class loader at the time of actual resource access + * + * 接受classload 作为参数 */ public DefaultResourceLoader(@Nullable ClassLoader classLoader) { this.classLoader = classLoader; @@ -104,6 +108,10 @@ public class DefaultResourceLoader implements ResourceLoader { * resolution rules. It may therefore also override any default rules. * @since 4.3 * @see #getProtocolResolvers() + * + * 添加用户自定义协议 + * DefaultResourceLoader 的spi 允许用户自定义加载协议 而不需要继承classLoader + * */ public void addProtocolResolver(ProtocolResolver resolver) { Assert.notNull(resolver, "ProtocolResolver must not be null"); @@ -114,6 +122,8 @@ public class DefaultResourceLoader implements ResourceLoader { * Return the collection of currently registered protocol resolvers, * allowing for introspection as well as modification. * @since 4.3 + * + * 获取用户自定义策略 */ public Collection getProtocolResolvers() { return this.protocolResolvers; @@ -140,31 +150,58 @@ public class DefaultResourceLoader implements ResourceLoader { } + /** + * 资源解析 根据给定的localtion 返回对应的Resource + * @param location the resource location + * @return + * + * 示例 + * ResourceLoader resourceLoader = new DefaultResourceLoader(); + * // + * Resource fileResource1 = resourceLoader.getResource("D:/Users/chenming673/Documents/spark.txt"); + * System.out.println("fileResource1 is FileSystemResource:" + (fileResource1 instanceof FileSystemResource)); + * // + * Resource fileResource2 = resourceLoader.getResource("/Users/chenming673/Documents/spark.txt"); + * System.out.println("fileResource2 is ClassPathResource:" + (fileResource2 instanceof ClassPathResource)); + * + * Resource urlResource1 = resourceLoader.getResource("file:/Users/chenming673/Documents/spark.txt"); + * System.out.println("urlResource1 is UrlResource:" + (urlResource1 instanceof UrlResource)); + * + * Resource urlResource2 = resourceLoader.getResource("http://www.baidu.com"); + * System.out.println("urlResource1 is urlResource:" + (urlResource2 instanceof UrlResource)); + */ @Override public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); - + // 首先,通过 ProtocolResolver 来加载资源 + // 用户自定义协议资源解决策略 for (ProtocolResolver protocolResolver : getProtocolResolvers()) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return resource; } } - + //其次 以 / 开头的资源 返回ClassPathContextResource 类型的资源 if (location.startsWith("/")) { return getResourceByPath(location); } + //再次 以classpath: 开头 返回classPathResouce类型的资源 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进行资源定位如果没用抛出异常 + // 则判断是否为FileUrl 如果是则构造FileUrlResource 类型的资源,否则构造 UrlResource 类型的资源。 URL url = new URL(location); return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url)); } catch (MalformedURLException ex) { // No URL -> resolve as resource path. + //最后返回 classpathContextResource类型的资源 + //若在加载过程中抛出 MalformedURLException 异常,则委派 #getResourceByPath() 方法,实现资源定位加载。实际上,和【其次】相同落。 return getResourceByPath(location); } } @@ -182,6 +219,7 @@ public class DefaultResourceLoader implements ResourceLoader { * @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath */ protected Resource getResourceByPath(String path) { + //构造 ClassPathContextResource 并且返回 return new ClassPathContextResource(path, getClassLoader()); } @@ -189,6 +227,8 @@ public class DefaultResourceLoader implements ResourceLoader { /** * ClassPathResource that explicitly expresses a context-relative path * through implementing the ContextResource interface. + * + * ClassPathResource 通过实现 ContextResource 接口显式表达上下文相关路径。 */ protected static class ClassPathContextResource extends ClassPathResource implements ContextResource { diff --git a/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java b/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java index feb2f9067da3edb730d4e3426ef678ed903690a4..3a501ce26912dd435f80d77c66dfab2a16c58b80 100644 --- a/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java @@ -54,6 +54,8 @@ import org.springframework.util.StringUtils; * @see #FileSystemResource(Path) * @see java.io.File * @see java.nio.file.Files + * + * FileSystemResource :对 java.io.File 类型资源的封装,只要是跟 File 打交道的,基本上与 FileSystemResource 也可以打交道。支持文件和 URL 的形式,实现 WritableResource 接口,且从 Spring Framework 5.0 开始,FileSystemResource 使用 NIO2 API进行读/写交互。 */ public class FileSystemResource extends AbstractResource implements WritableResource { diff --git a/spring-core/src/main/java/org/springframework/core/io/FileSystemResourceLoader.java b/spring-core/src/main/java/org/springframework/core/io/FileSystemResourceLoader.java index 1cf38e3ff7ac764a7dbf5c33942f8a7a606ab4b3..eb0671f56a71506ccca76671c56bcca2f561d5fa 100644 --- a/spring-core/src/main/java/org/springframework/core/io/FileSystemResourceLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/FileSystemResourceLoader.java @@ -45,6 +45,9 @@ public class FileSystemResourceLoader extends DefaultResourceLoader { * @return the corresponding Resource handle * @see FileSystemResource * @see org.springframework.web.context.support.ServletContextResourceLoader#getResourceByPath + * + * 重写getResourceByPath 使之从文件系统加载资源文件 + * 并以 FileSystemResource 类型返回 */ @Override protected Resource getResourceByPath(String path) { diff --git a/spring-core/src/main/java/org/springframework/core/io/InputStreamResource.java b/spring-core/src/main/java/org/springframework/core/io/InputStreamResource.java index 5a5eaa96e9baa12743a97b987484a9d7fe09c9a1..7a23cce5b32a679d7df0b97165518029f913610b 100644 --- a/spring-core/src/main/java/org/springframework/core/io/InputStreamResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/InputStreamResource.java @@ -41,6 +41,8 @@ import org.springframework.util.Assert; * @see ClassPathResource * @see FileSystemResource * @see UrlResource + * + * InputStreamResource :将给定的 InputStream 作为一种资源的 Resource 的实现类。 */ public class InputStreamResource extends AbstractResource { diff --git a/spring-core/src/main/java/org/springframework/core/io/InputStreamSource.java b/spring-core/src/main/java/org/springframework/core/io/InputStreamSource.java index 8d72c9cd8bbc71816b1b760eb0fd584509169ac2..8e1ef93e20d1835a21039f112c47ede1064ae41c 100644 --- a/spring-core/src/main/java/org/springframework/core/io/InputStreamSource.java +++ b/spring-core/src/main/java/org/springframework/core/io/InputStreamSource.java @@ -37,6 +37,8 @@ import java.io.InputStream; * @see Resource * @see InputStreamResource * @see ByteArrayResource + * + * 获取流对象 */ public interface InputStreamSource { diff --git a/spring-core/src/main/java/org/springframework/core/io/ProtocolResolver.java b/spring-core/src/main/java/org/springframework/core/io/ProtocolResolver.java index 53cc9301eb60d7094791ab98d17ac7fa9cc8910d..7544d67e8dc62bc2d6aa760f916dcd5bf8987015 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ProtocolResolver.java +++ b/spring-core/src/main/java/org/springframework/core/io/ProtocolResolver.java @@ -39,6 +39,9 @@ public interface ProtocolResolver { * @param resourceLoader the associated resource loader * @return a corresponding {@code Resource} handle if the given location * matches this resolver's protocol, or {@code null} otherwise + * + * 使用指定的ResouceLoader解析指定的localtion + * 若成功 则直接返回Resource */ @Nullable Resource resolve(String location, ResourceLoader resourceLoader); diff --git a/spring-core/src/main/java/org/springframework/core/io/Resource.java b/spring-core/src/main/java/org/springframework/core/io/Resource.java index 1995ee783e6d7e2ca6469b05ef8e0831462ccfe6..9ab0288885283d0a38ac70eb42ac681ff8ed31e2 100644 --- a/spring-core/src/main/java/org/springframework/core/io/Resource.java +++ b/spring-core/src/main/java/org/springframework/core/io/Resource.java @@ -56,6 +56,8 @@ public interface Resource extends InputStreamSource { *

This method performs a definitive existence check, whereas the * existence of a {@code Resource} handle only guarantees a valid * descriptor handle. + * + * 资源是否存在 */ boolean exists(); @@ -69,6 +71,8 @@ public interface Resource extends InputStreamSource { * that the resource content cannot be read. * @see #getInputStream() * @see #exists() + * + * 资源是否可读 */ default boolean isReadable() { return exists(); @@ -79,6 +83,9 @@ public interface Resource extends InputStreamSource { * If {@code true}, the InputStream cannot be read multiple times, * and must be read and closed to avoid resource leaks. *

Will be {@code false} for typical resource descriptors. + * + * 资源所代表的句柄 是否被一个steam资源打开 + * */ default boolean isOpen() { return false; @@ -91,6 +98,8 @@ public interface Resource extends InputStreamSource { *

This is conservatively {@code false} by default. * @since 5.0 * @see #getFile() + * + * 是否是File */ default boolean isFile() { return false; @@ -100,6 +109,8 @@ public interface Resource extends InputStreamSource { * 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 + * + * 返回资源的uri的句柄 */ URL getURL() throws IOException; @@ -108,6 +119,8 @@ public interface Resource extends InputStreamSource { * @throws IOException if the resource cannot be resolved as URI, * i.e. if the resource is not available as descriptor * @since 2.5 + * + * 回资源的uri的句柄 */ URI getURI() throws IOException; @@ -117,6 +130,8 @@ public interface Resource extends InputStreamSource { * absolute file path, i.e. if the resource is not available in a file system * @throws IOException in case of general resolution/reading failures * @see #getInputStream() + * + * 返回资源的File的句柄 */ File getFile() throws IOException; @@ -130,6 +145,9 @@ public interface Resource extends InputStreamSource { * @throws IOException if the content channel could not be opened * @since 5.0 * @see #getInputStream() + * + * 返回 ReadableByteChannel + * */ default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); @@ -139,6 +157,8 @@ public interface Resource extends InputStreamSource { * 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; @@ -146,6 +166,8 @@ public interface Resource extends InputStreamSource { * 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; @@ -154,6 +176,8 @@ public interface Resource extends InputStreamSource { * @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; @@ -162,6 +186,8 @@ public interface Resource extends InputStreamSource { * part of the path: for example, "myfile.txt". *

Returns {@code null} if this type of resource does not * have a filename. + * + * 资源的文件名 */ @Nullable String getFilename(); @@ -172,6 +198,8 @@ public interface Resource extends InputStreamSource { *

Implementations are also encouraged to return this value * from their {@code toString} method. * @see Object#toString() + * + * 资源的描述 */ String getDescription(); diff --git a/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java b/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java index f66befdd547d2f879e92b8c39cd034873dfa9216..4799cfc9d5dd1796c2c5abee31e826e43b8d20eb 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java @@ -38,10 +38,13 @@ import org.springframework.util.ResourceUtils; * @see org.springframework.core.io.support.ResourcePatternResolver * @see org.springframework.context.ApplicationContext * @see org.springframework.context.ResourceLoaderAware + * + * 资源的加载则由 ResourceLoader 来统一定义。 */ public interface ResourceLoader { /** Pseudo URL prefix for loading from the class path: "classpath:". */ + /** ClassPath url 的前缀 classpath:**/ String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; @@ -63,6 +66,13 @@ public interface ResourceLoader { * @see #CLASSPATH_URL_PREFIX * @see Resource#exists() * @see Resource#getInputStream() + * + * 根据所提供的的路径location 返回Resource实力 但他不一定保证资源存在所以需要调用exist判断资源 + * 支持以下类型资源加载 + * file:C:/test.dat + * classpath:test.dat + * WEB-INF/test.dat + * 这个过程主要是通过默认的DefaultResourceLoader实现的 */ Resource getResource(String location); @@ -75,6 +85,9 @@ public interface ResourceLoader { * (only {@code null} if even the system {@code ClassLoader} isn't accessible) * @see org.springframework.util.ClassUtils#getDefaultClassLoader() * @see org.springframework.util.ClassUtils#forName(String, ClassLoader) + * + * 返回classLoader实力,对于想要获取clasloder的ResourceLoader + * 比如 ClasspathResource 主要是使用 classloader 加载指定资源 */ @Nullable ClassLoader getClassLoader(); diff --git a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java index 3f1dcc05ffe477de6b3175dcb497464508aa1dea..6dd1fc8a975f7c6f0940afe60150c7cc4322727a 100644 --- a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java @@ -39,6 +39,8 @@ import org.springframework.util.StringUtils; * @author Juergen Hoeller * @since 28.12.2003 * @see java.net.URL + * + * 对 java.net.URL类型资源的封装。内部委派 URL 进行具体的资源操作。 */ public class UrlResource extends AbstractFileResolvingResource { diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java index aef2476b0a55948d2724552502714706915e64dd..673b3af91384dd0b411a90e6210c39a8bfd8811b 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java @@ -201,8 +201,14 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol } + /** + * 内置的 ResourceLoader 资源定位器 + */ private final ResourceLoader resourceLoader; + /** + * 默认为 AntPathMatcher 对象,用于支持 Ant 类型的路径匹配。 + */ private PathMatcher pathMatcher = new AntPathMatcher(); @@ -212,6 +218,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol * @see org.springframework.core.io.DefaultResourceLoader */ public PathMatchingResourcePatternResolver() { + //默认使用DefaultResourceLoader为资源解析器 this.resourceLoader = new DefaultResourceLoader(); } @@ -277,28 +284,35 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol @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, - // and on Tomcat only after the "*/" separator for its "war:" protocol. + // Generally only look for a pattern after a prefix here, 一般只在此处查找前缀后的模式 + // and on Tomcat only after the "*/" separator for its "war:" protocol. 而在 Tomcat 上只有在 “*/ ”分隔符之后才为其 “war:” 协议 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 + //则直接交给默认的ResourceLoader 进行加载 return new Resource[] {getResourceLoader().getResource(locationPattern)}; } } @@ -315,13 +329,16 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol */ protected Resource[] findAllClassPathResources(String location) throws IOException { String path = location; + //去除开头的/ if (path.startsWith("/")) { path = path.substring(1); } + //执行加载所有的classpath资源 Set result = doFindAllClassPathResources(path); if (logger.isTraceEnabled()) { logger.trace("Resolved classpath location [" + location + "] to resources " + result); } + //转换成 Resource 数组返回 return result.toArray(new Resource[0]); } @@ -331,18 +348,24 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol * @param path the absolute path within the classpath (never a leading slash) * @return a mutable Set of matching Resource instances * @since 4.1.1 + * + * 其实就是利用 ClassLoader 来加载指定路径下的资源,不论它是在 class 路径下还是在 jar 包中。如果我们传入的路径为空或者 /,则会调用 #addAllClassLoaderJarRoots(...) 方法,加载所有的 jar 包。 */ protected Set doFindAllClassPathResources(String path) throws IOException { + //创建容器 Set result = new LinkedHashSet<>(16); ClassLoader cl = getClassLoader(); + //根据ClassLoader记载路径下的所有资源 Enumeration resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path)); while (resourceUrls.hasMoreElements()) { URL url = resourceUrls.nextElement(); + //将url 转变成 UrlResource result.add(convertClassLoaderURL(url)); } if (!StringUtils.hasLength(path)) { // The above result is likely to be incomplete, i.e. only containing file system references. // We need to have pointers to each of the jar files on the classpath as well... + //记载路径下所有的jar包 addAllClassLoaderJarRoots(cl, result); } return result; @@ -490,15 +513,21 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol * @see #doFindPathMatchingJarResources * @see #doFindPathMatchingFileResources * @see org.springframework.util.PathMatcher + * + * 如果包含了通配符 则调用该方法进行i资源加载 */ protected Resource[] findPathMatchingResources(String locationPattern) throws IOException { + // 确定根路径、子路径 String rootDirPath = determineRootDir(locationPattern); String subPattern = locationPattern.substring(rootDirPath.length()); + //获取根据路径下的资源 Resource[] rootDirResources = getResources(rootDirPath); + //遍历 迭代 Set 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) { @@ -506,12 +535,15 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol } 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)); } @@ -519,6 +551,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol if (logger.isTraceEnabled()) { logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result); } + + // 转换成 Resource 数组返回 return result.toArray(new Resource[0]); } @@ -533,16 +567,27 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol * @param location the location to check * @return the part of the location that denotes the root directory * @see #retrieveMatchingFiles + * */ protected String determineRootDir(String location) { + //举例 + // classpath*:test/cc*/spring-*.xml classpath*:test/ + // classpath*:test/aa/spring-*.xml classpath*:test/aa/ + + //找到冒号的后一位 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); } diff --git a/spring-core/src/main/java/org/springframework/core/io/support/ResourcePatternResolver.java b/spring-core/src/main/java/org/springframework/core/io/support/ResourcePatternResolver.java index 901323fae0f9233daf2f41abe6f2f7a293006e92..a17f24faf139ad0a2a06924e8abb2e86250f3455 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/ResourcePatternResolver.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/ResourcePatternResolver.java @@ -52,6 +52,8 @@ import org.springframework.core.io.ResourceLoader; * @see org.springframework.core.io.ResourceLoader * @see org.springframework.context.ApplicationContext * @see org.springframework.context.ResourceLoaderAware + * + * 是对ResourceLoader 的扩展 支持根据指定的资源路径匹配模式每次返回多个 Resource 实例 */ public interface ResourcePatternResolver extends ResourceLoader { @@ -72,6 +74,8 @@ public interface ResourcePatternResolver extends ResourceLoader { * @param locationPattern the location pattern to resolve * @return the corresponding {@code Resource} objects * @throws IOException in case of I/O errors + * + * 根据路径匹配模式 返回多个Resource实例 */ Resource[] getResources(String locationPattern) throws IOException;