Resource
接口定义如下
package org.springframework.core.io;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import org.springframework.lang.Nullable;
public interface Resource extends InputStreamSource {
boolean exists();
default boolean isReadable() {
return this.exists();
}
default boolean isOpen() {
return false;
}
default boolean isFile() {
return false;
}
URL getURL() throws IOException;
URI getURI() throws IOException;
File getFile() throws IOException;
default ReadableByteChannel readableChannel() throws IOException {
return Channels.newChannel(this.getInputStream());
}
long contentLength() throws IOException;
long lastModified() throws IOException;
Resource createRelative(String var1) throws IOException;
@Nullable
String getFilename();
String getDescription();
}
如果你读过Spring源码,那么对Resource接口你就不会陌生,特别在资源加载环节,到处是Resource的身影。
常见Resource
- UrlResource:包装了java.net.URL,可以访问通过URL访问的任何对象。比如:文件、Http链接地址、FTP等等。
- ClassPathResource:从类路径中获取的资源。它使用线程上下文类加载器、给定的类加载器或给定的类来加载资源。
- FileSystemResource:支持file:///开头的读取系统文件(如:file:///c:/work/aaa.txt)
直接使用Resource对象
@Test
public void testUrlResource(){
try {
// 读取CSDN博客文章
UrlResource urlResource = new UrlResource("");
print(urlResource);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
@Test
public void testClassPathResource() {
// 读取resource目录下(classPath根路径)的test.properties文件
ClassPathResource classPathResource = new ClassPathResource("test.properties");
print(classPathResource);
}
@Test
public void testFileSystemResource() {
// 读取系统文件c:/work/aaa.txt
FileSystemResource classPathResource = new FileSystemResource("c:/work/aaa.txt");
print(classPathResource);
}
public void print(Resource resource) {
try (BufferedReader r = new BufferedReader(new InputStreamReader(resource.getInputStream()))){
String str;
while ((str = r.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
如果aaa.txt文件中包含中文,我们的代码打印可能会出现乱码,请使用InputStreamReader的带编码参数的构造函数来指定编码格式。
注:打印代码中我们使用了try-with-resouce机制(jdk1.7版本及以上才支持)
ResourceLoader
接口定义如下
package org.springframework.core.io;
import org.springframework.lang.Nullable;
public interface ResourceLoader {
String CLASSPATH_URL_PREFIX = "classpath:";
Resource getResource(String var1);
@Nullable
ClassLoader getClassLoader();
}
ResourceLoader定义了如何获取Resource,它支持一些特定的前缀,如"classpath:"等。
我们前面是有的ApplicationContext都实现了该接口。意味着我们可以通过context.getResource方法来获取我们想要的Resource对象。
@Test
public void ctxTest1() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource resource = context.getResource("");
System.out.println(resource.getClass());// UrlResource
print(resource);
}
@Test
public void ctxTest2() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource resource = context.getResource("file:///c:/work/aaa.txt");
System.out.println(resource.getClass()); // FileUrlResource
print(resource);
}
@Test
public void ctxTest3() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource resource = context.getResource("test.properties");
System.out.println(resource.getClass()); // DefaultResourceLoader$ClassPathContextResource
print(resource);
}
@Test
public void ctxTest4() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource resource = context.getResource("classpath:test.properties");
System.out.println(resource.getClass()); // ClassPathResource
print(resource);
}
可以看到我们使用了同样的方法,但是却得到了不同的Resource实现,根据我们的String参数的不同得到的Resource实现也不同。我们示例中有https开头、file开头、classpath:开头,对应得到的Resource实现也不一样。这是由PropertyEditor最终决定的。PropertyEditor我们将在后续讲解。
ResourcePatternResolver接口
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
上面实例中我们使用了classpath:,ResourcePatternResolver接口中定义了calasspath*:,它们有什么区别吗。我们先来看看如下的demo
@Test
public void ctxTest5() throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource[] resource = context.getResources("classpath*:test.properties");
System.out.println(resource[0].getClass()); // UrlResource
print(resource[0]);
}
@Test
public void ctxTest6() throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
Resource[] resource = context.getResources("classpath*:*.properties");
if(resource != null) {
System.out.println(resource.length);
for(int i = 0 ; i < resource.length;i++) {
System.out.println("========================");
System.out.println(resource[i].getFilename());
System.out.println(resource[i].getClass());
print(resource[i]);
}
}
}
注意:
- classpath*:,需要使用getResources方法
- getResource方法使用classpath*:将会报错
- classpath*:可以获取在依赖的jar包中的资源
- classpath*:可以与通配符共用来实现模糊匹配,如classpath:spring-*.properties
- classpath:不能与*通配符共用。
使用注解来获取Resource对象
package com.yyoo.boot.annotation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
@Component
public class MyResourceBean {
@Value("${my.resource.path}")
public Resource[] resources;
public Resource[] getResources(){
return this.resources;
}
}
注意:我们的示例配置my.resource.path = classpath*:test.properties,如果路径是classpath*:,则需要使用Resource[] 数组接收,否则会报错。
注意:将Resource这里的资源获取跟@PropertySource注解导入配置区分开来。Resource可以不止是Properties配置文件,还可以是其他任何的https、ftp或本地、classpath路径下的任意文件。
总结
我们在使用中尽量不要自己new Resource,通过ApplicationContext的getResource或getResources方法获取,或者直接使用@Value注解配合配置文件来实现。
classpath*:支持通配符匹配,而且可以扫描不同位置的多个同名资源。