Fitting the artifact size in Spring Boot applications
A few weeks ago, I started working in a new Java project with Spring Boot as the base framework. Despite the advantages of using this framework, we found an inconvenience in the size of the artifact generated in the build process. In this post I will explain the approach that we used to solve this problem.
The build process of a Spring Boot app
Spring Boot is a great framework that eases the bootstrapping and development of our new Spring projects. To achieve this, the framework provides a set of starters that allow us to avoid the inclusion of multiple dependencies and to establish a default configuration to quickly start coding.
Using the Spring initializr website, we can generate a skeleton to start coding our new Spring Boot project. It provides a pom.xml
configuration file with some defaults including the starters that we have selected. The spring-boot-maven-plugin is included in the build
section to generate an artifact and make the application executable. This generated artifact contains the following things:
- The application classes and the associated resources
- The dependencies
- Some Spring classes that give support to deploy the application in a Tomcat Server or to run it standalone
Unfortunately this impacts the size of the generated artifact: around 40–50 MB even in a small web application. In our case, this is a problem because we need to make regular deploys through a slow network.
Using the spring-boot-thin-launcher plugin we found a solution to this problem. It extracts the application dependencies from the artifact. With this approach, we reduced the artifact size speeding up the deploy process.
How does the spring-boot-thin-launcher works?
The idea is simple: the plugin excludes the dependencies at the build process and adds the ThinJarWrapper
as the main class in the application manifest.
When the application starts, the ThinJarWrapper
class automatically retrieves the necessary dependencies to start the application. It searches for them in the pom.xml
and thin.properties
files. The thin.properties
file is optional, and we may use it to include some additional dependencies. Then, these dependencies are loaded in the classpath and, finally, the original main class is executed.
By default, the application dependencies are cached in the local maven repository. Anyway, we can change this behaviour and force ThinJarWrapper
to store them in another place. If so, we must use the -Dthin.root
parameter when we start the application.
Configuring the spring-boot-thin-launcher
Setting up the plugin is easy. All we have to do is add the plugin as dependency of the spring-boot-maven-plugin
:
After doing a mvn package
, the project artifact jar file should be generated in the target directory. Now, the file size should be around a few KB.
Speeding up the deploy process
As I said before, in our particular case, the server that hosts the applications is placed in a slow external network. To minimize the deploy time we are going to transfer the minimum required artifacts on every deploy. The first step is to add another plugin that places all the generated artifacts in the target/thin/root
folder, instead of putting them in the maven cache. This folder will contain the application artifact and its dependencies. To enable this plugin we need to put the following code into the build
section of the pom.xml
file:
In combination with the plugin, we use the following bash script:
The trick here is to use rsync to synchronize the binaries. The first time we deploy the application all binaries will be synchronized (including the application artifact and its dependencies). When we deploy a new release only the artifact and the new dependencies will be synchronized :)
Now we can run the application in the server using the -Dthin.root
option to load the dependencies from the /apps/ourawesomeapp/usr/bin
folder.
Summing up
With the spring-boot-thin-launcher plugin we can reduce the artifact size of our applications extracting the dependencies from them. This can be useful in scenarios like this. Also, it’s possible to deploy multiple related applications (a set of microservices, for example) sharing the dependencies among them.