In Java, resources from different sources are abstracted into URLs. By registering different handler (URLStreamHandler), you can process the read logic of resources from different sources, generally, handler types are identified by different prefixes (Protocols), such as "file:", "http:", and "jar, however, URLs do not have handler defined by default relative to resources such as classpath or ServletContext. Although you can register your own URLStreamHandler to parse specific URL prefixes (Protocols), such as "classpath :", however, you need to understand the URL implementation mechanism, and the URL does not provide some basic methods, such as checking whether the current resource exists or whether the current resource is readable. Therefore, Spring implements its own abstract structure for the resources used internally: The Resource interface encapsulates the underlying resources:
Public interface InputStreamSource {
InputStream getInputStream () throws IOException;
}
Public interface Resource extends InputStreamSource {
Boolean exists ();
Boolean isReadable ();
Boolean isOpen ();
URL getURL () throws IOException;
URI getURI () throws IOException;
File getFile () throws IOException;
Long lastModified () throws IOException;
Resource createRelative (String relativePath) throws IOException;
String getFilename ();
String getDescription ();
}
InputStreamSource encapsulates any class that can return InputStream, such as File, resources under classpath, and Byte Array. It has only one method definition: getInputStream (), which returns a new InputStream object.
The Resource interface abstracts all the underlying resources used in Spring: File, URL, classpath, and so on. First, it defines three methods to determine the current resource status: exists, isReadable, and isOpen ). In C language, when we get a file handle, we need to call the open Method to open the file to actually read the file, but there is no definition of the open method shown in Java, generally, when we create an InputStream or Reader, the resource (File) is already open. Therefore, the isOpen method here does not determine whether the current resource is open and operable, it indicates whether the abstract underlying Resource of the Resource interface can call the getInputStream () method multiple times. If this method returns true, the getInputStream () method cannot be called multiple times. In Spring 2.5.6, only the isOpen () method of the InputStreamResource class returns true, and the others return false.
In addition, the Resource interface provides conversion from different types of resources to URLs, Uris, and files, as well as getFilename () for obtaining lastModified attributes and File names (without path information. For ease of operation, Resource also provides a method to create relative resources based on the current Resource: createRelative (); in error handling, You need to print the Resource files with errors in detail, therefore, Resource also provides the getDescription () method for printing information in error handling.
In Spring 2.5.6, the inheritance relationships between all interface classes that implement Resource are as follows:
That is, Resource implementation is available for Resource files of different sources: file (FileSystemResource), classpath Resource (ClassPathResource), URL Resource (UrlResource), and InputStream Resource (InputStreamResource) and Byte array (ByteArrayResource.
AbstractResource class
AbstractResource is the basic implementation of Resource. All Resource implementation classes inherit the class. Generally, all resources that inherit the class only need to implement the following methods:
Public File getFile () throws IOException
Public URL getURL () throws IOException
Public String getDescription ()
Public InputStream getInputStream () throws IOException
In the default implementation of this class, toString, equals, and hashCode are all proxies to the Description attribute; isReadable always returns true, while isOpen always returns false; exists method implementation, call the exists method of the File object returned by getFile first. If the method fails, check whether the InputStream can be obtained. If yes, return true. Otherwise, return false. The getURL, getFile, and createRelative Methods throw the FileNotFoundException, the getURI method is used as the proxy to the getURL method.
ByteArrayResource class
ByteArrayResource is a simple Resource implementation that encapsulates binary arrays. Each call to getInputStream creates a ByteArrayInputStream using this binary array as the source. Its exists method always returns true, and the equals and hashCode methods are rewritten to determine the content of the binary array. Its description attribute can be customized or the default value can be used: resource loaded from byte array
Public final byte [] getByteArray (){
Return this. byteArray;
}
Public boolean exists (){
Return true;
}
Public InputStream getInputStream () throws IOException {
Return new ByteArrayInputStream (this. byteArray );
}
FileSystemResource class
FileSystemResource is the encapsulation of File. When constructing FileSystemResource, You can input a File object or path string (the path here can be a relative path, and the relative path is relative to System. getProperty ("user. the path where the dir ") value is located, which can be an absolute path or a path value starting with" file: ". A File object is created internally, and calculate the path value. Here, the path is calculated. and... "Impact Value (normalization ).
In the getInputStream method, use this File object to create FileInputStream, while the path value is used as the implementation of the description attribute, equals, hashCode, and other methods. All other methods (exists, isReadable, getURL, etc) the createRelative method uses path to calculate the relative path. The algorithm is: Find the last path separator (/) and add the relative path to the separator, the Input relative path can start with a path separator (/) or not start with a separator (/). They have the same effect on the relative path. and... "will be processed when the FileSystemResource class is created. Finally, when a File object in a directory is used to construct FileSystemResource, The createRelative method is called. The parent directory of the relative path is the same as the parent directory of the current FileSystemResource, for example, if you use the "/home/Levin/dir1" directory to create a FileSystemResource object, this Resource object calls createRelative and passes in "file ", the result is/home/Levin/file. If you want to get "/home/Levin/dir1/file", when creating FileSystemResource, the "/home/Levin/dir1/" string should be input.
Public boolean isReadable (){
Return (this. file. canRead ()&&! This. file. isDirectory ());
}
Public InputStream getInputStream () throws IOException {
Return new FileInputStream (this. file );
}
Public Resource createRelative (String relativePath ){
String pathToUse = StringUtils. applyRelativePath (this. path, relativePath );
Return new FileSystemResource (pathToUse );
}
Public String getDescription (){
Return "file [" + this. file. getAbsolutePath () + "]";
}
UrlResource class
UrlResource is the encapsulation of URL and URI. When building a UrlResource, You can input a URL, URI, and Path string (with a protocol string, such as "file :"). In UrlResource, A cleanedUrl is also created, which is normalized (the values after "." and "..." are calculated). This URL will be used for the implementation of equals and hashCode methods.
In the implementation of the getInputStream method, it uses the URL. openConnection () method to obtain the URLConnection, and then calls the getInputStream method of the URLConnection. The getFile () method only supports the resources of the file system, that is, the Protocol Part of the URL string is "file :". UrlResource also supports internal files such as jar, zip, vfszip, and wsjar. For example, the strings of these files are expressed as: jar: file:/<jarpath>/jarfile. jar! /<Filepath>/filename, such as jar: file:/E:/Program % 20 Files/eclipse-juno/plugins/org. junit_4.10.0.v4_10_0_v20120426-0900/junit. jar! /Org/junit/Test. class, however, these internal files do not have the attribute of lastModified. Therefore, for these internal files, UrlResource regards the lastModified attribute of these internal files, such as jar and zip files as the lastModified attribute of these internal files. The createRelative method directly uses the constructor provided by the URL to ignore the path separator "/" in the imported relativePath.
Public InputStream getInputStream () throws IOException {
URLConnection con = this. url. openConnection ();
Con. setUseCaches (false );
Return con. getInputStream ();
}
Protected File getFileForLastModifiedCheck () throws IOException {
If (ResourceUtils. isJarURL (this. url )){
URL actualUrl = ResourceUtils. extractJarFileURL (this. url );
Return ResourceUtils. getFile (actualUrl );
}
Else {
Return getFile ();
}
}
Public Resource createRelative (String relativePath) throws MalformedURLException {
If (relativePath. startsWith ("/")){
RelativePath = relativePath. substring (1 );
}
Return new UrlResource (new URL (this. url, relativePath ));
}
ClassPathResource class
Encapsulate resources in classpath, or encapsulate the ClassLoader. getResource () method or Class. getResource () method. It supports reading resource files in the current classpath. You can input the full path name of the file relative to classpath and ClassLoader to construct ClassPathResource, or ignore the ClassLoader using the default ClassLoader (that is, Thread Context ClassLoader). At this time, the getInputStream () method always uses ClassLoader. the getResourceAsStream () method, because the root directory relative to classpath is obtained by default when ClassLoader is used, the constructor ignores the "/" character at the beginning. ClassPathResource can also be constructed using the file path and Class as parameters. If the file path starts with "/", the file is the absolute path relative to classpath, otherwise, it is the relative path of the relative Class instance, and the Class is used when the getInputStream () method is implemented. getResourceAsStream () method.
The getFile () method only supports Resources in the file system. For lastModified attributes, if it is a resource in a jar or zip file, the lastModified attribute of the jar or zip file is used; equals checks the path, classloader, and clazz fields at the same time, while hashCode only uses path.
Public InputStream getInputStream () throws IOException {
InputStream is = null;
If (this. clazz! = Null ){
Is = this. clazz. getResourceAsStream (this. path );
}
Else {
Is = this. classLoader. getResourceAsStream (this. path );
}
If (is = null ){
Throw new FileNotFoundException (
GetDescription () + "cannot be opened because it does not exist ");
}
Return is;
}
Public URL getURL () throws IOException {
URL url = null;
If (this. clazz! = Null ){
Url = this. clazz. getResource (this. path );
}
Else {
Url = this. classLoader. getResource (this. path );
}
If (url = null ){
Throw new FileNotFoundException (
GetDescription () + "cannot be resolved to URL because it does not exist ");
}
Return url;
}
Public File getFile () throws IOException {
Return ResourceUtils. getFile (getURL (), getDescription ());
}
Protected File getFileForLastModifiedCheck () throws IOException {
URL url = getURL ();
If (ResourceUtils. isJarURL (url )){
URL actualUrl = ResourceUtils. extractJarFileURL (url );
Return ResourceUtils. getFile (actualUrl );
}
Else {
Return ResourceUtils. getFile (url, getDescription ());
}
}
Public Resource createRelative (String relativePath ){
String pathToUse = StringUtils. applyRelativePath (this. path, relativePath );
Return new ClassPathResource (pathToUse, this. classLoader, this. clazz );
}
InputStreamResource class
InputStreamResource is an encapsulation of InputStream. It receives InputStream as the constructor parameter. Its isOpen always returns true and can only be read once (that is, the getInputStream method can only be called once ), the exists and isReadable methods always return true. Because it cannot be read multiple times, this class is used only when there is no other available Resource class. It does not seem to be used in Spring. It only implements the getInputStream method:
Public InputStream getInputStream () throws IOException, IllegalStateException {
If (this. read ){
Throw new IllegalStateException ("InputStream has already been read-" +
"Do not use InputStreamResource if a stream needs to be read multiple times ");
}
This. read = true;
Return this. inputStream;
}
DescriptiveResource class
DescriptiveResource is the encapsulation of non-physical resource Description. It implements the getDescription () method. The Description attribute in Resource is mainly used to print error location information more accurately during error processing, descriptiveResource provides custom descriptions for methods that need to provide the Description attribute in the Resource interface as error printing information. For example, in BeanDefinitionReader, you can use DescriptiveResource to define your own Description when you only use InputSource as the source to add BeanDefinition, so that you can easily know the problem source in the error information.
BeanDefinitionResource class
In Spring, Resource can be used to extract non-physical resources. BeanDefinitionResource is the encapsulation of BeanDefinition. BeanDefinitionResource is similar to DescriptiveResource. It only implements the getDescription () method to display the error source information when parsing a BeanDefinition error:
Public String getDescription (){
Return "BeanDefinition defined in" + this. beanDefinition. getResourceDescription ();
}
ContextResource Interface
The ContextResource interface is also defined in Spring. It inherits from the Resource interface and only contains one method:
Public interface ContextResource extends Resource {
String getPathWithinContext ();
}
The getPathWithContext () method corresponds to the Context path, such as ServletContext, PortletContext, classpath, and FileSystem. In Spring core, it has two implementation classes: FileSystemContextResource and ClassPathContextResource, they are the internal classes in FileSystemResourceLoader and DefaultResourceLoader respectively. Their implementation of the getPathWithContext () method simply returns the path value.
In addition, in the Spring Web module, there is a ServletContextResource implementation class, which uses ServletContext and path as the parameter construction. In the getInputStream, getURL, getURI, getFile, and other methods, proxy is implemented to ServletContext, the getPathWithContext method still returns the path string:
Public boolean exists (){
Try {
URL url = this. servletContext. getResource (this. path );
Return (url! = Null );
}
Catch (MalformedURLException ex ){
Return false;
}
}
Public InputStream getInputStream () throws IOException {
InputStream is = this. servletContext. getResourceAsStream (this. path );
If (is = null ){
Throw new FileNotFoundException ("cocould not open" + getDescription ());
}
Return is;
}
Public URL getURL () throws IOException {
URL url = this. servletContext. getResource (this. path );
If (url = null ){
Throw new FileNotFoundException (
GetDescription () + "cannot be resolved to URL because it does not exist ");
}
Return url;
}
Public File getFile () throws IOException {
String realPath = WebUtils. getRealPath (this. servletContext, this. path );
Return new File (realPath );
}
Public Resource createRelative (String relativePath ){
String pathToUse = StringUtils. applyRelativePath (this. path, relativePath );
Return new ServletContextResource (this. servletContext, pathToUse );
}