Application Contexts and Resource Paths
This section covers how to create application contexts with resources, including shortcuts that work with XML, how to use wildcards, and other details.
Constructing Application Contexts
An application context constructor (for a specific application context type) generally takes a string or array of strings as the location paths of the resources, such as XML files that make up the definition of the context.
When such a location path does not have a prefix, the specific Resource
type built from
that path and used to load the bean definitions depends on and is appropriate to the
specific application context. For example, consider the following example, which creates a
ClassPathXmlApplicationContext
:
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
val ctx = ClassPathXmlApplicationContext("conf/appContext.xml")
The bean definitions are loaded from the classpath, because a ClassPathResource
is
used. However, consider the following example, which creates a FileSystemXmlApplicationContext
:
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/appContext.xml");
val ctx = FileSystemXmlApplicationContext("conf/appContext.xml")
Now the bean definition is loaded from a filesystem location (in this case, relative to the current working directory).
Note that the use of the special classpath prefix or a standard URL prefix on the
location path overrides the default type of Resource
created to load the
definition. Consider the following example:
ApplicationContext ctx =
new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
val ctx = FileSystemXmlApplicationContext("classpath:conf/appContext.xml")
Using FileSystemXmlApplicationContext
loads the bean definitions from the classpath. However, it is still a
FileSystemXmlApplicationContext
. If it is subsequently used as a ResourceLoader
, any
unprefixed paths are still treated as filesystem paths.
Constructing ClassPathXmlApplicationContext
Instances — Shortcuts
The ClassPathXmlApplicationContext
exposes a number of constructors to enable
convenient instantiation. The basic idea is that you can supply merely a string array
that contains only the filenames of the XML files themselves (without the leading path
information) and also supplies a Class
. The ClassPathXmlApplicationContext
then derives the path information from the supplied class.
Consider the following directory layout:
com/ foo/ services.xml daos.xml MessengerService.class
The following example shows how a ClassPathXmlApplicationContext
instance composed of the beans defined in
files named services.xml
and daos.xml
(which are on the classpath) can be instantiated:
ApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] {"services.xml", "daos.xml"}, MessengerService.class);
val ctx = ClassPathXmlApplicationContext(arrayOf("services.xml", "daos.xml"), MessengerService::class.java)
See the ClassPathXmlApplicationContext
javadoc for details on the various constructors.
Wildcards in Application Context Constructor Resource Paths
The resource paths in application context constructor values may be simple paths (as
shown earlier), each of which has a one-to-one mapping to a target Resource
or, alternately, may
contain the special "classpath*:" prefix or internal Ant-style regular expressions
(matched by using Spring’s PathMatcher
utility). Both of the latter are effectively
wildcards.
One use for this mechanism is when you need to do component-style application assembly. 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 are automatically picked up.
Note that this wildcarding is specific to the use of resource paths in application context
constructors (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
, as
a resource points to just one resource at a time.
Ant-style Patterns
Path locations can contain Ant-style patterns, as the following example shows:
/WEB-INF/-context.xml com/mycompany//applicationContext.xml file:C:/some/path/-context.xml classpath:com/mycompany//applicationContext.xml
When the path location contains an Ant-style pattern, the resolver follows a more complex procedure to try to resolve the
wildcard. It produces 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 container-specific variant
(such as zip:
in WebLogic, wsjar
in WebSphere, and so on), a java.io.File
is
obtained from it and used to resolve the wildcard by traversing the filesystem. In the
case of a jar URL, the resolver either gets a java.net.JarURLConnection
from it or
manually parses the jar URL and then traverses the contents of the jar file to resolve
the wildcards.
Implications on Portability
If the specified path is already a file URL (either implicitly because the base
ResourceLoader
is a filesystem one or explicitly), wildcarding is guaranteed to
work in a completely portable fashion.
If the specified path is a classpath location, the resolver must obtain the last
non-wildcard path segment URL by making a Classloader.getResource()
call. Since this
is just a node of the path (not the file at the end), it is actually undefined (in the
ClassLoader
javadoc) exactly what sort of a URL is returned in this case. In practice,
it is always a java.io.File
representing the directory (where the classpath resource
resolves to a filesystem location) or a jar URL of some sort (where the classpath resource
resolves to a jar location). Still, there is a portability concern on this operation.
If a jar URL is obtained for the last non-wildcard segment, the resolver must be able to
get a java.net.JarURLConnection
from it or manually parse the jar URL, to be able to
walk the contents of the jar and resolve the wildcard. This does work in most environments
but fails in others, and we strongly recommend that the wildcard resolution of resources
coming from jars be thoroughly tested in your specific environment before you rely on it.
The classpath*:
Prefix
When constructing an XML-based application context, a location string may use the
special classpath*:
prefix, as the following example shows:
ApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath*:conf/appContext.xml");
val ctx = ClassPathXmlApplicationContext("classpath*:conf/appContext.xml")
This special prefix specifies that all classpath resources that match the given name
must be obtained (internally, this essentially happens through a call to
ClassLoader.getResources(…)
) and then merged to form the final application
context definition.
The wildcard classpath relies on the getResources() method of the underlying
classloader. As most application servers nowadays supply their own classloader
implementation, the behavior might differ, especially when dealing with jar files. A
simple test to check if classpath* works is to use the classloader to load a file from
within a jar on the classpath:
getClass().getClassLoader().getResources("<someFileInsideTheJar>") . Try this test with
files that have the same name but are placed inside two different locations. In case an
inappropriate result is returned, check the application server documentation for
settings that might affect the classloader behavior.
|
You can also combine the classpath*:
prefix with a PathMatcher
pattern in the
rest of the location path (for example, classpath*:META-INF/*-beans.xml
). In this
case, the resolution strategy is fairly simple: A ClassLoader.getResources()
call is
used on the last non-wildcard path segment to get all the matching resources in the
class loader hierarchy and then, off each resource, the same PathMatcher
resolution
strategy described earlier is used for the wildcard subpath.
Other Notes Relating to Wildcards
Note that classpath*:
, when combined with Ant-style patterns, only works
reliably with at least one root directory before the pattern starts, unless the actual
target files reside in the file system. This means that a pattern such as
classpath*:*.xml
might not retrieve files from the root of jar files but rather only
from the root of expanded directories.
Spring’s ability to retrieve classpath entries originates from the JDK’s
ClassLoader.getResources()
method, which only returns file system locations for an
empty string (indicating potential roots to search). Spring evaluates
URLClassLoader
runtime configuration and the java.class.path
manifest in jar files
as well, but this is not guaranteed to lead to portable behavior.
The scanning of classpath packages requires the presence of corresponding directory entries in the classpath. When you build JARs with Ant, do not activate the files-only switch of the JAR task. Also, classpath directories may not get exposed based on security policies in some environments — for example, stand-alone applications on JDK 1.7.0_45 and higher (which requires 'Trusted-Library' to be set up in your manifests. See https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources). On JDK 9’s module path (Jigsaw), Spring’s classpath scanning generally works as expected. Putting resources into a dedicated directory is highly recommendable here as well, avoiding the aforementioned portability problems with searching the jar file root level. |
Ant-style patterns with classpath:
resources are not guaranteed to find matching
resources if the root package to search is available in multiple class path locations.
Consider the following example of a resource location:
com/mycompany/package1/service-context.xml
Now consider an Ant-style path that someone might use to try to find that file:
classpath:com/mycompany/**/service-context.xml
Such a resource may be in only one location, but when a path such as the preceding example
is used to try to resolve it, the resolver works off the (first) URL returned by
getResource("com/mycompany");
. If this base package node exists in multiple
classloader locations, the actual end resource may not be there. Therefore, in such a case
you should prefer using classpath*:
with the same Ant-style pattern, which
searches all class path locations that contain the root package.
FileSystemResource
Caveats
A FileSystemResource
that is not attached to a FileSystemApplicationContext
(that
is, when a FileSystemApplicationContext
is not the actual ResourceLoader
) treats
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
filesystem.
For backwards compatibility (historical) reasons however, this changes when the
FileSystemApplicationContext
is the ResourceLoader
. The
FileSystemApplicationContext
forces all attached FileSystemResource
instances
to treat all location paths as relative, whether they start with a leading slash or not.
In practice, this means the following examples are equivalent:
ApplicationContext ctx =
new FileSystemXmlApplicationContext("conf/context.xml");
val ctx = FileSystemXmlApplicationContext("conf/context.xml")
ApplicationContext ctx =
new FileSystemXmlApplicationContext("/conf/context.xml");
val ctx = FileSystemXmlApplicationContext("/conf/context.xml")
The following examples are also equivalent (even though it would make sense for them to be different, as one case is relative and the other absolute):
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("some/resource/path/myTemplate.txt");
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("some/resource/path/myTemplate.txt")
FileSystemXmlApplicationContext ctx = ...;
ctx.getResource("/some/resource/path/myTemplate.txt");
val ctx: FileSystemXmlApplicationContext = ...
ctx.getResource("/some/resource/path/myTemplate.txt")
In practice, if you need true absolute filesystem paths, you should avoid using
absolute paths with FileSystemResource
or FileSystemXmlApplicationContext
and
force the use of a UrlResource
by using the file:
URL prefix. The following examples
show how to do so:
// actual context type doesn't matter, the Resource will always be UrlResource
ctx.getResource("file:///some/resource/path/myTemplate.txt");
// 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");
// force this FileSystemXmlApplicationContext to load its definition via a UrlResource
val ctx = FileSystemXmlApplicationContext("file:///conf/context.xml")