【SpringBoot笔记9】Resource接口

参考:官方文档

1 简介

Java标准库中的java.net.URL类和标准处理器对于处理低层的资源没有提供很好的功能。例如,并没有提供一个URL的实现能够从classpath或者ServletContext中读取资源等等。因此,在Spring中提供了这样一个Resource接口,能够更加方便的读取各种资源。

2 Resource接口

Spring提供的Resource接口,是对第低层资源访问进行的一个抽象,提供能方便的使用。

下面是org.springframework.core.io.Resource接口的定义:

public interface Resource extends InputStreamSource {

	/**
	 * 判断资源在物理上是否存在
	 */
	boolean exists();

	/**
	 * 表明该资源中的非空内容是否可以通过getInputStream()读取
	 */
	default boolean isReadable() {
		return exists();
	}

	/**
	 * 表明该资源是否被一个打开的stream处理
	 */
	default boolean isOpen() {
		return false;
	}

	/**
	 * 判断该资源是否代表文件系统中的一个文件
	 */
	default boolean isFile() {
		return false;
	}

	/**
	 * 返回该资源的URL
	 */
	URL getURL() throws IOException;

	/**
	 * 返回该资源的URI
	 */
	URI getURI() throws IOException;

	/**
	 * 返回该资源对应的File
	 */
	File getFile() throws IOException;

	/**
	 * 返回一个ReadableByteChannel(可读的字节流通道)
	 */
	default ReadableByteChannel readableChannel() throws IOException {
		return Channels.newChannel(getInputStream());
	}

	/**
	 * 返回资源中内容的长度
	 */
	long contentLength() throws IOException;

	/**
	 * 返回该资源的最后修改时间
	 */
	long lastModified() throws IOException;

	/**
	 * 创建一个和该资源相关的资源
	 */
	Resource createRelative(String relativePath) throws IOException;

	/**
	 * 返回文件名
	 */
	@Nullable
	String getFilename();

	/**
	 * 返回资源的描述
	 */
	String getDescription();

}

Resource接口继承的InputStreamSource接口中海油一个非常重要的方法:

public interface InputStreamSource {

	/**
	 * 找到并打开资源,返回读取资源内容的InputStream. 每次调用返回一个新的InputStream.
	 */
	InputStream getInputStream() throws IOException;

}

3 Spring中内置的Resource接口的实现类

Spring中包含了如下几个Resource接口的实现类:

  • UrlResource:从URL获取资源。
  • ClassPathResource:从classpath获取资源。
  • FileSystemResource:从文件系统获取资源。
  • ServletContextResource:从servlet上下文获取资源。
  • InputStreamResource:从InputStream获取资源。
  • ByteArrayResource:从字节数组获取资源。

在这里插入图片描述

UrlResource

UrlResource是对java.net.URL的封装,可以被用来访问任何可以通过URL访问的资源对象,例如文件、HTTP目标对象、FTP目标对象等等。

每种类型的URL都有表示该类型资源的前缀。例如file:表示访问文件系统的URLhttp:表示通过http协议访问;ftp:表示通过ftp协议访问。

ClassPathResource

ClassPathResource表示从classpath中获取资源。

FileSystemResource

FileSystemResource是对java.io.Filejava.nio.file.Path的封装,代表从文件系统中读取文件。

ServletContextResource

ServletContextResource是为了方便你从ServletContext中获取资源而设计的,可以从相对于web应用程序的根目录中获取资源。

InputStreamResource

InputStreamResource是用来从给定的InputStream中获取资源的。

ByteArrayResource

ByteArrayResource用来从给定的字节数组中获取资源,它会创建一个ByteArrayInputStream

4 ResourceLoader

ResourceLoader接口是用来加载资源的,它提供了一个getResource函数用来返回一个Resource对象。下面是它的定义:

public interface ResourceLoader {

	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;

	Resource getResource(String location);

	@Nullable
	ClassLoader getClassLoader();
}

所有的应用程序上下文都实现了ResourceLoader接口,因此可以从应用程序上下文中获取Resource对象:

Resource template = ctx.getResource("some/resource/path/myTemplate.txt");

如果在调用getResource()的时候,指定的资源路径上没有给出前缀,那么Spring会根据context的类型返回一个合适类型的资源对象。例如,在ClassPathXmlApplicationContext对象上调用getResource()函数,则会返回一个ClassPathResource对象。

如果你指定了前缀,那么不管context是什么类型,都将返回前缀对应的资源类型,例如:

Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");

下表总结了资源路径对应的资源类型:

前缀 例子 说明
classpath: classpath:com/myapp/config.xml classpath加载资源。
file: file:///data/config.xml 从文件系统加载资源。
http: http://myserver/logo.png 使用http协议加载资源。
(none) /data/config.xml 根据context类型判断。

5 ResourceLoaderAware接口

ResourceLoaderAwareSpring提供的一个回调接口,用于注入ResourceLoader

public interface ResourceLoaderAware extends Aware {

	void setResourceLoader(ResourceLoader resourceLoader);
}

如果一个类实现了ResourceLoaderAware接口并在Spring上下文中注册为一个bean,那么context会调用它的setResourceLoader()方法将context本身设置进去(因为所有的context都实现了ResourceLoader接口)。

下面是一个例子:

@Component
public class ResourceBean implements ResourceLoaderAware { 
    
    private ResourceLoader resourceLoader;
    
    @Override 
    public void setResourceLoader(ResourceLoader resourceLoader) { 
        this.resourceLoader = resourceLoader; 
    }
    
    public ResourceLoader getResourceLoader() { 
        return resourceLoader; 
    }
} 
@Test 
public void test() { 
    ApplicationContext ctx = new ClassPathXmlApplicationContext("test/resourceLoaderAware.xml"); 
    ResourceBean resourceBean = ctx.getBean(ResourceBean.class); 
    ResourceLoader loader = resourceBean.getResourceLoader(); 
    Assert.assertTrue(loader instanceof ApplicationContext); 
} 

上述方法采用实现ResourceLoaderAware接口的方式注入ResourceLoader,属于侵入式的方法。从Spring 2.5之后,可以直接通过@Autowired自动注入的方式注入ResourceLoader

6 应用个上下文和资源路径

6.1 构造一个应用上下文

应用上下文的构造函数通常将资源的路径以一个字符串或者字符串数组传入。当路径没有前缀的时候,资源的类型依据上下文的类型构建。

下面的例子,通过资源的路径构造一个ClassPathXmlApplicationContext上下文对象:

ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");

下面的例子,根据资源路径构造一个FileSystemXmlApplicationContext上下文对象:

ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");

也可以通过路径前缀指定具体资源的类型。

6.2 资源路径支持通配符

可以通过*来表示一批资源文件:

/WEB-INF/*-context.xml
com/mycompany/**/applicationContext.xml
file:C:/some/path/*-context.xml
classpath:com/mycompany/**/applicationContext.xml

6.3 classpath*:和classpath:

classpath*:classpath:的主要区别体现在:

  • classpath::只会在当前项目的WEB-INF/classes路径下查找文件。
  • classpath*::不只在当前羡慕的WEB-INF/classes路径下查找文件,还会到第三方jar文件的WEB-INF/classes查找文件。

6.4 FileSystemResource的路径问题

为了兼容,FileSystemXmlApplicationContext中的相对路径和绝对路径都将视为相对路径,相对于当前项目的工作目录。

下面两个是等价的:

ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("/conf/context.xml");

如果真的需要绝对路径,应该尽量使用UrlResource并通过file:前缀来指定:

// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx = new FileSystemXmlApplicationContext("file:///conf/context.xml");

猜你喜欢

转载自blog.csdn.net/hbtj_1216/article/details/85487787