I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

This article shows how to minify Javascript and CSS assets as a build step and serve the resulting files with Spring MVC.

We will use YUI Compressor as the underlying minification library and YUI Compressor Maven plugin to integrate it into our build process.

2. Maven Plugin Configuration

First, we need to declare that we will use the compressor plugin in our pom.xml file and execute the compress goal. This will compress all .js and .css files under src/main/webapp so that foo.js will be minified as foo-min.js and myCss.css will be minified as myCss-min.css:

<plugin>
   <groupId>net.alchim31.maven</groupId>
    <artifactId>yuicompressor-maven-plugin</artifactId>
    <version>1.5.1</version>
    <executions>
        <execution>
            <goals>
                <goal>compress</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Our src/main/webapp directory contains the following files:

js/
├── foo.js
├── jquery-1.11.1.min.js
resources/
└── myCss.css

After executing mvn clean package, the generated WAR file will contain the following files:

js/
├── foo.js
├── foo-min.js
├── jquery-1.11.1.min.js
├── jquery-1.11.1.min-min.js
resources/
├── myCss.css
└── myCss-min.css

3. Keeping the Filenames the Same

At this stage, when we execute mvn clean package, foo-min.js and myCss-min.css are created by the plugin. Since we have originally used foo.js and myCss.css when referring to the files, our page will still use the original non-minified files, as the minified files have different names than the original.

In order to prevent having both foo.js/foo-min.js and myCss.css/myCss-min.css and have the files minified without changing their names, we need to configure the plugin with nosuffix option as follows:

<plugin>
    <groupId>net.alchim31.maven</groupId>
    <artifactId>yuicompressor-maven-plugin</artifactId>
    <version>1.5.1</version>
    <executions>
        <execution>
            <goals>
                <goal>compress</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <nosuffix>true</nosuffix>
    </configuration>
</plugin>

Now when we execute mvn clean package, we will have the following files in the generated WAR file:

js/
├── foo.js
├── jquery-1.11.1.min.js
resources/
└── myCss.css

4. WAR Plugin Configuration

Keeping the filenames the same has a side effect. It causes the WAR plugin to overwrite the minified foo.js and myCss.css files with the original files, so we don’t have the minified versions of the files in the final output. foo.js file contains the following lines before minification:

function testing() {
    alert("Testing");
}

When we examine the contents of the foo.js file in the generated WAR file, we see that it has the original content instead of the minified content. To solve this problem, we need to specify a webappDirectory for the compressor plugin and reference this from within WAR plugin configuration.

<plugin>
    <groupId>net.alchim31.maven</groupId>
    <artifactId>yuicompressor-maven-plugin</artifactId>
    <version>1.5.1</version>
    <executions>
        <execution>
            <goals>
                <goal>compress</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <nosuffix>true</nosuffix>
        <webappDirectory>${project.build.directory}/min</webappDirectory>
    </configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
    <webResources>
        <resource>
            <directory>${project.build.directory}/min</directory>
        </resource>
    </webResources>
</configuration>
</plugin>

Here we have specified the min directory as the output directory for the minified files and configured the WAR plugin to include this in the final output.

Now we have the minified files in the generated WAR file, with their original filenames foo.js and myCss.css. We can check foo.js to see that it has the following minified content now:

function testing(){alert("Testing")};

5. Excluding Already Minified Files

Third-party Javascript and CSS libraries may have minified versions available for download. If you happen to use one of these in your project, you don’t need to process them again.

Including already minified files produces warning messages when building the project.

For example, jquery-1.11.1.min.js is an already minified Javascript file and it causes warning messages similar to the following during the build:

[WARNING] .../src/main/webapp/js/jquery-1.11.1.min.js [-1:-1]: 
Using 'eval' is not recommended. Moreover, using 'eval' reduces the level of compression!
execScript||function(b){a. ---> eval <--- .call(a,b);})
[WARNING] ...jquery-1.11.1.min.js:line -1:column -1: 
Using 'eval' is not recommended. Moreover, using 'eval' reduces the level of compression!
execScript||function(b){a. ---> eval <--- .call(a,b);})

To exclude already minified files from the process, configure the compressor plugin with an excludes option as follows:

<plugin>
    <groupId>net.alchim31.maven</groupId>
    <artifactId>yuicompressor-maven-plugin</artifactId>
    <version>1.5.1</version>
    <executions>
        <execution>
            <goals>
                <goal>compress</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <nosuffix>true</nosuffix>
        <webappDirectory>${project.build.directory}/min</webappDirectory>
        <excludes>
            <exclude>**/*.min.js</exclude>
        </excludes>
    </configuration>
</plugin>

This will exclude all files under all directories whose filenames end with min.js. Executing mvn clean package doesn’t produce warning messages now and the build doesn’t try to minify already minified files.

6. Conclusion

In this article, we have described a nice way to integrate minification of Javascript and CSS files into your Maven workflow. To serve these static assets with your Spring MVC application, see our Serve Static Resources with Spring article.

You can find the code for this article on GitHub.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS

newest oldest most voted
Notify of
Renmei
Guest
Renmei

Thanks for the article! This is a topic I’ve been hoping you’d address for a while now since I haven’t found a great solution in my own research. I always enjoy working on Ruby/Rails projects since their Asset Pipeline makes this stuff so easy. But the Java world has been sorely lacking in this area for a long time now, imo. The solution you present is a good start, but I think some kind of md5 fingerprinting is required for any realistic, production application. Otherwise you run into caching issues when the JS/CSS change. Any insights into supporting fingerprinting for… Read more »

Eugen Paraschiv
Guest

Yeah, I was thinking the same thing. However, @g00glen00b:disqus makes a good point here – there are more idiomatic ways of doing this as well – worth looking into.
As for MD5 – I entirely agree. I haven’t done with Maven but I can add it to the Content Calendar and maybe someone who actually has experience doing it with Maven will pick it up.
Cheers,
Eugen.

g00glen00b
Guest

I’m more a fan of the frontend-maven-plugin and doing these things with Gulp or Grunt.

Eugen Paraschiv
Guest

Yeah, I think that’s a good way to go as well, and I did consider having that on the Content Calendar but decided not to, simply because the front end is a huge area and not so much the focus of Baeldung.
Hope that makes sense. Cheers,
Eugen.