ResourcePatternResolver就是根据资源的前缀来决定使用哪种Resource;因为Resource实在太多了
public interface ResourceLoader { String CLASSPATH_URL_PREFIX = "classpath:"; Resource getResource(String var1); ClassLoader getClassLoader(); }
public interface ResourcePatternResolver extends ResourceLoader { String CLASSPATH_ALL_URL_PREFIX = "classpath*:"; Resource[] getResources(String var1) throws IOException; } 重点看DefaultResourceLoader
public class DefaultResourceLoader implements ResourceLoader { private ClassLoader classLoader; public DefaultResourceLoader() { this.classLoader = ClassUtils.getDefaultClassLoader(); } public DefaultResourceLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public ClassLoader getClassLoader() { return this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader(); } public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); if (location.startsWith("/")) { return this.getResourceByPath(location); } else if (location.startsWith("classpath:")) { return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader()); } else { try { URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException var3) { return this.getResourceByPath(location); } } } protected Resource getResourceByPath(String path) { return new DefaultResourceLoader.ClassPathContextResource(path, this.getClassLoader()); } protected static class ClassPathContextResource extends ClassPathResource implements ContextResource { public ClassPathContextResource(String path, ClassLoader classLoader) { super(path, classLoader); } public String getPathWithinContext() { return this.getPath(); } public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(this.getPath(), relativePath); return new DefaultResourceLoader.ClassPathContextResource(pathToUse, this.getClassLoader()); } } }
package org.springframework.core.io.support; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.net.JarURLConnection; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.UrlResource; import org.springframework.core.io.VfsResource; import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.PathMatcher; import org.springframework.util.ReflectionUtils; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { private static final Log logger = LogFactory.getLog(PathMatchingResourcePatternResolver.class); private static Method equinoxResolveMethod; private final ResourceLoader resourceLoader; //为啥说spring支持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(ClassLoader classLoader) { this.resourceLoader = new DefaultResourceLoader(classLoader); } public ResourceLoader getResourceLoader() { return this.resourceLoader; } public ClassLoader getClassLoader() { return this.getResourceLoader().getClassLoader(); } // 设置路径匹配器 public void setPathMatcher(PathMatcher pathMatcher) { Assert.notNull(pathMatcher, "PathMatcher must not be null"); this.pathMatcher = pathMatcher; } public PathMatcher getPathMatcher() { return this.pathMatcher; } public Resource getResource(String location) { return this.getResourceLoader().getResource(location); } public Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); if (locationPattern.startsWith("classpath*:")) { return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length())); } else { int prefixEnd = locationPattern.indexOf(":") + 1; return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)}; } } protected Resource[] findAllClassPathResources(String location) throws IOException { String path = location; if (location.startsWith("/")) { path = location.substring(1); } Setresult = this.doFindAllClassPathResources(path); return (Resource[])result.toArray(new Resource[result.size()]); } protected Set doFindAllClassPathResources(String path) throws IOException { Set result = new LinkedHashSet(16); ClassLoader cl = this.getClassLoader(); Enumeration resourceUrls = cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path); while(resourceUrls.hasMoreElements()) { URL url = (URL)resourceUrls.nextElement(); result.add(this.convertClassLoaderURL(url)); } if ("".equals(path)) { this.addAllClassLoaderJarRoots(cl, result); } return result; } protected Resource convertClassLoaderURL(URL url) { return new UrlResource(url); } protected void addAllClassLoaderJarRoots(ClassLoader classLoader, Set result) { if (classLoader instanceof URLClassLoader) { try { URL[] var3 = ((URLClassLoader)classLoader).getURLs(); int var4 = var3.length; for(int var5 = 0; var5 < var4; ++var5) { URL url = var3[var5]; if (ResourceUtils.isJarFileURL(url)) { try { UrlResource jarResource = new UrlResource("jar:" + url.toString() + "!/"); if (jarResource.exists()) { result.add(jarResource); } } catch (MalformedURLException var9) { if (logger.isDebugEnabled()) { logger.debug("Cannot search for matching files underneath [" + url + "] because it cannot be converted to a valid 'jar:' URL: " + var9.getMessage()); } } } } } catch (Exception var10) { if (logger.isDebugEnabled()) { logger.debug("Cannot introspect jar files since ClassLoader [" + classLoader + "] does not support 'getURLs()': " + var10); } } } if (classLoader != null) { try { this.addAllClassLoaderJarRoots(classLoader.getParent(), result); } catch (Exception var8) { if (logger.isDebugEnabled()) { logger.debug("Cannot introspect jar files in parent ClassLoader since [" + classLoader + "] does not support 'getParent()': " + var8); } } } } protected Resource[] findPathMatchingResources(String locationPattern) throws IOException { String rootDirPath = this.determineRootDir(locationPattern); String subPattern = locationPattern.substring(rootDirPath.length()); Resource[] rootDirResources = this.getResources(rootDirPath); Set result = new LinkedHashSet(16); Resource[] var6 = rootDirResources; int var7 = rootDirResources.length; for(int var8 = 0; var8 < var7; ++var8) { Resource rootDirResource = var6[var8]; rootDirResource = this.resolveRootDirResource(rootDirResource); if (rootDirResource.getURL().getProtocol().startsWith("vfs")) { result.addAll(PathMatchingResourcePatternResolver.VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, this.getPathMatcher())); } else if (this.isJarResource(rootDirResource)) { result.addAll(this.doFindPathMatchingJarResources(rootDirResource, subPattern)); } else { result.addAll(this.doFindPathMatchingFileResources(rootDirResource, subPattern)); } } if (logger.isDebugEnabled()) { logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result); } return (Resource[])result.toArray(new Resource[result.size()]); } protected String determineRootDir(String location) { int prefixEnd = location.indexOf(":") + 1; int rootDirEnd; for(rootDirEnd = location.length(); rootDirEnd > prefixEnd && this.getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd)); rootDirEnd = location.lastIndexOf(47, rootDirEnd - 2) + 1) { ; } if (rootDirEnd == 0) { rootDirEnd = prefixEnd; } return location.substring(0, rootDirEnd); } protected Resource resolveRootDirResource(Resource original) throws IOException { if (equinoxResolveMethod != null) { URL url = original.getURL(); if (url.getProtocol().startsWith("bundle")) { return new UrlResource((URL)ReflectionUtils.invokeMethod(equinoxResolveMethod, (Object)null, new Object[]{url})); } } return original; } protected boolean isJarResource(Resource resource) throws IOException { return ResourceUtils.isJarURL(resource.getURL()); } protected Set doFindPathMatchingJarResources(Resource rootDirResource, String subPattern) throws IOException { URLConnection con = rootDirResource.getURL().openConnection(); boolean newJarFile = false; JarFile jarFile; String jarFileUrl; String rootEntryPath; if (con instanceof JarURLConnection) { JarURLConnection jarCon = (JarURLConnection)con; ResourceUtils.useCachesIfNecessary(jarCon); jarFile = jarCon.getJarFile(); jarFileUrl = jarCon.getJarFileURL().toExternalForm(); JarEntry jarEntry = jarCon.getJarEntry(); rootEntryPath = jarEntry != null ? jarEntry.getName() : ""; } else { String urlFile = rootDirResource.getURL().getFile(); try { int separatorIndex = urlFile.indexOf("!/"); if (separatorIndex != -1) { jarFileUrl = urlFile.substring(0, separatorIndex); rootEntryPath = urlFile.substring(separatorIndex + "!/".length()); jarFile = this.getJarFile(jarFileUrl); } else { jarFile = new JarFile(urlFile); jarFileUrl = urlFile; rootEntryPath = ""; } newJarFile = true; } catch (ZipException var16) { if (logger.isDebugEnabled()) { logger.debug("Skipping invalid jar classpath entry [" + urlFile + "]"); } return Collections.emptySet(); } } LinkedHashSet var22; try { if (logger.isDebugEnabled()) { logger.debug("Looking for matching resources in jar file [" + jarFileUrl + "]"); } if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) { rootEntryPath = rootEntryPath + "/"; } Set result = new LinkedHashSet(8); Enumeration entries = jarFile.entries(); while(entries.hasMoreElements()) { JarEntry entry = (JarEntry)entries.nextElement(); String entryPath = entry.getName(); if (entryPath.startsWith(rootEntryPath)) { String relativePath = entryPath.substring(rootEntryPath.length()); if (this.getPathMatcher().match(subPattern, relativePath)) { result.add(rootDirResource.createRelative(relativePath)); } } } var22 = result; } finally { if (newJarFile) { jarFile.close(); } } return var22; } protected JarFile getJarFile(String jarFileUrl) throws IOException { if (jarFileUrl.startsWith("file:")) { try { return new JarFile(ResourceUtils.toURI(jarFileUrl).getSchemeSpecificPart()); } catch (URISyntaxException var3) { return new JarFile(jarFileUrl.substring("file:".length())); } } else { return new JarFile(jarFileUrl); } } protected Set doFindPathMatchingFileResources(Resource rootDirResource, String subPattern) throws IOException { File rootDir; try { rootDir = rootDirResource.getFile().getAbsoluteFile(); } catch (IOException var5) { if (logger.isWarnEnabled()) { logger.warn("Cannot search for matching files underneath " + rootDirResource + " because it does not correspond to a directory in the file system", var5); } return Collections.emptySet(); } return this.doFindMatchingFileSystemResources(rootDir, subPattern); } protected Set doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException { if (logger.isDebugEnabled()) { logger.debug("Looking for matching resources in directory tree [" + rootDir.getPath() + "]"); } Set matchingFiles = this.retrieveMatchingFiles(rootDir, subPattern); Set result = new LinkedHashSet(matchingFiles.size()); Iterator var5 = matchingFiles.iterator(); while(var5.hasNext()) { File file = (File)var5.next(); result.add(new FileSystemResource(file)); } return result; } protected Set retrieveMatchingFiles(File rootDir, String pattern) throws IOException { if (!rootDir.exists()) { if (logger.isDebugEnabled()) { logger.debug("Skipping [" + rootDir.getAbsolutePath() + "] because it does not exist"); } return Collections.emptySet(); } else if (!rootDir.isDirectory()) { if (logger.isWarnEnabled()) { logger.warn("Skipping [" + rootDir.getAbsolutePath() + "] because it does not denote a directory"); } return Collections.emptySet(); } else if (!rootDir.canRead()) { if (logger.isWarnEnabled()) { logger.warn("Cannot search for matching files underneath directory [" + rootDir.getAbsolutePath() + "] because the application is not allowed to read the directory"); } return Collections.emptySet(); } else { String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/"); if (!pattern.startsWith("/")) { fullPattern = fullPattern + "/"; } fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/"); Set result = new LinkedHashSet(8); this.doRetrieveMatchingFiles(fullPattern, rootDir, result); return result; } } protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set result) throws IOException { if (logger.isDebugEnabled()) { logger.debug("Searching directory [" + dir.getAbsolutePath() + "] for files matching pattern [" + fullPattern + "]"); } File[] dirContents = dir.listFiles(); if (dirContents == null) { if (logger.isWarnEnabled()) { logger.warn("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]"); } } else { File[] var5 = dirContents; int var6 = dirContents.length; for(int var7 = 0; var7 < var6; ++var7) { File content = var5[var7]; String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/"); if (content.isDirectory() && this.getPathMatcher().matchStart(fullPattern, currPath + "/")) { if (!content.canRead()) { if (logger.isDebugEnabled()) { logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() + "] because the application is not allowed to read the directory"); } } else { this.doRetrieveMatchingFiles(fullPattern, content, result); } } if (this.getPathMatcher().match(fullPattern, currPath)) { result.add(content); } } } } static { try { Class> fileLocatorClass = ClassUtils.forName("org.eclipse.core.runtime.FileLocator", PathMatchingResourcePatternResolver.class.getClassLoader()); equinoxResolveMethod = fileLocatorClass.getMethod("resolve", URL.class); logger.debug("Found Equinox FileLocator for OSGi bundle URL resolution"); } catch (Throwable var1) { equinoxResolveMethod = null; } } private static class PatternVirtualFileVisitor implements InvocationHandler { private final String subPattern; private final PathMatcher pathMatcher; private final String rootPath; private final Set resources = new LinkedHashSet(); public PatternVirtualFileVisitor(String rootPath, String subPattern, PathMatcher pathMatcher) { this.subPattern = subPattern; this.pathMatcher = pathMatcher; this.rootPath = rootPath.length() != 0 && !rootPath.endsWith("/") ? rootPath + "/" : rootPath; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (Object.class == method.getDeclaringClass()) { if (methodName.equals("equals")) { return proxy == args[0]; } if (methodName.equals("hashCode")) { return System.identityHashCode(proxy); } } else { if ("getAttributes".equals(methodName)) { return this.getAttributes(); } if ("visit".equals(methodName)) { this.visit(args[0]); return null; } if ("toString".equals(methodName)) { return this.toString(); } } throw new IllegalStateException("Unexpected method invocation: " + method); } public void visit(Object vfsResource) { if (this.pathMatcher.match(this.subPattern, VfsPatternUtils.getPath(vfsResource).substring(this.rootPath.length()))) { this.resources.add(new VfsResource(vfsResource)); } } public Object getAttributes() { return VfsPatternUtils.getVisitorAttribute(); } public Set getResources() { return this.resources; } public int size() { return this.resources.size(); } public String toString() { return "sub-pattern: " + this.subPattern + ", resources: " + this.resources; } } private static class VfsResourceMatchingDelegate { private VfsResourceMatchingDelegate() { } public static Set findMatchingResources(Resource rootResource, String locationPattern, PathMatcher pathMatcher) throws IOException { Object root = VfsPatternUtils.findRoot(rootResource.getURL()); PathMatchingResourcePatternResolver.PatternVirtualFileVisitor visitor = new PathMatchingResourcePatternResolver.PatternVirtualFileVisitor(VfsPatternUtils.getPath(root), locationPattern, pathMatcher); VfsPatternUtils.visit(root, visitor); return visitor.getResources(); } } }