1. Overview
This article explores how to serve static resources with Spring – from the basic XML-based resource mapping introduced since 3.0.x. to the more flexible and efficient alternatives in Spring 4.1.
2. XML Configuration
If you need to go the old fashion way with XML-based configuration, you can make good use of the mvc:resources element to point to the location of resources with a specific public URL pattern.
For example – the following line will serve all requests for resources coming in with a public URL pattern like “/resources/**” by searching in the “/resources/” directory under the root folder in our application.
<mvc:resources mapping="/resources/**" location="/resources/" />
Now, we can access a CSS file as in the following html page:
Example 2.1.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <link href="<c:url value="/resources/myCss.css" />" rel="stylesheet"> <title>Home</title> </head> <body> <h1>Hello world!</h1> </body> </html>
3. The ResourceHttpRequestHandler
Spring 3.1. introduced the ResourceHanlerRegistry to configure ResourceHttpRequestHandlers for serving static resources from the classpath, the WAR, or the file system. We can configure the ResourceHandlerRegistry programmatically inside our web context configuration class.
3.1. Serving a Resource Stored in the WAR
To illustrate this, we’ll use the same URL as before to point to myCss.css, but now the actual file will be located in the WAR’s webapp/resources folder, which is where static resources should be placed when deploying Spring 3.1+ applications:
Example 3.1.1.
@Configuration @EnableWebMvc public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/resources/**") .addResourceLocations("(/resources/"); } }
Lest analyze the example bit. We first configure the external facing URI path by adding defining a resource handler. We then map that external facing URI path internally, to the physical path where the resources are actually located.
We can of course define multiple resource handlers using this simple yet flexible API.
Now – the following line in an html page would get us the myCss.css resource inside the webapp/resources directory:
<link href="<c:url value="/resources/myCss.css" />" rel="stylesheet">
3.2. Serving a Resource Stored in the File System
Lets say we want to serve a resource stored in the /opt/files/ directory whenever a request comes in for the public URL matching the pattern: /files/**. We simply configure the URL pattern and map it to that particular location on disk:
Example 3.2.1.
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/files/**") .addResourceLocations("file:/opt/files/"); }
*(For Windows users: The argument passed to addResourceLocations for this example would be “file:///C:/opt/files/“).
Once we configure the resource location, we can use the mapped URL pattern in our home.html to load an image stored in the file system as follows:
Example 3.2.2.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <link href="<c:url value="/resources/myCss.css" />" rel="stylesheet"> <title>Home</title> </head> <body> <h1>Hello world!</h1> <img alt="image" src="<c:url value="files/myImage.png" />"> </body> </html>
3.3. Configuring Multiple Locations for a Resource
What if we want to look for a resource in more than one location?
We can include multiple locations with the addResourceLocations method. The list of locations will be searched in order until the resource is found. Lets take a look at Example 3.3.1.
Example 3.3.1
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/resources/**") .addResourceLocations("/resources/","classpath:/other-resources/"); }
The following curl request will display the Hello.html page stored in either the application’s webappp/resources or the other-resources folder in the classpath.
curl -i http://localhost:8080/handling-spring-static-resources/resources/Hello.html
4. The New ResourceResolvers
Spring 4.1. provides – with the new ResourcesResolvers – different types of resource resolvers that can be used to optimize browser performance when loading static resources. These resolvers can be chained and cached in the browser to optimize request handling.
4.1. The PathResourceResolver
This is the simplest resolver and its purpose is to find a resource given a public URL pattern. In fact, if no ResourceResolver is added to the ResourceChainRegistration, this is the default resolver.
Lets see an example:
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/resources/**") .addResourceLocations("/resources/","/other-resources/") .setCachePeriod(3600) .resourceChain(true) .addResolver(new PathResourceResolver()); }
Things to notice:
- We are registering the PathResourceResolver in the resource chain as the sole ResourceResolver in it. See section 4.4. to check how to chain more than one ResourceResolver.
- The resources served will be cached in the browser for 3600 seconds.
- The chain is finally configured with the method resourceChain(true).
Now – the html code that, in conjunction with the PathResourceResolver, locates the foo.js script in either the webapp/resources of the webapp/other-resources folder:
<script type="text/javascript" src="<c:url value="/resources/foo.js" />">
4.2. The GzipResourceResolver
This is a convenient resolver to use when we need to optimize bandwidth by serving the compressed version of a static resource. This resolver will delegate to other resolvers the search for the requested resource, if none is found it will attempt to find a gzip version.
To configure a GzipResourceResolver, we just need to configure it in the ResourceChain just as we configured the PathResourceResolver, as in the following line of code:
registry .addResourceHandler("/other-files/**") .addResourceLocations("file:/Users/Me/") .setCachePeriod(3600) .resourceChain(true) .addResolver(new GzipResourceResolver());
So, the following curl request will get the zipped version of the Home.html file located in the file system in the Users/Me/ directory:
curl -H "Accept-Encoding:gzip,deflate" http://localhost:8080/handling-spring-static-resources/other-files/Hello.html
Notice how we are setting the header’s “Accept-Encoding” value to gzip – this is important because this particular resolver will only kick in if the gzip content is valid for the response.
Finally, note that, same as before, the compressed version will remain available for the period of time it is cached in the browser – which in this case is 3600 seconds.
4.3. Chaining ResourceResolvers
To optimize resource lookup, ResourceResolvers can delegate the handling of resources to other resolvers. The only resolver that can’t delegate to the chain is the PathResourceResolver which should be added at the end of the chain.
In fact, if the resourceChain is not set to true, then by default only a PathResourceResolver will be used to serve resources. In Example 4.3.1. we’re chaining the PathResourceResolver to resolve the resource if the GzipResourceResolver is unsuccessful.
Example 4.3.1.
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/js/**") .addResourceLocations("/js/") .setCachePeriod(3600) .resourceChain(true) .addResolver(new GzipResourceResolver()) .addResolver(new PathResourceResolver()); }
Now that we have added the /js/** pattern to the ResourceHandler, lets include the foo.js resource located in the webapp/js/ directory in our home.html page as in Example 4.3.2.
Example 4.3.2.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <link href="<c:url value="/resources/bootstrap.css" />" rel="stylesheet" /> <script type="text/javascript" src="<c:url value="/js/foo.js" />"></script> <title>Home</title> </head> <body> <h1>This is Home!</h1> <img alt="bunny hop image" src="<c:url value="files/myImage.png" />" /> <input type = "button" value="Click to Test Js File" onclick = "testing();" /> </body> </html>
5. Additional Security Configuration
If using Spring Security – it’s important to allow access to the static resources. We’ll need to add the corresponding permissions for accessing the resource URL’s:
<intercept-url pattern="/files/**" access="permitAll" /> <intercept-url pattern="/other-files/**/" access="permitAll" /> <intercept-url pattern="/resources/**" access="permitAll" /> <intercept-url pattern="/js/**" access="permitAll" />
6. Conclusion
In this article we have illustrated various way in which static resources can be served in a Spring application.
The XML-based resource configuration is a “legacy” option which can be used if you cannot go down the java configuration route yet.
Spring 3.1. came out with a basic programmatic alternative through its ResourceHandlerRegistry object.
And finally – the new out of the box ResourceResolvers and ResourceChainRegistration object shipped with Spring 4.1. offer resource loading optimization features like caching and resource handler chaining to improve efficiency in serving static resources.
I usually post about Spring stuff on Google+ - you can follow me there: