Spring源码分析——资源文件加载

Spring 源码分析——资源文件加载

本篇文章讨论 Spring 源码中如何加载和解析资源文件。

对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/04-resource-loader

之前我们提到,在 Spring 中 Bean 的生命周期第一步是需要定位加载,而这种定位方式有多种,其中从配置文件中加载 Bean 就是一种常见的方式,那么就涉及到 Spring 读取指定配置文件并加载到项目中。

在 Spring 中提供了Resource接口用于处理项目运行时需要加载的任何资源,Resource 接口的主要实现类有如下:

  • UrlResource:表示通过 URL 定位资源,可以通过 HTTP URL 或者本地文件路由获取资源;
  • ClassPathResource:表示通过 Classpath 定位资源,在 SpringBoot 项目中classpath:表示项目的resources目录;
  • FileSystemResource:表示通过文件路径定位资源,可以是项目的相对或绝对路径;
  • ServletContextResource:表示通过 ServerContext 定位资源,在 JavaWeb 项目中表示WEB-INF目录;

其中,Resource 对文件流的处理提供了两个接口:

  • InputStreamResource:基于输入流的 Resource 实现接口,接受一个 InputStream 参数,用于将输入流封装成 Resource 对象;
  • WriteableResource:可写的 Resource 实现接口,表示可以写入数据的资源,提供write()函数将数据写入指定数据流;

设计模式

Spring 对于 Resource 的设计实现中用到了很多设计模式;这里我们先按照上面的讲解设计几个实现类:

// ClassPath资源加载
public class ClassPathResource implements Resource {

    private final String path;

    public ClassPathResource(String path) {
        this.path = path;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        // 从ClassLoader拿到此path对应的输入流
    }
}

// Url资源加载
public class UrlResource implements Resource {

    private final URL url;

    public UrlResource(URL url) {
        this.url = url;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        // URL对象中拿到网络连接输入流
    }
}

// 系统资源加兹安
public class FileSystemResource implements Resource {

    private final String filePath;

    public FileSystemResource(String filePath) {
        this.filePath = filePath;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        // 从磁盘路径获取文件输入流
    }
}

工厂模式

工厂模式:将对象创建过程抽象出来,定义一个抽象工厂接口,让具体的工厂子类负责对象的创建,在客户端和实现创建类之间建立一个间接层,避免客户端直接依赖具体的实现类。

简单说,工厂模式就是通过定义工厂接口,客户端调用工厂接口,工厂接口的实现交由子类负责。

在 Resource 接口的工厂模式具体体现是ResourceLoader接口:

public interface ResourceLoader {
    Resource getResource(String location);
}

ResourceLoader接口定义了获取资源的方法,而具体获取资源的实现交由Resource接口的实现类实现。也就是只需要调用 ResourceLoader 接口获取资源即可,location代表具体文件路径。例如通过:

getResource("classpath:test.xml")
getResource("http://tycoding.cn/test.xml")

那么判断classpath:http://分别使用那种 Resource 实现去获取资源则交由 ResourceLoader 接口的实现类实现,

策略模式

策略模式:策略模式是一种行为性模式,定义了一些列算法并封装起来,根据客户端的调用,通过不同的策略调用不同的实现。

在 Rresource 接口的实现上,策略模式可以认为是对工厂模式的进一步封装实现,通过不同的策略去查找使用哪种具体的工厂去实现。

在 Spring 对 Resource 的实现上,策略模式的实现可以看ResourcePatternResolver接口,继承至 ResourceLoader 接口。ResourceLoader 工厂模式被用于查找和实现,而这个查找匹配过程交由策略类实现,通过一系列的策略决定选择使用哪个工厂选择符合条件的资源。

public interface ResourcePatternResolver extends ResourceLoader {
    Resource[] getResources(String locationPattern) throws IOException;
}

例如:

ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath*:**/*.xml");

ResourcePatternResolver 接口的默认实现是 PathMatchingResourcePatternResolver,支持 Ant-style 路径匹配的策略类。

因为要获取classpath下的资源,所以 PathMatchingResourcePatternResolver 策略类匹配到所有 xml 结尾的文件后,最终还会通过ClassPathResource类获取 Resource 资源,伪代码如下:

protected Resource getResource(String path) {
    if (path.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
        return new ClassPathResource(path.substring(CLASSPATH_ALL_URL_PREFIX.length()), getClassLoader());
    }
    ...
}

DefaultResourceLoader

ResourceLoader 接口也有一个默认实现类:DefaultResourceLoader。在手写 Spring 的项目:https://github.com/TyCoding/mini-spring 中我们可以用此类做一个简单的实现:

public class DefaultResourceLoader implements ResourceLoader {

    public static final String CLASSPATH_URL_PREFIX = "classpath:";

    @Override
    public Resource getResource(String location) {
        if (location.startsWith(CLASSPATH_URL_PREFIX)) {
            // 从本地相对路径获取
            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));
        } else {
            try {
                // 作为URL处理
                URL url = new URL(location);
                return new UrlResource(url);
            } catch (MalformedURLException e) {
                // 从磁盘地址获取
                return new FileSystemResource(location);
            }
        }
    }
}

可以看到,其实就是根据某些字符串匹配条件,匹配的资源路径将会调用对应的 Resource 工厂类实现获取资源的逻辑。

Spring 源码专栏

此专栏将从 Spring 源码角度整体分析 Spring 设计思路以及常见的面试题

配套作者的手写 Spring 的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关 Spring 源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs

联系我

  • 个人博客:http://tycoding.cn/
  • GitHub:https://github.com/tycoding
  • 微信公众号:程序员涂陌


原文始发于微信公众号(程序员涂陌):Spring源码分析——资源文件加载

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145605.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!