Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reduce size of Java Spring app by removing not needed dependencies or replace starters

I'm using Spring with Gradle to build a restful API. Since this API works with a PostgreSQL database I'm also using JPA to connect and interact with this database. I used this spring.io guide to develop the basic app and to be able to use JPA I added some more dependiencies. In total the app is now 29MB in size which is a bit too big to my taste. The API features so far nothing more complex than a few basic CRUD actions so I tried to reduce the overall size by:

  • Using the gradle lint plugin to handle my unused dependencies automatic but running gradle buildJava fixGradleLint returns with

    Task :fixGradleLint Passed lint check with 0 violations; no corrections necessary Corrected 0 lint problems

  • in the gradle build file I added a configurations block where I used the exclude group or module on unused dependencies like:

    configurations {
        implementation {
            exclude group: 'org.yaml', module: 'snakeyaml'
            exclude group: 'net.bytebuddy'
            exclude group: 'org.objenesis', module: 'objenesis'
            exclude group: 'org.mockito'
           // exclude module: 'spring-boot-starter-logging'
        }
     }
    

This had the effect that for example Mockito disappeared from the dependency listing offered by gradle dependiencies but their excludes did not reduces the size of the app at all.

How is it possible to reduce effectively the size of the app? Maybe I can get rid of the spring-starters, because they seem to pull in everything including my coffee mug ;). However, I did not find a way how to do so. I will be happy for every disappearing MB.

Below I posted my build.gradle file for clarifications:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
    classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.0.2.RELEASE'
    classpath 'com.netflix.nebula:gradle-lint-plugin:latest.release'
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'war'
apply plugin: 'nebula.lint'

gradleLint.rules =  ['dependency-parentheses','unused-dependency']

bootJar { baseName = 'name' version =  '0.1.0' }
repositories {mavenCentral()}

sourceCompatibility = 1.8
targetCompatibility = 1.8

configurations {
    implementation {
        exclude group: 'org.yaml', module: 'snakeyaml'
        exclude group: 'net.bytebuddy'
        exclude group: 'org.objenesis', module: 'objenesis'
        exclude group: 'org.mockito'
        //exclude module: 'spring-boot-starter-logging'
     }
 }
dependencies {
    compile 'org.springframework:spring-beans:5.0.6.RELEASE'
    compile 'org.springframework.boot:spring-boot-autoconfigure:2.0.2.RELEASE'
    compile 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
    compile 'org.springframework.boot:spring-boot:2.0.2.RELEASE'
    compile 'org.springframework.data:spring-data-commons:2.0.7.RELEASE'
    compile 'org.springframework:spring-web:5.0.6.RELEASE'
    compile 'org.springframework:spring-context:5.0.6.RELEASE'

    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-data-rest'
    implementation 'org.postgresql:postgresql:42.2.0'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
 }
like image 911
theDrifter Avatar asked Oct 29 '25 00:10

theDrifter


1 Answers

If you take a look at the insides of the generated JAR files, you'll see that most of the dependencies are mandatory.

Changing the servlet container

There are a few options though, while Tomcat is lightweight, Jetty and Undertow are a bit more lightweight, with Undertow being the smallest. This will only allow you to win a few 100 kilobytes though, but you can replace them by excluding the following (using Maven):

<exclusion>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>

After that, you could include the following:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

However, on my system this reduced the JAR size about 0,2MB, not really a big difference.

Removing logging

While the Logback framework is included with spring-boot-starter-logging, it isn't mandatory. For science, we can by adding the following exclusion to all Spring boot starters:

 <exclusion>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
</exclusion>

On my system, this reduced the JAR size by about 1MB.

Removing Tomcat's WebSocket support

If you decide to stay with Tomcat, you could remove WebSocket support if you don't need it. You can do this by adding the following exclusion:

 <exclusion>
     <groupId>org.apache.tomcat.embed</groupId>
     <artifactId>tomcat-embed-websocket</artifactId>
</exclusion>

On my system, that reduced the JAR size with about 0,3MB.

Removing support for bean validation (JSR-380)

If you are not going to use bean validation in your API, you can exclude it from your web starter using the following exclusion:

<exclusion>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</exclusion>

This will reduce the JAR size by another 1MB on my system.

YAML support

If you're not going to use application.yml, you could additionally remove the snakeyaml dependency by adding the following exclusion to all your starters:

<exclusion>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
</exclusion>

This will reduce the JAR size by another 0,3MB.

Removing Tomcat's expression language

Another dependency you might not need when developing simple REST API's is Tomcat's expression language. You can exclude this from the web starter using:

<exclusion>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-el</artifactId>
</exclusion>

On my system, this reduced the JAR size about another 0,2MB.


Summary

Summarized, all dependencies that these libraries include are necessary to make them work correctly. If not, they usually provide them as optional dependencies and are very well documented.

It could just so happen that you don't need that functionality. However, this is a risky path, since there's no official guidelines telling you may need this dependency or not, and it could change over time, or you might need that functionality after all.

Typically, the gains are quite small since the large JARS (hibernate-core, tomcat-embed-core, jackson-databind, spring-web, spring-core, ...) are all mandatory.

like image 159
g00glen00b Avatar answered Oct 30 '25 17:10

g00glen00b