Spring-boot: Document using the Shadow plugin as an alternative to Boot's fat jars when using Gradle

Created on 5 Nov 2014  路  10Comments  路  Source: spring-projects/spring-boot

I found the following is required in Gradle to get a fat jar working when cannot use the spring-boot-gradle-plugin plugin:

import com.github.jengelman.gradle.plugins.shadow.transformers.*

// Can't use Boot's nested jar packaging due to environment constrained classloader
apply plugin: 'com.github.johnrengelman.shadow'

shadowJar {
  zip64 true

  exclude 'META-INF/*.SF'
  exclude 'META-INF/*.DSA'
  exclude 'META-INF/*.RSA'

  // Required for Spring
  mergeServiceFiles()
  transform(AppendingTransformer) { resource = 'reference.conf'  }
  transform(AppendingTransformer) { resource = 'META-INF/spring.handlers'  }
  transform(AppendingTransformer) { resource = 'META-INF/spring.schemas'  }
  transform(AppendingTransformer) { resource = 'META-INF/spring.tooling'  }
  transform(PropertiesFileTransformer) { paths = ['META-INF/spring.factories' ] }

  // ...
}

The docs should strongly recommend the shadow / shade plugins for environmentally constrained classloaders鹿 because resource transformation is a requirement. Without it, essential beans such as converters, property placeholder configurers, etc. go missing from Spring Boot's autoconfiguration (i.e. spring.factories). The result is very difficult to diagnose.

鹿environmentally constrained classloaders: A system classloader not under your control.

documentation

Most helpful comment

Final working example should be like that, strategy is required if you are using several components that contains spring.factories:

import com.github.jengelman.gradle.plugins.shadow.transformers.*
shadowJar {

    // Required for Spring
    mergeServiceFiles()
    append 'META-INF/spring.handlers'
    append 'META-INF/spring.schemas'
    append 'META-INF/spring.tooling'
    transform(PropertiesFileTransformer) {
        paths = ['META-INF/spring.factories' ]
        mergeStrategy = "append"
    }

}

I hope it will save someone's lot of time :)

All 10 comments

what do you mean not using the boot plugin? Are you creating a fat jar yourself?

Yes, this is a requirement in certain environments such as Hadoop, Spark, Storm, etc. since they don't know how to load nested jars. Please see https://github.com/spring-projects/spring-boot/issues/1668 for more details on this topic.

Here is the section in the docs im referring to
http://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html#executable-jar-alternatives

Just hit an issue in a new Spring Boot version. I needed to amend the above to have:

transform(PropertiesFileTransformer) { paths = ['META-INF/spring.factories' ] }

This seemed to have resolved my issue.

Final working example should be like that, strategy is required if you are using several components that contains spring.factories:

import com.github.jengelman.gradle.plugins.shadow.transformers.*
shadowJar {

    // Required for Spring
    mergeServiceFiles()
    append 'META-INF/spring.handlers'
    append 'META-INF/spring.schemas'
    append 'META-INF/spring.tooling'
    transform(PropertiesFileTransformer) {
        paths = ['META-INF/spring.factories' ]
        mergeStrategy = "append"
    }

}

I hope it will save someone's lot of time :)

We're cleaning out the issue tracker and closing issues that we've not seen much demand to fix. Feel free to comment with additional justifications if you feel that this one should not have been closed.

I think there might be some merit to doing something in this area. Spring Cloud Function (and FaaS in general) would benefit from an easy way to produce a shadowed jar. We really need something that's equivalent to the Maven Shade Plugin's configuration from the starter parent pom. That could take the form of documentation, or it could possibly take the form of some code in Boot's gradle plugin that sets up the various transformers.

With the spring-boot-docs project, there's no easy way for us to test that the Shadow plugin configuration works as expected and I don't want to add an untested snippet. I will add a link to the Shadow plugin alongside the other alternatives though.

Final working example should be like that, strategy is required if you are using several components that contains spring.factories:

import com.github.jengelman.gradle.plugins.shadow.transformers.*
shadowJar {

    // Required for Spring
    mergeServiceFiles()
    append 'META-INF/spring.handlers'
    append 'META-INF/spring.schemas'
    append 'META-INF/spring.tooling'
    transform(PropertiesFileTransformer) {
        paths = ['META-INF/spring.factories' ]
        mergeStrategy = "append"
    }

}

I hope it will save someone's lot of time :)

Helped a alot 馃 Thanks

@unilama thanks for your comment. I was looking for this as Google's Dataflow also doesn't work with Spring's Fat-jar.
For KotlinDSL, here's what I had to do to make it work -

plugins {
    id("com.github.johnrengelman.shadow") version "5.2.0"
}

...

tasks.withType<ShadowJar> {
    isZip64 = true
    // Required for Spring
    mergeServiceFiles()
    append("META-INF/spring.handlers")
    append("META-INF/spring.schemas")
    append("META-INF/spring.tooling")
    transform(PropertiesFileTransformer().apply {
        paths = listOf("META-INF/spring.factories")
        mergeStrategy = "append"
    })
}
Was this page helpful?
0 / 5 - 0 ratings