一 、前言
Spring容器在启动的过程中,很多环节都需要加载一些资源文件,例如xml配置文件和properties配置文件等等。Java提供了标准java.net.URL用于资源访问,然而URL不足以满足各种资源文件的访问,例如没有办法访问类相对路径或者相对于ServletContext的资源等,Spring 提供一个Resource接口来统一这些底层资源一致的访问,并提供了对应的资源加载器用于加载对应的资源。以下为Spring官方对于Resource的介绍
官方文档:Java’s standard java.net.URL class and standard handlers for various URL prefixes, unfortunately, are not quite adequate enough for all access to low-level resources. For example, there is no standardized URL implementation that may be used to access a resource that needs to be obtained from the classpath or relative to a ServletContext. While it is possible to register new handlers for specialized URL prefixes (similar to existing handlers for prefixes such as http:), this is generally quite complicated, and the URL interface still lacks some desirable functionality, such as a method to check for the existence of the resource being pointed to.
Spring’s Resource interface is meant to be a more capable interface for abstracting access to low-level resources.
二、Spring 资源介绍
Spring中所有的资源访问最终都会被抽象为Resource的形式,如classpath:xxxx,xml配置文件,application.propoerties等。
2.1 资源文件的接口定义
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();
}
public interface InputStreamSource {
InputStream getInputStream() throws IOException;
}
2.1.1 getInputStream()
定位并打开资源,以输入流InputStream的形式返回。接口每次调用都应该返回一个新的InputStream。调用者在需要在读取数据之后关闭流.
getInputStream(): Locates and opens the resource, returning an InputStream for reading from the resource. It is expected that each invocation returns a fresh InputStream. It is the responsibility of the caller to close the stream.
2.1.2 exists()
判断资源是不是真实存在的.
exists(): Returns a boolean indicating whether this resource actually exists in physical form.
2.1.3 isOpen()
用于判断资源是不是被输入流占用,仅仅当一个资源无法被同时访问的时候才有可能返回true。
isOpen(): Returns a boolean indicating whether this resource represents a handle with an open stream. If true, the InputStream cannot be read multiple times and must be read once only and then closed to avoid resource leaks. Returns false for all usual resource implementations, with the exception of InputStreamResource.
ClassPathResource, 代表classpath路径的资源,将使用ClassLoader进行加载资源。lasspath 资源存在于类路径中的文件系统中或jar包里,且“isOpen”永远返回false,表示可多次读取资源。
2.1.4 getDescription()
资源的详细描述信息, 如资源的全称或者url等.
getDescription(): Returns a description for this resource, to be used for error output when working with the resource. This is often the fully qualified file name or the actual URL of the resource.
2.2 Spring的资源类型
2.2.1 UrlResource
UrlResource用于加载能够使用url表述的资源,如文件系统(file:)、http资源(http:)和ftp资源(ftp:)等,对于一些无法识别的资源类型,也会尝试使用UrlResource进行加载.
UrlResource wraps a java.net.URL and can be used to access any object that is normally accessible with a URL, such as files, an HTTP target, an FTP target, and others. All URLs have a standardized String representation, such that appropriate standardized prefixes are used to indicate one URL type from another. This includes file: for accessing filesystem paths, http: for accessing resources through the HTTP protocol, ftp: for accessing resources through FTP, and others.
A UrlResource is created by Java code by explicitly using the UrlResource constructor but is often created implicitly when you call an API method that takes a String argument meant to represent a path. For the latter case, a JavaBeans PropertyEditor ultimately decides which type of Resource to create. If the path string contains well-known (to it, that is) prefix (such as classpath:), it creates an appropriate specialized Resource for that prefix. However, if it does not recognize the prefix, it assume the string is a standard URL string and creates a UrlResource.
2.2.2 UrlResource
用于加载类文件相对位置下的资源(classpath:),使用上下文的类加载器或指定的类加载器加载。
This class represents a resource that should be obtained from the classpath. It uses either the thread context class loader, a given class loader, or a given class for loading resources.
This Resource implementation supports resolution as java.io.File if the class path resource resides in the file system but not for classpath resources that reside in a jar and have not been expanded (by the servlet engine or whatever the environment is) to the filesystem. To address this, the various Resource implementations always support resolution as a java.net.URL.
A ClassPathResource is created by Java code by explicitly using the ClassPathResource constructor but is often created implicitly when you call an API method that takes a String argument meant to represent a path. For the latter case, a JavaBeans PropertyEditor recognizes the special prefix, classpath:, on the string path and creates a ClassPathResource in that case.
2.2.3 FileSystemResource
通过io或者nio的方式加载指定绝对路径的资源.
This is a Resource implementation for java.io.File and java.nio.file.Path handles. It supports resolution as a File and as a URL.
2.2.4 ServletContextResource
加载ServletContext相关的资源,如"WEB-INF/xxx"等.
This is a Resource implementation for ServletContext resources that interprets relative paths within the relevant web application’s root directory.
It always supports stream access and URL access but allows java.io.File access only when the web application archive is expanded and the resource is physically on the filesystem. Whether or not it is expanded and on the filesystem or accessed directly from the JAR or somewhere else like a database (which is conceivable) is actually dependent on the Servlet container.
2.2.5 InputStreamResource
加载已经读取为流形式的资源.
An InputStreamResource is a Resource implementation for a given InputStream. It should be used only if no specific Resource implementation is applicable. In particular, prefer ByteArrayResource or any of the file-based Resource implementations where possible.
In contrast to other Resource implementations, this is a descriptor for an already-opened resource. Therefore, it returns true from isOpen(). Do not use it if you need to keep the resource descriptor somewhere or if you need to read a stream multiple times.
2.2.6. ByteArrayResource
加载已经读取为字节数组的资源.
This is a Resource implementation for a given byte array. It creates a ByteArrayInputStream for the given byte array.
It is useful for loading content from any given byte array without having to resort to a single-use InputStreamResource.
三、Spring 资源加载器
资源加载器,顾名思义是用于加载资源的工具,Spring提供了ResourceLoader用于加载各种资源,其参数为一个指定资源URI的字符串,ResourceLoader会根据URI字符串的内容自动解析为对应的资源类型。Spring中的各种ApplicationContext也都继承自ResourceLoader,所以ApplicationContext也可以用于加载资源。
public interface ResourceLoader {
Resource getResource(String location);
}
例如对于一个常见的Spring FrameApplication ClassPathXmlApplicationContext,我们可以通过以下方式读取资源文件。
Resource template = ctx.getResource("some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("classpath:some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
Resource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");
四、获取容器的资源加载器
4.1 ResourceLoaderAware
对于一个Spring应用,我如何获取到一个容器的资源加载器并加载对应的资源呢?Spring提供了ResourceLoaderAware用于获取上下文的资源加载器。用法可以参考ApplicationContextAware的使用(事实上ResourceLoaderAware中的ResourceLoader也是容器的ApplicationContext,所有的ApplicationContext都实现了ResourceLoader接口)。
public interface ResourceLoaderAware {
void setResourceLoader(ResourceLoader resourceLoader);
}
当一个Spring的Bean实现了ResourceLoader接口,那么在容器启动的时候
4.2 @Autowired
在程序中可以通过@Autowired ResourceLoader获取上下文中的资源加载器。
五、资源动态加载
如果需要在程序中动态的决定加载什么资源信息,那么可以尝试通过资源加载器加载资源的属性(这应该是资源加载器比较大的意义之一)。
5.1 各种类型的Application
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/context.xml");
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
我是御狐神,欢迎大家关注我的微信公众号