Spring Basics Tutorial—Resources

This chapter covers how Spring handles resources and how to use resources in Spring. It includes the following topics.

  • Introduction
  • Resource interface
  • Built-in Resource implementation
  • ResourceLoader interface
  • ResourcePatternResolver interface
  • ResourceLoaderAware interface
  • Resources as dependencies
  • Application Context and resource paths

2.1. Introduction

Java's standard java.net.URL class and standard handlers for various URL prefixes are, unfortunately, not sufficient for all access to low-level resources. For example, there is no standardized URL implementation available for accessing resources that need to be obtained from the classpath or relative to the ServletContext. While it is possible to register new handlers for specialized URL prefixes (similar to the existing handlers for prefixes such as http: ), this is often quite complex, and the URL interface still lacks some desirable features, such as checking what is being pointed to Method to determine whether the resource exists.

2.2.Resource interface

The Spring Resource interface, located in the org.springframework.core.io. package, aims to be a more capable interface for abstract access to low-level resources. The following list provides an overview of the Resource interface. See the Resource javadoc for more details.

public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isReadable();

    boolean isOpen();

    boolean isFile();

    URL getURL() throws IOException;

    URI getURI() throws IOException;

    File getFile() throws IOException;

    ReadableByteChannel readableChannel() throws IOException;

    long contentLength() throws IOException;

    long lastModified() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();
}

As the definition of the Resource interface shows, it extends the InputStreamSource interface. The following list shows the definition of the InputStreamSource interface.

public interface InputStreamSource {

    InputStream getInputStream() throws IOException;
}

Some of the most important methods in the Resource interface are.

  • getInputStream(): Locate and open the resource, and return an InputStream for reading the resource. We expect each call to return a new InputStream. It is the caller's responsibility to close the stream.
  • exists(): Returns a boolean value indicating whether the resource actually exists in physical form.
  • isOpen(): Returns a boolean indicating whether the resource represents a handle with an open stream. If true, the InputStream cannot be read multiple times and must be read only once and then closed to avoid resource leaks. For all usual resource implementations, except InputStreamResource, return false.
  • getDescription(): Returns the description of the resource, used for error output when processing the resource. This is usually the full path to the filename or the actual URL of the resource.

Other methods let you obtain the actual URL or File object representing the resource (if the underlying implementation is compatible and supports this functionality).

Some implementations of the Resource interface also implement the extended WritableResource interface to support resources written to it.

Spring itself also makes extensive use of the Resource abstraction, which is a parameter type in many method signatures when a resource is required. Some other methods in the Spring API (such as the constructors of various ApplicationContext implementations) accept a String, which is used in unmodified or simple form to create a Resource suitable for that context implementation, or through a special String path prefix, allowing the caller to specify that a specific Resource implementation must be created and used.

Although the Resource interface is heavily used in Spring, in your own code it serves as a normal utility class for accessing resources, even if your code doesn't know or care about any other part of Spring. Very convenient. While this ties your code to Spring, it's really just tied to this small set of utility classes that serve as a more capable replacement for URL and can be considered equivalent to the one you use for this purpose. any other library.

The Resource abstraction does not replace functionality. It wraps it up as much as possible. For example, UrlResource wraps a URL and uses the wrapped URL to do its work.

2.3. Built-in Resource implementation

Spring includes several built-in Resource implementations.

  • UrlResource
  • ClassPathResource
  • FileSystemResource
  • PathResource
  • ServletContextResource
  • InputStreamResource
  • ByteArrayResource

For a complete list of Resource implementations available in Spring, please consult the "All known implementation classes" section of the Resource avadoc.

2.3.1.UrlResource

UrlResource wraps a java.net.URL and can be used to access any object that can usually be accessed with a URL, such as files, HTTPS targets, FTP targets, etc. All URLs have a standardized String representation, such that the appropriate standardized prefix is ​​used to represent one URL type with another. This includes file: for accessing file system paths, https: for accessing resources via HTTPS protocol, ftp: for accessing resources via FTP, and others.

UrlResource is created by Java code explicitly using the UrlResource constructor, but is often created implicitly when you call an API method that requires a String parameter representing the path. In the latter case, the JavaBeans PropertyEditor ultimately decides which type of Resource to create. If the path string contains a well-known (to the property editor) prefix (such as classpath:), it will create an appropriate private Resource for that prefix. However, if it doesn't recognize the prefix, it assumes the string is a standard URL string and creates an UrlResource.

2.3.2.ClassPathResource

This class represents resources that should be obtained from the classpath. It loads resources using the thread context's class loader, a given class loader, or a given class.

This Resource implementation supports resolution as java.io.File if the class path resource resides in the file system, but not for class paths that reside in a jar and have not been expanded (by the servlet engine or any environment) into the file system. resources are not supported. To solve this problem, various Resource implementations always support parsing to java.net.URL.

Java code creates a ClassPathResource by explicitly using the ClassPathResource constructor, but it is often created implicitly when you call an API method that requires a String parameter representing the path. In the latter case, the JavaBeans PropertyEditor recognizes the special prefix classpath: on the string path and creates a ClassPathResource in this case.

2.3.3.FileSystemResource

This is a Resource implementation for the java.io.File handle. It also supports the java.nio.file.Path handle, applying Spring's standard String-based path conversions, but performs all operations through the java.nio.file.Files API. For pure java.nio.path.Path-based support, use PathResource instead. FileSystemResource supports parsing in the form of `File` and URL.

2.3.4.PathResource

This is a Resource implementation for java.nio.file.Path handling, performing all operations and transformations through the Path API. It supports parsing as File and URL, and also implements the extended WritableResource interface. PathResource is actually a pure FileSystemResource replacement based on java.nio.path.Path with different createRelative behavior.

2.3.5.ServletContextResource

This is a Resource implementation for a ServletContext resource that interprets relative paths relative to the root directory of the web application.

It always supports stream access and URL access, but only allows java.io.File access if the web application archive is extended and the resource is on the file system. Whether it's extended and on the file system, or accessed directly from a JAR or somewhere else like a database (which is conceivable) is really up to the Servlet container.

2.3.6.InputStreamResource

InputStreamResource is a Resource implementation of the given InputStream. It should only be used if there is no specific Resource implementation. In particular, it is better to choose ByteArrayResource or any file-based Resource implementation when possible.

Unlike other Resource implementations, this is a descriptor for an already open resource. Therefore, it returns true from isOpen(). Don't use it if you need to save resource descriptors somewhere, or if you need to read a stream multiple times.

2.3.7.ByteArrayResource

This is a Resource implementation of 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 the single-purpose InputStreamResource.

2.4.ResourceLoader interface

The purpose of the ResourceLoader interface is to be implemented by an object that can return (that is, load) a resource instance. The following list shows the definition of the ResourceLoader interface.

public interface ResourceLoader {

    Resource getResource(String location);

    ClassLoader getClassLoader();
}

All application application contexts implement the ResourceLoader interface. Therefore, all application contexts can be used to obtain Resource instances.

When you call getResource() on a specific application context and specify the location path without a specific prefix, you will get a Resource type appropriate for that specific application context. For example, assume that the following code is run against a ClassPathXmlApplicationContext instance.

Java

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

ntext, this code returns ClassPathResource. If the same method is run against a FileSystemXmlApplicationContext instance, a FileSystemResource is returned. For WebApplicationContext, it will return a ServletContextResource. Likewise, it will return the appropriate object for each context.

Therefore, you can load resources in a way that is appropriate for a specific application context.

On the other hand, you can also force the use of ClassPathResource regardless of the application context type by specifying the special classpath: prefix, as shown in the following example.

Java

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

Force the use of UrlResource by specifying any of the standard java.net.URL prefixes. The following example uses the file and https prefixes.

Java

Resource template = ctx.getResource("file:///some/resource/path/myTemplate.txt");
esource template = ctx.getResource("https://myhost.com/resource/path/myTemplate.txt");

slightly.

Table 10. Resource strings

prefix

Example

illustrate

classpath:

classpath:com/myapp/config.xml

Loaded from classpath.

file:

file:///data/config.xml

Loaded from the file system as a URL. See also FileSystemResource considerations.

https:

https://myserver/logo.png

Load as URL.

(none)

/data/config.xml

Depends on the underlying `ApplicationContext'.

2.5.ResourcePatternResolver interface

The ResourcePatternResolver interface is an extension of the ResourceLoader interface and defines a strategy for parsing location patterns (for example, Ant-style path patterns) into Resource objects.

public interface ResourcePatternResolver extends ResourceLoader {

    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    Resource[] getResources(String locationPattern) throws IOException;
}

As can be seen from the above, this interface also defines a special classpath*: resource prefix for all matching resources from the classpath. Note that in this case, the resource location should be a path without placeholders—for example, classpath*:/config/beans.xml. A JAR file or different directories in the classpath can contain multiple files with the same path and the same name. For further details on wildcard support for classpath*: resource prefixes, see Wildcard Characters in Application Context Constructor Resource Paths and its subsections.

The incoming ResourceLoader (eg, one provided via ResourceLoaderAware semantics) can be checked to see if it also implements this extended interface.

PathMatchingResourcePatternResolver is an independent implementation that can be used outside the ApplicationContext and can also be used by ResourceArrayPropertyEditor to populate Resource[] bean properties. PathMatchingResourcePatternResolver can resolve the specified resource location path into one or more matching Resource objects. The source path can be a simple path that has a one-to-one mapping to the target Resource, or it can contain special classpath*: prefixes and/or internal Ant-style regular expressions (using Spring's org.springframework.util.AntPathMatcher tool match). The latter are all valid wildcard characters.

The default ResourceLoader in any standard ApplicationContext is actually an instance of PathMatchingResourcePatternResolver, which implements the ResourcePatternResolver interface. The same goes for the ApplicationContext instance itself, which also implements the ResourcePatternResolver interface and delegates to the default PathMatchingResourcePatternResolver.

2.6.ResourceLoaderAware interface

The ResourceLoaderAware interface is a special callback interface used to identify components that expect to be provided with ResourceLoader references. The following list shows the definition of the ResourceLoaderAware interface.

public interface ResourceLoaderAware {

    void setResourceLoader(ResourceLoader resourceLoader);
}

When a class implements ResourceLoaderAware and is deployed into an application context (as a Spring-managed bean), it is recognized as ResourceLoaderAware by the application context. The application context then calls setResourceLoader(ResourceLoader), passing itself as a parameter (remember that all Spring application contexts implement the ResourceLoader interface).

Since ApplicationContext is a ResourceLoader, Beans can also implement the ApplicationContextAware interface and directly use the provided application context to load resources. However, in general, it's better if you just need to use the specialized ResourceLoader interface. The code will be coupled only to the resource loading interface (which can be considered a utility interface), rather than to the entire Spring ApplicationContext interface.

In application components, you can also rely on ResourceLoader autowiring as an alternative to implementing the ResourceLoaderAware interface. The traditional constructor and byType autowiring patterns (as described in Injecting Collaborators (Autowiring Collaborators)) can provide a ResourceLoader for the constructor parameter or setter method parameter respectively. For more flexibility, including the ability to autowire fields and multiple parameter methods, consider using annotation-based autowiring capabilities. In this case, whenever a field, constructor, or method is annotated with @Autowired, the ResourceLoader will be autowired into the field, constructor parameter, or method parameter that expects a ResourceLoader type. See Using @Autowired for more information.

To load one or more Resource objects for resource paths that contain wildcards or use the special classpath*: resource prefix, consider autowiring an instance of ResourcePatternResolver into your application component instead of ResourceLoader.

2.7. Resources as dependencies

If the Bean itself is to determine and provide resource paths through some dynamic process, it may make sense for the Bean to use the ResourceLoader or ResourcePatternResolver interface to load resources. For example, consider loading a template where the specific resources required depend on the user's role. If the resource is static, it makes sense not to use the ResourceLoader interface (or ResourcePatternResolver interface) at all, let the bean expose the Resource properties it needs, and expect them to be injected into it.

The trivial thing about injecting these properties is that all application contexts register and use a special JavaBeans PropertyEditor that converts String paths into Resource objects. For example, the following MyBean class has a template property of type Resource.

Java

public class MyBean {

    private Resource template;

    public setTemplate(Resource template) {
        this.template = template;
    }

    // ...
}

In an XML configuration file, the template attribute can be configured with a simple string for the resource, as shown in the following example.

<bean id="myBean" class="example.MyBean">
    <property name="template" value="some/resource/path/myTemplate.txt"/>
</bean>

Note that resource paths are not prefixed. Therefore, since the application context itself will be used as a ResourceLoader, the resources will be loaded through ClassPathResource, FileSystemResource or ServletContextResource, depending on the specific type of application context.

If you need to force the use of a specific Resource type, you can use a prefix. The following two examples show how to force the use of ClassPathResource and UrlResource (the latter is used to access a file in the file system).

<property name="template" value="classpath:some/resource/path/myTemplate.txt">
<property name="template" value="file:///some/resource/path/myTemplate.txt"/>

If the MyBean class is refactored for annotation-driven configuration, the path to myTemplate.txt can be stored under a key named template.path — for example, in a properties file provided to the Spring Environment (see Environment abstraction) . The template path can then be referenced with property placeholders via the @Value annotation (see Using @Value). Spring will retrieve the value of the template path in the form of a string. A special PropertyEditor will convert the string into a Resource object and inject it into the MyBean constructor. The example below demonstrates how to achieve this.

Java

@Component
public class MyBean {

    private final Resource template;

    public MyBean(@Value("${template.path}") Resource template) {
        this.template = template;
    }

    // ...
}

If we want to support multiple templates found under the same path in multiple locations on the classpath—for example, in multiple jars on the classpath—we can use the special classpath*: prefix and wildcards to define the templates.path key as classpath*:/config/templates/*.txt. If we redefine the MyBean class as follows, Spring will convert the template path pattern into an array of Resource objects that can be injected into the MyBean constructor.

Java

@Component
public class MyBean {

    private final Resource[] templates;

    public MyBean(@Value("${templates.path}") Resource[] templates) {
        this.templates = templates;
    }

    // ...
}

2.8. Application Context and resource paths

This section describes how to create an application context with resources, including shortcuts for use with XML, how to use wildcards, and other details.

2.8.1. Building Application Context

The application context constructor (for a specific application context type) usually requires a string or array of strings as the location path to a resource, such as the XML file that makes up the context definition.

When such a location path is not prefixed, the specific Resource type that is built from the path and used to load the bean definition depends on and is appropriate for the specific application environment. For example, consider the following example, which creates a ClassPathXmlApplicationContext.

Java

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

Because ClassPathResource is used, the bean definition is loaded from the classpath. However, look at the following example, which creates a FileSystemXmlApplicationContext.

Java

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

Now, the bean's definition is loaded from a location on the file system (in this case, relative to the current working directory).

Note that using a special classpath prefix or a standard URL prefix on the location path will override the default type of the Resource created to load the bean definition. Consider the following example.

Java

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

Using FileSystemXmlApplicationContext loads the bean definition from the classpath. However, it is still a FileSystemXmlApplicationContext. If it is subsequently used as a ResourceLoader, any unprefixed path is still considered a file system path.

Build ClassPathXmlApplicationContext instance - shortcut

ClassPathXmlApplicationContext exposes some constructors to facilitate instantiation. The basic idea is that you can just provide an array of strings containing just the filename of the XML file itself (without the leading path information), and also provide a Class. ClassPathXmlApplicationContext then exports path information from the provided class.

Consider the following directory layout.

com/
  example/
    services.xml
    repositories.xml
    MessengerService.class

The following example shows how to instantiate a ClassPathXmlApplicationContext instance consisting of beans defined in files named services.xml and repositories.xml (on the classpath).

Java

ApplicationContext ctx = new ClassPathXmlApplicationContext(
    new String[] {"services.xml", "repositories.xml"}, MessengerService.class);

See the ClassPathXmlApplicationContext javadoc for details on the various constructors.

2.8.2. Wildcard characters in the resource path of the Application Context constructor

The resource paths in the application context constructor value can be simple paths (as shown previously), each with a one-to-one mapping to the target Resource, or, can contain special classpath*: prefixes or internal Ant-style pattern (matched using Spring's PathMatcher tool). The latter are all valid wildcard characters.

One use for this mechanism is when you need to assemble your application into components. All components can publish context definition fragments to a well-known location path, and when the final application context is created using the same path, prefixed with classpath*:, all component fragments will be automatically received.

Note that this wildcard is specific to using resource paths in the application context constructor (or when you use the PathMatcher utility class hierarchy directly), and is resolved at construction time. It has nothing to do with the Resource type itself. You cannot use the classpath*: prefix to construct an actual Resource; a Resources object only points to one resource at a time.

Ant style Pattern

Path locations can contain Ant-style patterns, as shown in the example below.

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

When a path location contains an Ant-style pattern, the parser follows a more complex procedure to try to resolve wildcards. It generates a Resource for the path up to the last non-wildcard segment and obtains a URL from it. If this URL is not a jar: URL or a container-specific variant (such as zip in WebLogic: wsjar in WebSphere, etc.), the java.io.File is obtained from it and the wildcard is resolved by traversing the file system. In the case of a jar URL, the parser either obtains a java.net.JarURLConnection from it or manually parses the jar URL and then iterates over the contents of the jar file to resolve wildcards.

Impact on portability

If the specified path is already a file URL (implicitly, because the base ResourceLoader is a file system, or explicitly), wildcards are guaranteed to work in a fully portable manner.

If the specified path is a classpath location, the parser must obtain the last non-wildcard path segment URL by calling Classloader.getResource(). Since this is just a node of the path (and not the final file), it's actually not defined (in the ClassLoader's javadoc) exactly what URL will be returned in this case. In practice it is always a java.io.File representing a directory (in the case of a classpath resource resolving to a filesystem location) or some kind of jar URL (in the case of a classpath resource resolving to a jar location). However, there is still a portability issue with this operation.

If a jar URL is obtained for the last non-wildcard segment, the parser must be able to obtain a java.net.JarURLConnection from it or manually parse the jar URL in order to be able to browse the contents of the jar and resolve the wildcard. This does work in most environments, but fails in others, and we strongly recommend thoroughly testing wildcard resolution of resources from jars in your particular environment before you rely on it.

classpath*:prefix

When building XML-based application contexts, location strings can use the special classpath*: prefix, as shown in the following example.

Java

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

This special prefix specifies that all classpath resources matching the given name must be obtained (internally, this basically happens by calling ClassLoader.getResources(…​)) and then merged to form the final application context definition.

The wildcard classpath relies on the getResources() method of the underlying ClassLoader. Since most application servers now provide their own ClassLoader implementation, behavior may vary, especially when dealing with jar files. A simple test to check if the classpath* is valid is to use ClassLoader to load a file from a jar on the classpath: getClass().getClassLoader().getResources("<someFileInsideTheJar>"). Try this test with a file with the same name but in two different locations - for example, a file with the same name and the same path but in different jars on the classpath. If the results returned are inappropriate, check the application server documentation for settings that may affect ClassLoader behavior.

You can also combine the classpath*: prefix with a PathMatcher pattern that locates the rest of the path (for example, classpath*:META-INF/*-beans.xml). In this case, the parsing strategy is fairly simple. Use the ClassLoader.getResources() call on the last non-wildcard path segment to obtain all matching resources in the class loader hierarchy, and then on each resource, use the same PathMatcher resolution strategy described previously for wildcard subpaths.

Additional notes about wildcards

Note that classpath*:, when combined with Ant-style patterns, will only work reliably if there is at least one root directory before the pattern starts, unless the actual target file is in the file system. This means that a pattern like classpath*:*.xml may not retrieve files from the root of the jar file, but only from the root of the extension directory.

Spring's ability to retrieve classpath entries stems from the JDK's ClassLoader.getResources() method, which simply returns the file system location of an empty string (indicating the potential root to search for). Spring also evaluates the URLClassLoader's runtime configuration and java.class.path manifest, but this is not guaranteed to result in portable behavior.

Scanning classpath packages requires the existence of corresponding directory entries in the classpath. When you build a JAR with Ant, do not activate the files-only switch for the JAR task. Additionally, in some environments, the classpath directory may not be exposed according to security policies - for example, standalone applications with JDK 1.7.0_45 and above (this requires 'Trusted-Library' to be set in the manifest. See https: //stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources).

On the JDK 9 module path (Jigsaw), Spring's classpath scanning generally works as expected. It is also strongly recommended here to put the resources in a dedicated directory to avoid the aforementioned portability issue of searching the root level of the jar file.

Ant-style patterns containing classpath: resources are not guaranteed to find matching resources if the root package being searched is available at multiple classpath locations. Consider the following resource location example.

com/mycompany/package1/service-context.xml

Now consider an Ant-style path that someone might use to try to find this file.

classpath:com/mycompany/**/service-context.xml

Such a resource may only exist in one location on the classpath, but when a path like the previous example is used to try to parse it, the parser bases its response on the (first) URL returned by getResource("com/mycompany"); Come to work. If this base package node exists in multiple ClassLoader locations, the required resources may not be present in the first location found. Therefore, in this case, you should prefer to use classpath*: The same pattern as Ant style, which can search all classpath locations containing the com.mycompany base package: classpath*:com/mycompany/**/service- context.xml.

2.8.3.FileSystemResource Notes

A FileSystemResource that is not attached to a FileSystemApplicationContext (that is, when the FileSystemApplicationContext is not an actual ResourceLoader) handles absolute and relative paths as you would expect. Relative paths are relative to the current working directory, while absolute paths are relative to the root of the file system.

However, for backward compatibility (historical) reasons, the situation changes when the FileSystemApplicationContext is a ResourceLoader. FileSystemApplicationContext forces all attached FileSystemResource instances to treat all location paths as relative, regardless of whether they start with a leading slash. In practice, this means that the following examples are equivalent.

Java

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

Java

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

The following examples are also equivalent (although their difference makes sense since one case is relative and the other is absolute).

Java

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

Java

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

In practice, if a true absolute file system path is required, avoid using absolute paths to FileSystemResource or FileSystemXmlApplicationContext and instead force the use of UrlResource by using the file: URL prefix. The example below shows how to do this.

Java

// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");

Java

// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
ApplicationContext ctx =
    new FileSystemXmlApplicationContext("file:///conf/context.xml");

Guess you like

Origin blog.csdn.net/leesinbad/article/details/132946671