Spring资源加载机制介绍
Java标准APIjava.net.URL
能够处理URL资源,但是却不能处理”低等级”的资源,比如,没有标准化的URL
实现用来加载类路径的资源, 虽然可以注册用于特殊URL前缀的新处理程序(类似于用于诸如http:的现有前缀的处理程序),但这通常相当复杂,并且URL接口仍然缺少某些理想的功能,例如用于检查是否存在的方法 指向的资源。
Resource接口定义
Spring的Resource
接口抽象出了处理低级资源的方法:
public interface Resource extends InputStreamSource {
boolean exists();
boolean isOpen();
URL getURL() throws IOException;
File getFile() throws IOException;
Resource createRelative(String relativePath) throws IOException;
String getFilename();
String getDescription();
}
Resource
集成了InputStreamSource
接口:
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
Resource
中比较重要的方法是:
getInputStream()
: 定位并打开资源,返回InputStream
用于读取资源。作为调用方必须在使用完后关闭这个流。exists()
: 返回boolean
值,判断资源是否存在.isOpen()
: 返回boolean
值,判断当前资源是否已经在被处理,如果返回true
,InputStream
不能同时被调用,同一时间只能有一个调用者,并且必须被关闭防止资源。常规资源的实现通常返回false
。getDescription()
: 通常用于处理异常时,返回该resource的说明。结果通常是返回文件的完整名称或是资源的实际URL
如果其底层实现支持的话,其他的方法则可以获取URL
和File
对象。
Spring本身许多需要操作资源的方法签名都是使用Resource
接口,还有一部分Spring的API接收资源的路径,并且会根据路径的前缀自动创建合适的Resource
对象。
尽管Spring经常使用Resource
接口,但实际上,在自己的代码中单独使用Resource
接口类作为通用实用工具类来访问资源非常有用,即使我们的代码不了解或不关心其他Spring的任何东西
Resource
API并没有替换任何功能,只是在相关的场景进行包装,比如,UrlResource
包装了URL
,并且实际上是使用这个URL
来进行相关操作
Spring提供的Resource
实现
UrlResource
ClassPathResource
FileSystemResource
ServletContextResource
InputStreamResource
ByteArrayResource
UrlResource
UrlResource
包装了java.net.URL
,可以被用来获取常规资源,比如:文件、http资源、ftp资源,所有的URL都使用String
进行表述,并且都包含了适当的资源路径前缀。file:
用于读取文件系统,http:
获取Http协议资源,ftp:
获取FTP文件等
UrlResource
可以通过构造器显式的创建,但是通常都是调用的Spring API接收String
类型的路径参数自动创建合适的Resource
实现类,当其无法识别路径前缀才创建UrlResource
。
ClassPathResource
ClassPathResource
用于加载类路径的资源,使用当前线程的类加载器或给定的类加载器进行资源加载。如果类路径资源已经被解压到文件系统,则会将资源加载为java.io.File
,为了解决这个问题,许多Resource
的实现总是支持解析为java.net.URL
FileSystemResource
支持处理 java.io.File
和 java.nio.file.Path
。
ServletContextResource
ServletContext资源的Resource
实现,用于解析相关Web应用程序根目录中的相对路径。
它始终支持流访问和URL
访问,但仅在Jar包被解压才允许java.io.File
访问。 不管是解压还是直接访问文件系统、或者直接从JAR或其他类似数据库访问,取决于Servlet容器。
InputStreamResource
适用于给定的InputStream
,应该被用于未指定的Resource
的情况下进行适配。通常情况下isOpen()
返回true
,意味着不能多次读取流。
ByteArrayResource
将会创建一个ByteArrayResource
基于给定的字节数组,比使用InputStreamResource
更有效。
ResourceLoader
接口
ResourceLoader
接口需要被那些能够加载资源的对象实现:
public interface ResourceLoader {
Resource getResource(String location);
}
所有的ApplicationContext
都实现了ResourceLoader
接口,因此可以被用来获取相关的Resource
实例。
更具具体的ApplicationContext
实现类,通常不需要加资源路径的前缀,比如通过ClassPathXmlApplicationContext
加载:
public void loadresource(){
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
}
以上代码将返回ClassPathResource
,如果换成FileSystemXmlApplicationContext
,将会得到FileSystemResource
,而对于WebApplicationContext
来说,将会返回ServletContextResource
。
如果需要强制使用类路径加载则也可以指定路径前缀classpath:
public void loadresource(){
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
}
同样的,指定使用UrlResource
需要指定file
或 http
前缀:
public void loadresource(){
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
}
资源前缀表:
前缀 | 案例 | 解析 |
---|---|---|
classpath: | classpath:com/myapp/config.xml | 从类路径加载 |
file: | file:///data/config.xml | 作为 URL 从文件系统加载 |
http: | https://myserver/logo.png | 作为 URL 加载 |
(none) | /data/config.xml | 具体由ApplicationContext 实现决定 |
ResourceLoaderAware
接口
ResourceLoaderAware
是一个特殊的回掉接口,任何实现这个接口的bean意味着bean需要获取ResourceLoader
的引用。
public interface ResourceLoaderAware {
void setResourceLoader(ResourceLoader resourceLoader);
}
Spring ioC容器会自动检测到所有实现ResourceLoaderAware
接口的bean并且回掉setResourceLoader(ResourceLoader)
方法,为当前bean设置ResourceLoader
引用。另一种获取ResourceLoader
的方式是通过@Autowired
自动装配。