整理下 Spring 项目中读取 resources 下的文件的方式,包括 Resource 接口,另外跟一下 ResourceUtils 读取资源文件的流程。
File 方式读取
直接使用 File 类的接口读取资源文件,需要注意的是传入应是相对路径,生成的项目文件中资源文件的路径可以参考 target 目录下的结构。比如,资源文件目录下的文件 resources/static/test.html,在生成的 target 目录下路径是:target/classes/static/test.html
所以可以用以下方式访问:
1 |
|
要注意,使用相对路径,使用绝对路径(“/”代表根目录)
1 | /target/classes/static/test.html |
或者
1 | classes/static/test.html |
会 “file not found”。
Resource 接口与 ResourceLoader
Resource 接口抽象了资源文件,提供了 exists()、isFile()、getFile()、getURL() 等方法,并从 InputStreamSource 接口继承了 getInputStream() 方法。InputStreamSource 接口的 getInputStream() 方法保证多次调用此方法时,每次得到的是一个”fresh”的输入流。Resource 接口有对应各种类型资源的实现类,如 ClassPathResource、FileSystemResource、UrlResource 等。
ResourceLoader 接口中定义了 getResource(String location) 方法,用于获取一个 Resource,并且规定其实现需要支持如”file:C:/test.dat”、”classpath:test.dat”这样的前缀的 URL。另,一般其实现类 ApplicationContext 支持使用如”WEB-INF/test.dat”的相对路径读取文件。
getResource(String location) 不返回 null,对于返回的 Resource 对象,可以使用 exists() 判断资源是否存在。
ResourceLoader 接口的实现有:
DefaultResourceLoader,子类包括 ClassRelativeResourceLoader、FileSystemResourceLoader、ServletContextResourceLoader等;
ResourcePatternResolver 接口,实现中需要关注的是 ApplicationContext 接口。
当使用了某种协议前缀时,ResourceLoader 返回相应的 Resource 的实现对象,或者不指定时,将根据使用的 ApplicationContext 决定,这是策略模式的一个应用。
注册一个 ResourceLoaderAware 接口的实现类,可以从其回调 setResourceLoader(ResourceLoader resourceLoader) 中得到 ResourceLoader 实例。
使用如下代码可以读取资源文件:
1 | Resource r = ApplicationContextUtil.getApplicationContext().getResource("classpath:static/test.html"); |
其中,ApplicationContextUtil 实现了 ApplicationContextAware 接口,来获取 ApplicationContext。
使用 Spring 提供的 ResourceUtils 读取资源文件
ResourceUtils 提供了 getFile(URL)、getFile(String)方法。但是,类的文档中说明,这一类主要在框架内使用,开发者读取资源文件建议使用的是 ResourceLoader 和 Resource 接口。使用如下:
1 | File file = ResourceUtils.getFile("classpath:static/test.html"); |
或者
1 | File file = ResourceUtils.getFile("file:target/classes/static/test.html"); |
或者
1 | File file = ResourceUtils.getFile("target/classes/static/test.html"); |
使用”classpath:”协议和”file:”协议或者直接作为文件路径时,也要使用相对路径。
getFile(String) 方法对以上三种方式,其实现 DefaultResourceLoader 中的实现基本一致,可以看下其代码:
1 | public static File getFile(String resourceLocation) throws FileNotFoundException { |
首先检查参数 resourceLocation 是否是使用了”classpath:”的方式,使用了这种方式,使用 ClassLoader 类的 getResource(String) 方法读取文件。
否则尝试将作为其他协议的 URL 读取,如果使用的是”file:”之外的其他协议的 URL,抛出 FileNotFoundException 异常;上文使用”file:”的方式即作为”file:”协议的情况处理,提取 URL 中的文件路径初始化 File 对象。
最后,如果在尝试将 resourceLocation 解析为 URL 时失败,即不是 URL 的情况,URL构造器会抛出 MalformedURLException 异常,此时将尝试把 resourceLocation 作为文件路径读取。
打包为 jar 时的资源读取
以上示例,是在 IDE 中执行代码时的情况,当把项目打包成 jar 包时,在文件系统中无法找到相应文件,会出现 FileNotFoundException 异常。此时可以使用 InputStreamSource 接口的 getInputStream() 方法来读取资源文件。
代码如下:
1 | Resource r = ApplicationContextUtil.getApplicationContext().getResource("classpath:static/test.html"); |