Spring MVC Static Resources + WebJars + Caching

In this tutorial we build a simple web application that serves static content from org.webjars and from the resources folder, add caching rules to these static resources and remove the version number from the org.webjars libraries.

WebJars are simple JAR (Java Archive) files which contain client-side static resources. There are several benifits to using WebJars, including support for Java build tools such as Gradle and Maven, easier versioning support, automatically resolve transitive dependencies, etc..

Maven Dependencies

We build the project using maven. Notice, we included several webjars, including: bootstrap, JQuery, font-awesome and webjars-locator. The last one is used in conjuntion with Spring MVC’s WebJarsResourceResolver, we’ll talk about that later. For now, make sure you included all the dependencies in your project and maven will resolve them automatically.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.memorynotfound.spring.mvc</groupId>
    <artifactId>resource-handler</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>SPRING-MVC - ${project.artifactId}</name>
    <url>http://memorynotfound.com</url>
    <packaging>war</packaging>

    <properties>
        <encoding>UTF-8</encoding>
        <spring.version>4.2.6.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- CSS/JavaScript/Bootstrap/Font-Awesome -->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>3.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>2.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>font-awesome</artifactId>
            <version>4.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator</artifactId>
            <version>0.31</version>
        </dependency>

        <!-- servlet api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Configure Spring MVC Static Resources + WebJars + Caching

We override the addResourceHandlers() method and add a ResourceHttpRequestHandler, this will be the root of our static resources. Next, we add the location of where the static resrouces are located. We are serving static resources from a location other than the web application root. This hides the internal mapping structure.

We apply caching for our static resources by creating a CacheControl with a maxAge of 30 days and adding it via the setCacheControl() method.

The last step is actually unnecessary, Spring will automatically add the WebJarsResourceResolver if the org.webjars:webjars-locator library resides on the classpath. This resolver allows you to remove the version number in the path.

package com.memorynotfound.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.resource.WebJarsResourceResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

import java.util.concurrent.TimeUnit;

@EnableWebMvc
@Configuration
@ComponentScan({"com.memorynotfound"})
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("/resources/", "/webjars/")
                .setCacheControl(
                        CacheControl.maxAge(30L, TimeUnit.DAYS).cachePublic())
                .resourceChain(true)
                .addResolver(new WebJarsResourceResolver());

    }

    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

This XML configuration is equivalent to the static resource configuration found above. You can copy this to the configuration file managed by the DispatcherServlet.

<mvc:resources mapping="/static/**" location="/webjars/, /resources/" >
    <mvc:cache-control cache-public="true" max-age="2592000"/>
    <mvc:resource-chain resource-cache="true">
        <mvc:resolvers>
            <bean class="org.springframework.web.servlet.resource.WebJarsResourceResolver"/>
        </mvc:resolvers>
    </mvc:resource-chain>
</mvc:resources>

Include Static Resources in the View

This simple view illustrates multiple ways we can include our static resources. First, we include our webjars the traditional way which includes the version number. Second, we include our webjars with no version number included. Note, that the WebJarsResourceResolver will handle the translation. And last we include the static resources which are located in our web application.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>Spring MVC Resource Handler + WebJars + Caching</title>

    <!-- original -->
    <link href="static/bootstrap/3.3.6/css/bootstrap.min.css" type="text/css" rel="stylesheet"/>
    <link href="static/font-awesome/4.6.2/css/font-awesome.css" type="text/css" rel="stylesheet"/>

    <!-- without version number (ref: WebJarsResourceResolver) -->
    <link href="static/bootstrap/css/bootstrap.min.css" type="text/css" rel="stylesheet"/>
    <link href="static/font-awesome/css/font-awesome.css" type="text/css" rel="stylesheet"/>

    <!-- self hosted libraries -->
    <link href="static/css/style.css" type="text/css" rel="stylesheet"/>

</head>
<body>


    <!-- original -->
    <script src="static/jquery/2.2.3/jquery.min.js"></script>
    <script src="static/bootstrap/3.3.6/js/bootstrap.min.js"></script>
    
    <!-- without version number (ref: WebJarsResourceResolver) -->
    <script src="static/jquery/jquery.min.js"></script>
    <script src="static/bootstrap/js/bootstrap.min.js"></script>
    
    <!-- self hosted libraries -->
    <script src="static/js/script.js"></script>

</body>
</html>

Demo

First request returns 200 OK.

spring mvc static resources webjars caching

Subsequent requests return 304 NOT MODIFIED. Which indicates that these resources are not transmitted over the network buth that they are cached at the client-side. Which results in better performance and less network bandwith.

spring mvc static resources webjars caching

If you look close, we see that the response headers include a Cache-Control entry, indicating that the client may cache the resources.

spring mvc static resources webjars caching

References

Download

You may also like...

  • Анатолий Корсаков

    When i download this zip file “spring mvc static resouces webjars caching java config” in archive other project exists. Please fix that.