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'
 }
If you take a look at the insides of the generated JAR files, you'll see that most of the dependencies are mandatory.
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.
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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With