• Spring统一资源加载策略 Resource和ResourceLoader


    转:https://www.cnblogs.com/loveLands/articles/10797772.html

    1 Resource统一资源

    1.1 简介

      处理外部资源是很繁琐的事情,我们可能需要处理URL资源、File资源资源、ClassPath相关资源、服务器相关资源(JBoss AS 5.x上的VFS资源)等等很多资源;
      而且处理这些资源步骤都是类似的(打开资源、读取资源、关闭资源),因此如果能抽象出一个统一的接口来对这些底层资源进行统一访问,是不是很方便,而且使我们系统更加简洁。
      Spring 提供Resource接口来统一这些底层资源一致的访问,作为所有资源的统一抽象。

    1.2 Resource源码

    public interface Resource extends InputStreamSource {
    
      //返回当前Resource代表的资源是否存在,true表示存在 boolean exists();   //返回当前Resource代表的资源是否可读,true表示可读 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代表,则返回该URI,否则抛出IOException URI getURI() throws IOException;   //如果当前Resource代表的底层资源能由java.io.File代表,则返回该File,否则抛出IOException File getFile() throws IOException;   //返回 ReadableByteChannel default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); }   //返回当前Resource代表的底层文件资源的长度,一般是值代表的文件资源的长度 long contentLength() throws IOException;   //返回当前Resource代表的底层资源的最后修改时间 long lastModified() throws IOException;   //用于创建相对于当前Resource代表的底层资源的资源,比如当前Resource代表文件资源“d:/test/”则createRelative(“test.txt”)将返回表文件资源“d:/test/test.txt”Resource资源 Resource createRelative(String relativePath) throws IOException;   //返回当前Resource代表的底层文件资源的文件路径,比如File资源“file://d:/test.txt”将返回“d:/test.txt”,而URL资源http://www.javass.cn将返回“”,因为只返回文件路径 @Nullable String getFilename();   //返回当前Resource代表的底层资源的描述符,通常就是资源的全路径(实际文件名或实际URL地址) String getDescription(); }

     从Resource源码中可以看到,Resource提供了判断资源是否存在,是否可访问,是什么类型资源,获取资源长度、上次修改时间、资源路径等等的方法。为我们提供了统一的资源访问

     

    1.3 Resource的体系

    从上图可以看到,Resource 根据资源的不同类型提供不同的具体实现

      1)FileSystemResource:对 java.io.File 类型资源的封装,支持文件和 URL 的形式,实现 WritableResource 接口,从 Spring Framework 5.0 开始,FileSystemResource 使用NIO.2 API进行读/写交互

      2)ByteArrayResource:对字节数组提供的数据的封装。如果通过 InputStream 形式访问该类型的资源,该实现会根据字节数组的数据构造一个相应的 ByteArrayInputStream。

      3)UrlResource:对 java.net.URL类型资源的封装。内部委派 URL 进行具体的资源操作。

      4)ClassPathResource:class path 类型资源的实现。使用给定的 ClassLoader 或者给定的 Class 来加载资源。

      5)InputStreamResource:将给定的 InputStream 作为一种资源的 Resource 的实现类。

      我们发现他们都继承及抽象类AbstractResource。AbstractResource 为 Resource 接口的默认实现,已经替我们实现Resource 接口的大部分的公共实现。如果我们想要实现自定义的 Resource,记住不要实现 Resource 接口,而应该继承 AbstractResource 抽象类,然后根据当前的具体资源特性覆盖相应的方法即可

     

    1.4 AbstractResource

    1.4.1 AbstractResource源码

    public abstract class AbstractResource implements Resource {
    
        /**
         * 判断文件是否存在,若判断过程产生异常(因为会调用SecurityManager来判断),就关闭对应的流
         */
        @Override
        public boolean exists() {
            try {
                return getFile().exists();
            }
            catch (IOException ex) {
                // Fall back to stream existence: can we open the stream?
                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,表示不为 File
         */
        @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");
        }
    
        /**
         * 根据 getInputStream() 的返回结果构建 ReadableByteChannel
         */
        @Override
        public ReadableByteChannel readableChannel() throws IOException {
            return Channels.newChannel(getInputStream());
        }
    
        /**
         * 获取资源的长度
         *
         * 这个资源内容长度实际就是资源的字节长度,通过全部读取一遍来判断
         */
        @Override
        public long contentLength() throws IOException {
            InputStream is = getInputStream();
            try {
                long size = 0;
                byte[] buf = new byte[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();
        }
    
        /**
         * 交给子类实现
         */
        @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())));
        }
    
        @Override
        public int hashCode() {
            return getDescription().hashCode();
        }
    }

     从源码中我们看到,它已经实现了Resource定义的功能。

     

    2 ResourceLoader统一资源定位

      Spring 将资源的定义和资源的加载区分开,Resource 定义统一的资源,那资源的加载则由 ResourceLoader 来统一定义。

    2.1 ResourceLoader源码

    package org.springframework.core.io;
    
    import org.springframework.lang.Nullable;
    import org.springframework.util.ResourceUtils;
    
    public interface ResourceLoader {
        String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
        Resource getResource(String location);
        @Nullable
        ClassLoader getClassLoader();
    }

    2.2 getResource()方法

      从源码中看到,ResourceLoader 接口提供两个方法:getResource()getClassLoader()

      getResource()根据所提供资源的路径 location 返回 Resource 实例,但是它不确保该 Resource 一定存在。

      该方法支持以下模式的资源加载:

        URL位置资源,如”file:C:/test.dat”
        ClassPath位置资源,如”classpath:test.dat”
        相对路径资源,如”WEB-INF/test.dat”,此时返回的Resource实例根据实现不同而不同

      该方法的主要实现是在其子类 DefaultResourceLoader 中实现

     

    2.3 getClassLoader()方法

      getClassLoader() 返回 ClassLoader 实例,对于想要获取 ResourceLoader 使用的 ClassLoader 用户来说,可以直接调用该方法来获取

     

    2.4 ResourceLoader的体系

      Spring 统一的资源加载器,它提供了统一的抽象,具体的实现则由相应的子类来负责实现,其类的类结构图如下

     

    2.5 DefaultResourceLoader

    2.5.1 DefaultResourceLoader源码

    public class DefaultResourceLoader implements ResourceLoader {
    
        @Nullable
        private ClassLoader classLoader;
    
        private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);
    
        private final Map<Class<?>, Map<Resource, ?>> resourceCaches = new ConcurrentHashMap<>(4);
    
    
        public DefaultResourceLoader() {
            this.classLoader = ClassUtils.getDefaultClassLoader();
        }
    
    
        public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
            this.classLoader = classLoader;
        }
    
    
    
        public void setClassLoader(@Nullable ClassLoader classLoader) {
            this.classLoader = classLoader;
        }
    
    
        @Override
        @Nullable
        public ClassLoader getClassLoader() {
            return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
        }
    
    
        public void addProtocolResolver(ProtocolResolver resolver) {
            Assert.notNull(resolver, "ProtocolResolver must not be null");
            this.protocolResolvers.add(resolver);
        }
    
    
        public Collection<ProtocolResolver> getProtocolResolvers() {
            return this.protocolResolvers;
        }
    
    
        @SuppressWarnings("unchecked")
        public <T> Map<Resource, T> getResourceCache(Class<T> valueType) {
            return (Map<Resource, T>) this.resourceCaches.computeIfAbsent(valueType, key -> new ConcurrentHashMap<>());
        }
    
    
        public void clearResourceCaches() {
            this.resourceCaches.clear();
        }
    
    
        @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;
                }
            }
    
            if (location.startsWith("/")) {
                return getResourceByPath(location);
            }
            else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
                return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
            }
            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) {
                    // No URL -> resolve as resource path.
                    return getResourceByPath(location);
                }
            }
        }
    
    
        protected Resource getResourceByPath(String path) {
            return new ClassPathContextResource(path, getClassLoader());
        }
    
    
        protected static class ClassPathContextResource extends ClassPathResource implements ContextResource {
    
            public ClassPathContextResource(String path, @Nullable ClassLoader classLoader) {
                super(path, classLoader);
            }
    
            @Override
            public String getPathWithinContext() {
                return getPath();
            }
    
            @Override
            public Resource createRelative(String relativePath) {
                String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
                return new ClassPathContextResource(pathToUse, getClassLoader());
            }
        }
    
    }

    2.5.2 DefaultResourceLoader简介

      DefaultResourceLoader 是 ResourceLoader 的默认实现,接收 ClassLoader 作为构造函数的参数或者使用不带参数的构造函数,在使用不带参数的构造函数时,使用的 ClassLoader 为默认的 ClassLoader(一般为Thread.currentThread().getContextClassLoader()),可以通过 ClassUtils.getDefaultClassLoader()获取。当然也可以调用 setClassLoader()方法设置使用的ClassLoader

      下面是它的构造方法

    public DefaultResourceLoader() {
            this.classLoader = ClassUtils.getDefaultClassLoader();
        }
    
        public DefaultResourceLoader(@Nullable ClassLoader classLoader) {
            this.classLoader = classLoader;
        }
    
        public void setClassLoader(@Nullable ClassLoader classLoader) {
            this.classLoader = classLoader;
        }
    
        @Override
        @Nullable
        public ClassLoader getClassLoader() {
            return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader());
        }

    2.5.3 它的核心方法getResource()

      ResourceLoader中最核心的方法为 getResource(),根据提供的 location 返回相应的 Resource,而 DefaultResourceLoader 对该方法提供核心实现,如下

    public Resource getResource(String location) {
            Assert.notNull(location, "Location must not be null");
    
            for (ProtocolResolver protocolResolver : this.protocolResolvers) {
                Resource resource = protocolResolver.resolve(location, this);
                if (resource != null) {
                    return resource;
                }
            }
    
            if (location.startsWith("/")) {
                return getResourceByPath(location);
            }
            else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
                return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
            }
            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) {
                    // No URL -> resolve as resource path.
                    return getResourceByPath(location);
                }
            }
        }

      1)首先通过 ProtocolResolver 来加载资源,成功返回 Resource,否则调用如下逻辑

      2)若 location 以 / 开头,则调用 getResourceByPath()构造 ClassPathContextResource 类型资源并返回。

      3)若 location 以 classpath: 开头,则构造 ClassPathResource 类型资源并返回,在构造该资源时,通过 getClassLoader()获取当前的 ClassLoader。

      4)构造 URL ,尝试通过它进行资源定位,若没有抛出 MalformedURLException 异常,有则判断是否为 FileURL , 如果是则构造 FileUrlResource 类型资源,否则构造 UrlResource。

      5)若在加载过程中抛出 MalformedURLException 异常,则委派 getResourceByPath() 实现资源定位加载

     

    2.5.4 ProtocolResolver介绍

      在getResource方法中,开始有这么一段代码

    for (ProtocolResolver protocolResolver : this.protocolResolvers) {
                Resource resource = protocolResolver.resolve(location, this);
                if (resource != null) {
                    return resource;
                }
            }

      this.protocolResolvers,它是一个set集合(ProtocolResolver)

    private final Set<ProtocolResolver> protocolResolvers = new LinkedHashSet<>(4);

      我们来看看ProtocolResolver 是什么

    2.5.4.1 ProtocolResolver 源码

    @FunctionalInterface
    public interface ProtocolResolver {
    
        @Nullable
        Resource resolve(String location, ResourceLoader resourceLoader);
    
    }

     

    2.5.4.2 ProtocolResolver 介绍 

      ProtocolResolver ,用户自定义协议资源解决策略,作为 DefaultResourceLoader 的 SPI,允许用户自定义资源加载协议,而不需要继承 ResourceLoader 的子类。在介绍 Resource 时,提到如果要实现自定义 Resource,我们只需要继承 DefaultResource 即可。而自定义ProtocolResolver ,有了 ProtocolResolver 后,不需要直接继承 DefaultResourceLoader,改为实现 ProtocolResolver 接口也可以实现自定义的 ResourceLoader,

      ProtocolResolver 接口,仅有一个方法 Resource resolve(String location, ResourceLoader resourceLoader),该方法接收两个参数:资源路径location和指定的加载器 ResourceLoader,返回为相应的 Resource 。在 Spring 中该接口并没有实现类,它需要用户自定义,自定义的 Resolver 如何加入 Spring 体系呢?调用 DefaultResourceLoader.addProtocolResolver() 即可,如下

    public void addProtocolResolver(ProtocolResolver resolver) {
            Assert.notNull(resolver, "ProtocolResolver must not be null");
            this.protocolResolvers.add(resolver);
        }

       也就是说我们可以通过实现ProtocolResolve来自定义我们自己的ResourceLoader

     

    2.6 FileSystemResourceLoader

      FileSystemResourceLoader ,它继承 DefaultResourceLoader 且覆写 getResourceByPath(String),使之从文件系统加载资源并以 FileSystemResource 类型返回,就可以得到想要的资源类型

    2.6.1 FileSystemResourceLoader源码

    public class FileSystemResourceLoader extends DefaultResourceLoader {
    
        @Override
        protected Resource getResourceByPath(String path) {
            if (path.startsWith("/")) {
                path = path.substring(1);
            }
            return new FileSystemContextResource(path);
        }
    
    
        private static class FileSystemContextResource extends FileSystemResource implements ContextResource {
    
            public FileSystemContextResource(String path) {
                super(path);
            }
    
            @Override
            public String getPathWithinContext() {
                return getPath();
            }
        }
    
    }

    2.7 ResourcePatternResolver

    2.7.1 源码

    public interface ResourcePatternResolver extends ResourceLoader {
    
        
        String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
    
    
        Resource[] getResources(String locationPattern) throws IOException;
    
    }

    2.7.2 简介

      ResourceLoader 的 getResource(String location)方法 每次只能根据 location 返回一个 Resource,当需要加载多个资源时,除了多次调用 getResource() 外别无他法。                           而ResourcePatternResolver 是 ResourceLoader 的扩展,它支持根据指定的资源路径匹配模式每次返回多个 Resource 实例

      ResourcePatternResolver 在 ResourceLoader 的基础上增加Resource[] getResources(String locationPattern)方法,以支持根据路径匹配模式返回多个 Resource 实例,同时也新增一种新的协议前缀 classpath*:,该协议前缀由其子类负责实现。

    2.8 PathMatchingResourcePatternResolver

    2.8.1 简介

      PathMatchingResourcePatternResolver 为 ResourcePatternResolver 最常用的子类,除了支持 ResourceLoader 和 ResourcePatternResolver 新增的 classpath*: 前缀外,还支持 Ant 风格的路径匹配模式(类似于 **/*.xml

    2.8.2 构造方法

      PathMatchingResourcePatternResolver 提供三个构造方法,如下:

    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);
        }

      PathMatchingResourcePatternResolver 在实例化的时候,可以指定一个 ResourceLoader,如果不指定的话,它会在内部构造一个 DefaultResourceLoader

    2.8.3 getResource(String location)方法

    @Override
        public Resource getResource(String location) {
            return getResourceLoader().getResource(location);
        }

      getResource() 方法中并没有自己来完成加载,而是直接委托给相应的其它的ResourceLoader 来实现,如果在实例化PathMatchingResourcePatternResolver 的时候,不知道 ResourceLoader ,那么在加载资源时,默认就是 DefaultResourceLoader来完成加载的

    2.8.4 getResources方法

    public Resource[] getResources(String locationPattern) throws IOException {
            Assert.notNull(locationPattern, "Location pattern must not be null");
    
            // 以 classpath*: 开头
            if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
                // 路径包含通配符
                if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
                    return findPathMatchingResources(locationPattern);
                }
                else {
                    // 路径不包含通配符
                    return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
                }
            }
            else {
                int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
                        locationPattern.indexOf(':') + 1);
                // 路径包含通配符
                if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
                    return findPathMatchingResources(locationPattern);
                }
                else {
                    return new Resource[] {getResourceLoader().getResource(locationPattern)};
                }
            }
        }

    其加载逻辑如下

     

     

     2.8.5 findAllClassPathResources()方法

      当 locationPattern 以 classpath*: 开头但是不包含通配符,则调用findAllClassPathResources() 方法加载资源。该方法返回 classes 路径下和所有 jar 包中的所有相匹配的资源

    protected Resource[] findAllClassPathResources(String location) throws IOException {
            String path = location;
            if (path.startsWith("/")) {
                path = path.substring(1);
            }
            Set<Resource> result = doFindAllClassPathResources(path);
            if (logger.isDebugEnabled()) {
                logger.debug("Resolved classpath location [" + location + "] to resources " + result);
            }
            return result.toArray(new Resource[0]);
        }

     doFindAllClassPathResources()方法

    protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
            Set<Resource> result = new LinkedHashSet<>(16);
            ClassLoader cl = getClassLoader();
            Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
            while (resourceUrls.hasMoreElements()) {
                URL url = resourceUrls.nextElement();
                result.add(convertClassLoaderURL(url));
            }
            if ("".equals(path)) {
                addAllClassLoaderJarRoots(cl, result);
            }
            return result;
        }

      doFindAllClassPathResources() 根据 ClassLoader 加载路径下的所有资源。在加载资源过程中,如果在构造 PathMatchingResourcePatternResolver 实例的时候,如果传入ClassLoader,则调用其 getResources(),否则调用ClassLoader.getSystemResources(path)。 ClassLoader.getResources()如下:

    public Enumeration<URL> getResources(String name) throws IOException {
            @SuppressWarnings("unchecked")
            Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
            if (parent != null) {
                tmp[0] = parent.getResources(name);
            } else {
                tmp[0] = getBootstrapResources(name);
            }
            tmp[1] = findResources(name);
    
            return new CompoundEnumeration<>(tmp);
        }

      findAllClassPathResources() 其实就是利用 ClassLoader 来加载指定路径下的资源。如果我们传入的路径为空或者 /,则会调用 addAllClassLoaderJarRoots() 方法加载所有的 jar 包

    2.8.6 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.isDebugEnabled()) {
                logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
            }
            return result.toArray(new Resource[0]);
        }

    主要分两步:

    1. 确定目录,获取该目录下得所有资源
    2. 在所获得的所有资源中进行迭代匹配获取想要的资源。

      一个是 determineRootDir(),一个是 doFindPathMatchingFileResources()

      determineRootDir()主要是用于确定根路径,如下:

    protected String determineRootDir(String location) {
            int prefixEnd = location.indexOf(':') + 1;
            int rootDirEnd = location.length();
            while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
                rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
            }
            if (rootDirEnd == 0) {
                rootDirEnd = prefixEnd;
            }
            return location.substring(0, rootDirEnd);
        }

    该方法一定要给出一个确定的根目录。该根目录用于确定文件的匹配的起始点,将根目录位置的资源解析为 java.io.File 并将其传递到 retrieveMatchingFiles()

     

     确定根路径后,则调用 getResources() 方法获取该路径下得所有资源,然后迭代资源获取符合条件的资源。

       

    3 Spring 整个资源记载过程简要总结下

      Spring 提供Resource 和 ResourceLoader 来统一抽象整个资源及其定位。使得资源与资源的定位有一个更加清晰的界限,并且提供合适的 Default 类,使得自定义实现更加方便和清晰。  

      DefaultResource 为 Resource 的默认实现,它对 Resource 接口做一个统一的实现,子类继承该类后只需要覆盖相应的方法即可,同时对于自定义的 Resource 也是继承该类

      DefaultResourceLoader 同样也是 ResourceLoader 的默认实现,在自定 义ResourceLoader 的时候除了可以继承该类外还可以实现 ProtocolResolver 接口来实现自定资源加载协议。  

      DefaultResourceLoader 每次只能返回单一的资源,所以 Spring 针对这个提供另外一个接口 ResourcePatternResolver ,该接口提供根据指定的 locationPattern 返回多个资源的策略。其子类 PathMatchingResourcePatternResolver 是一个集大成者的 ResourceLoader ,因为它即实现 Resource getResource(String location) 也实现 Resource[] getResources(String locationPattern)

  • 相关阅读:
    Python集成开发环境搭建
    Windows10 解决 “/”应用程序中的服务器错误
    Windows系统清除远程连接记录的方法
    C# 处理DateTime算法,取某月第1天及最后一天
    SQL语句
    3389远程连接爆破实战
    SQL Server常用SQL集合
    VS2013、VS2015中,新建项目没有看到解决方案的问题(已解决)
    我的黑苹果装机篇
    C# 使用GZip对字符串压缩和解压
  • 原文地址:https://www.cnblogs.com/jthr/p/15904651.html
Copyright © 2020-2023  润新知