Spring Resource
Spring Resource用于处理程序读取文件的业务。
学习之前,首先需要了解文件在Java工程中的几种情况:
- 文件在电脑上的某个位置,如D:/java/jdk1.8;
- 文件在工程目录下,如mywork/a.png;
- 文件在工程的src/main/resources目录下,这里是用于存放Maven文件的地方。
前两种情况我们使用File对象就可以进行读写,但第三种比较特殊,因为Maven在打包工程的时候会将resources目录下的文件一起打包进jar包,这种情况我们使用File对象是读取不到的。
例如我们读取下图的两个文件:
代码如下:
import java.io.File;
/**
* Test
*/
public class Test {
public static void main(String[] args) {
File file = new File("mywork/readme.md");
if(file.exists()){
System.out.println("readme.md 文件存在");
}
File file2 = new File("src/main/resources/nani.jpeg");
if(file2.exists()){
System.out.println("nani.jpeg 文件存在");
}
}
}
此时我们是可以读取到两个文件的。但是在大多数情况下我们并没有清晰的文件目录,而是导入jar包:
这是我们无法找到src/main/resources/nani.jpeg
文件。
使用解压工具将jar包解压,我们会发现目录出现了变化:
这就是 Java 文件系统和计算机文件系统的差异,工程目录最后是要编译成 jar 文件的,jar文件是从包路径
开始的。Maven 工程编译后,会自动去掉 src/main/java
、src/main/resources
目录。
此时,我们需要借助classpath读取文件。
classpath
在 Java 内部当中,我们一般把文件路径称为 classpath,所以读取内部的文件就是从 classpath 内读取,classpath 指定的文件不能解析成 File 对象,但是可以解析成 InputStream,我们借助 Java IO 就可以读取出来了。
想要实现classpath内读取文件,我们可以使用 commons-io 这个库来,需要我们在 pom.xml 下添加依赖:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
测试代码如下:
public class Test {
public static void main(String[] args) {
// 读取 classpath 的内容
InputStream in = Test.class.getClassLoader().getResourceAsStream("data.json");
// 使用 commons-io 库读取文本
try {
String content = IOUtils.toString(in, "utf-8");
System.out.println(content);
} catch (IOException e) {
// IOUtils.toString 有可能会抛出异常,需要我们捕获一下
e.printStackTrace();
}
}
}
其中最核心的就是这一段代码:
InputStream in = Test.class.getClassLoader().getResourceAsStream("data.json");
这段代码的含义是指在当前的Test.java文件编译后的Test.class文件中Java运行的类加载器(ClassLoader)实例中查找文件。
那么,我们用Spring Resources能做什么呢?
我们可以使用org.springframework.core.io.Resource
类来封装文件,这个类的优势在于可以支持普通的File文件也可以支持classpath文件,并且在Spring中通过org.springframework.core.io.ResourceLoader
服务来提供任意文件的读写,通过在任意的Spring Bean中引入ResourceLoader:
@Autowired
private ResourceLoader loader;
现在让我们来看一下在 Spring 当中如何读取文件,我们创建一个自己的 FileService:
public interface FileService {
String getContent(String name);
}
之后创建实现类:
import fm.douban.service.FileService;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
@Service
public class FileServiceImpl implements FileService {
@Autowired
private ResourceLoader loader;
@Override
public String getContent(String name) {
try {
InputStream in = loader.getResource(name).getInputStream();
return IOUtils.toString(in,"utf-8");
} catch (IOException e) {
return null;
}
}
}
之后通过依赖注入调用服务:
FileService fileService = context.getBean(FileService.class);
String content = fileService.getContent("classpath:data/urls.txt");
System.out.println(content);
他的功能不仅于此,它还可以读取本地或工程内的文件:
String content2 = fileService.getContent("file:mywork/readme.md");
System.out.println(content2);
甚至还能读取远程文件:
String content2 = fileService.getContent("https://www.zhihu.com/question/34786516/answer/822686390");
System.out.println(content2);
总而言之,在 Spring Resource 当中,可以把本地文件、classpath文件、远程文件都封装成 Resource 对象来统一加载,这就是它的强悍的地方。