How to avoid repeating dependency versions in a multi-module Gradle project? - spring

There's a sample Spring Boot project here that contains two modules.
The build.gradle for one of the modules looks like this:
buildscript {
ext { springBootVersion = '2.1.4.RELEASE' }
repositories { mavenCentral() }
dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") }
}
plugins {
id "io.spring.dependency-management" version "1.0.5.RELEASE"
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
bootJar {
baseName = 'gs-multi-module-application'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
repositories { mavenCentral() }
dependencies {
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-starter-web')
compile project(':library')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
The other module's build.gradle looks like this:
buildscript {
repositories { mavenCentral() }
}
plugins { id "io.spring.dependency-management" version "1.0.5.RELEASE" }
ext { springBootVersion = '2.1.4.RELEASE' }
apply plugin: 'java'
apply plugin: 'eclipse'
jar {
baseName = 'gs-multi-module-library'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
repositories { mavenCentral() }
dependencies {
compile('org.springframework.boot:spring-boot-starter')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}") }
}
The springBootVersion = '2.1.4.RELEASE' is declared in both modules. For a 2 module project that might not be a problem, but if my project had 10 modules and I wanted to make sure that all modules always depend on the same version of Spring Boot, it would be inconvenient and error-prone to repeat this version in every module.
Similarly, I might want to add a dependency on commons-io to both of these modules, and ensure they both always depend on the same version of commons-io.
How can I avoid repeating the version numbers in each and every build.gradle file?

See this Gradle documentation : a good practice in Gradle is to configure subprojects which share common traits in a single place, for example in the root project's build script (or using custom plugins)
EDIT the solution proposed here is no longer considered as good practice from Gradle team (the link above does not even mention subproject bloc in latests Gradle version doc); thank you #buggy for the warning .
In your example taken from Spring boot documentation, this pattern could be applied to centralize Spring boot and other common dependencies versions in a single place, but you could go further and also configure other common traits (Java plugin configuration, repositoties, etc..)
Here is how I would re-write the Spring example to make it cleaner and DRY:
Root project
/**
* Add Springboot plugin into build script classpath (without applying it)
* This is this only place where you need to define the Springboot version.
*
* See https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/html/#managing-dependencies-using-in-isolation
*/
plugins {
id "org.springframework.boot" version "2.1.4.RELEASE" apply false
}
// Set version for dependencies share between subprojects
ext {
commonsIoVersion = "2.6"
}
subprojects {
// common config for all Java subprojects
apply plugin: "java"
apply plugin: "eclipse"
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
// apply Spring Boot's dependency management plugin
apply plugin: "io.spring.dependency-management"
}
Library sub-project
// no need for additional plugins
jar {
baseName = 'gs-multi-module-library'
version = '0.0.1-SNAPSHOT'
}
dependencies {
implementation('org.springframework.boot:spring-boot-starter')
implementation "commons-io:commons-io:${commonsIoVersion}"
testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
imports {
mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
}
}
Application sub-project
plugins {
id "org.springframework.boot"
}
bootJar {
baseName = 'gs-multi-module-application'
version = '0.0.1-SNAPSHOT'
}
dependencies {
implementation project(':library')
implementation ('org.springframework.boot:spring-boot-starter-actuator')
implementation ('org.springframework.boot:spring-boot-starter-web')
implementation "commons-io:commons-io:${commonsIoVersion}"
// could also be configured in root project.
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Notes
this solution uses the new plugins {} DSL only (no need for old buildscript block)
version of the io.spring.dependency-management should not be configured explicitly, it will be inherit from Spring boot plugin

You can move the ext{} block to a new file and reference it in your project's build.gradle file via the apply from: statement.
// project/versions.gradle
ext {
springBootVersion = '2.1.4.RELEASE'
}
// project/build.gradle
buildscript {
apply from: 'versions.gradle'
}
// module/build.gradle
dependencies {
implementation "some.dependency:dependency:$springBootVersion"
}
Now you only have to define your dependency versions in one place.
Typically, a project will have a project-level build.gradle file in addition to module-specific build.gradle files. However, the repo you shared is missing the project-level build script. This is why the ext{} block is defined in each module's build script. This is likely not optimal, and I recommend looking at other repos to see how different developers tackled this issue.

There's a Gradle consistent versions plugin that will handle this:
https://github.com/palantir/gradle-consistent-versions
After you set this plugin up in the root build.gradle file, you will create a versions.props file like this, to use the example from the documentation.
com.fasterxml.jackson.*:jackson-* = 2.9.6
com.google.guava:guava = 21.0
com.squareup.okhttp3:okhttp = 3.12.0
junit:junit = 4.12
org.assertj:* = 3.10.0
There's also a versions.locks file, which is auto-generated by ./gradlew --write-locks. You can then specify version-less dependencies in your build.gradle files.

Related

Gradle spring boot 2 spring-boot-dependencies cannot download dependencies

I am using eclipse 2018, gradle 5.2.1, buildship 3.0.1.
My config looks like:
I try to create spring boot 2 according to building-spring-boot-2-projects-with-gradle
The build.gradle is:
plugins {
id 'java'
id 'com.gradle.build-scan' version '2.1'
id 'org.springframework.boot' version '2.1.3.RELEASE'
}
repositories {
jCenter()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-dependencies:2.1.3.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
components {
withModule('org.springframework:spring-beans') {
allVariants {
withDependencyConstraints {
// Need to patch constraints because snakeyaml is an optional dependency
it.findAll { it.name == 'snakeyaml' }.each { it.version { strictly '1.23' } }
}
}
}
}
}
buildScan {
// always accept the terms of service
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
termsOfServiceAgree = 'yes'
// always publish a build scan
publishAlways()
}
bootJar {
mainClassName = 'gt4.App'
}
However, after I save build.gradle, the Project and External Dependencies disappear, and the spring boot jars are not downloaded too.
What I was wrong?
If I create spring boot project with gradle by Spring Tool Suite 4, the generated build.gradle is:
plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
then it works.
Do I use spring-boot-dependencies wrong?
try to deleting the local Repository and build project again to download dependancy.
Delete .m2 or .gradle folder and then Rebuild your project.
a)On a Windows machine, the .gradle or .m2 path will be:
c:\Users\username\.m2 or c:\Users\username\.m2
b)On linux machine, the .gradle or .m2 path will be:
USER_HOME/.m2/

Could not find method bootJar() for arguments

While building the gradle project I am getting below error-
FAILURE: Build failed with an exception.
Where:
Build file '/Users/vdubey/Documents/microservices/workspace/Promo-Service/build.gradle' line: 30
What went wrong:
A problem occurred evaluating root project 'Promo-Service'.
Could not find method bootJar() for arguments [build_3jq74tz48uic808y18txabjvx$_run_closure1#5c4aa147] on root project 'Promo-Service' of type org.gradle.api.Project.
Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
Get more help at https://help.gradle.org
Any clue why it is failing?
I had this error while following the Spring Boot with Docker guide because my application is using Spring Boot 1.5.10.RELEASE and bootRun was only introduced in 2.0.0.
Luckily, the Spring Boot Docker guide code is in a Github repository, so I was able to navigate back to a pre 2.0.0 version: https://github.com/spring-guides/gs-spring-boot-docker/tree/8933f6efa9a94cf596095658dc0b81986d11a3ec
See the completed build.gradle file for 1.5.10-RELEASE:
// This is used as the docker image prefix (org)
group = 'springio'
jar {
baseName = 'gs-spring-boot-docker'
version = '0.1.0'
}
// tag::task[]
docker {
name "${project.group}/${jar.baseName}"
files jar.archivePath
buildArgs(['JAR_FILE': "${jar.archiveName}"])
}
// end::task[]
Consider checking the presence of gradle plugin for Spring Boot:
https://plugins.gradle.org/plugin/org.springframework.boot
For Gradle 2.1 and later:
plugins {
id "org.springframework.boot" version "2.1.0.RELEASE"
}
For older Gradle versions:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.RELEASE"
}
}
apply plugin: "org.springframework.boot"
I had this problem when I am following official Spring testing-web guide. They offer initial gradle file as below.
buildscript {
repositories { mavenCentral() }
}
plugins { id "io.spring.dependency-management" version "1.0.4.RELEASE" }
ext { springBootVersion = '2.0.5.RELEASE' }
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
bootJar {
baseName = 'gs-testing-web'
version = '0.1.0'
}
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
testCompile("org.springframework.boot:spring-boot-starter-test")
}
task wrapper(type: Wrapper) {
gradleVersion = '4.6'
}
But when I run the ./gradlew clean command I get the below exception
Could not find method bootJar() for arguments [] on root project '' of type org.gradle.api.Project.
The problem is instead of using plugins { id "io.spring.dependency-management" version "1.0.4.RELEASE" } use id "org.springframework.boot" version "2.1.3.RELEASE". Also don't forget the java and io.spring.dependency-management plugins.
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
8.1. Reacting to the Java plugin
When Gradle’s java plugin is applied to a project, the Spring Boot plugin:
Creates a BootJar task named bootJar that will create an executable,
fat jar for the project. The jar will contain everything on the
runtime classpath of the main source set; classes are packaged in
BOOT-INF/classes and jars are packaged in BOOT-INF/lib
8.4. Reacting to the dependency management plugin
When the io.spring.dependency-management plugin is applied to a
project, the Spring Boot plugin will automatically import the
spring-boot-dependencies bom.
I shared valid build.gradle file below which can be a starting point for anyone who have this problem.
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java project to get you started.
* For more details take a look at the Java Quickstart chapter in the Gradle
* user guide available at https://docs.gradle.org/4.6/userguide/tutorial_java_projects.html
*/
plugins {
id "org.springframework.boot" version "2.1.3.RELEASE"
}
apply plugin: 'idea'
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
ext {
springBootVersion = '2.1.3.RELEASE'
}
// In this section you declare where to find the dependencies of your project
repositories {
// You can declare any Maven/Ivy/file repository here.
mavenCentral()
}
bootJar {
mainClassName = 'com.softwarelabs.App'
baseName = 'spring-boot-integration-test'
version = '0.1.0'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
// This dependency is found on compile classpath of this component and consumers.
compile 'com.google.guava:guava:23.0'
testCompile("org.springframework.boot:spring-boot-starter-test")
// Use JUnit test framework
testCompile 'junit:junit:4.12'
}
task wrapper(type: Wrapper) {
gradleVersion = '4.6'
}
For more details please check the spring boot gradle plugin documentation getting started part.
Check how the Spring Boot plugin reacts by other plugins.
I had this error while building a repository I had downloaded.
I was able to resolve the same by modifying my build.gradle to include a buildscript dependency for spring-boot-gradle-plugin and apply org.springframework.boot as a plugin
buildscript {
ext {
springBootVersion = '<Spring boot version>'
}
repositories {
...<my repository config>...
}
// These are gradle build dependencies and not application requirements
dependencies {
...<other dependencies>...
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
}
}
apply plugin: 'org.springframework.boot'

Using spring-boot Gradle plugin but getting spring boot version from dependency management

I have a Gradle setup where I currently need to hard code the spring-boot version. Is it possible to do this with dependency management instead? The issue occurs in my project module that contains the actual code. There I need a call to :
springBoot {
requiresUnpack = ["com.example:util"]
}
In order to do that I need to apply the spring-boot plugin. For that plugin to be available I need to have a dependency to "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}". And for that I need to specify the springBootVersion hard coded...
This is my parent gradle file:
buildscript {
ext {
springBootVersion = '1.4.0.RELEASE'
}
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath 'io.spring.gradle:dependency-management-plugin:0.6.0.RELEASE'
}
}
plugins {
id "io.spring.dependency-management" version "0.6.0.RELEASE"
}
group 'test'
repositories {
mavenCentral()
}
allprojects {
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
// Note that these two BOM's are not always easy to combine, always define cloud first and try to find versions with same version of spring-boot when upgrading.
mavenBom 'org.springframework.cloud:spring-cloud-starter-parent:Brixton.SR5'
mavenBom 'io.spring.platform:platform-bom:2.0.7.RELEASE'
}
}
}
and this is my module gradle file:
group = "test"
apply plugin: 'spring-boot'
apply plugin: 'io.spring.dependency-management'
sourceCompatibility = 1.8
targetCompatibility = 1.8
// Temporary fix for jersey
springBoot {
requiresUnpack = ["com.example:util"]
}
If I remove the dependency to spring-boot-gradle-plugin in parent gradle file, then I cannot use the apply plugin: 'spring-boot' in the module gradle file and without that the gradle DLS method springBoot { is not defined...
Can I achieve this without having to hard code springBootVersion = '1.4.0.RELEASE'in my parent gradle file?

Gradle dependency plugin in a multi module Spring Boot project

What does a correct Gradle configuration look like in a multi-module project that uses the Gradle plugins spring-boot-dependencies and spring-boot?
I have the following project setup:
parent
|
+ build.gradle
|
+ alpha
| |
| + build.gradle
|
+ beta
| |
| + build.gradle
The parent module contains common project configuration.
The alpha module is a module in which I would like to import dependencies using versions numbers specified in the spring-boot-dependencies bom, but the result of the is a standard jar.
The beta module is a module that depends on alpha and the result is an executable Spring Boot jar file (including all dependencies). Consequently, this project needs both the spring-boot-dependencies as well as the spring-boot plugin.
In order to keep the Gradle files DRY, I have extracted common module scripts to the parent's build.gradle file.
Attempts to execute $ gradle build using the project configuration below results in:
> Plugin with id 'io.spring.dependency-management' not found.
parent gradle.build
allprojects {
group = "com.example"
version '0.0.1-SNAPSHOT'
ext {
dependencyManagementPluginVersion = '0.5.3.RELEASE'
springBootVersion = '1.3.0.RC1'
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
}
subprojects {
sourceCompatibility = 1.8
targetCompatibility = 1.8
buildscript {
repositories {
jcenter()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
classpath("io.spring.gradle:dependency-management-plugin:${dependencyManagementPluginVersion}")
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
// mavenBom("org.springframework.boot:spring-boot-starter-parent:${springBootVersion}")
}
}
}
alpha build.gradle
dependencies {
compile('org.springframework:spring-web')
}
beta gradle.build
apply plugin: 'spring-boot'
dependencies {
compile project(':alpha')
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework.boot:spring-boot-starter-web')
}
Comments:
The behavior of the spring-boot plugin was changed in Spring Boot 1.3.0.M1
Gradle version: 2.8
Spring Boot version 1.3.0.RC1
It turns out that parent/build.gradle should be rearranged in the following way:
buildscript {
ext {
dependencyManagementPluginVersion = '0.5.3.RELEASE'
springBootVersion = '1.3.0.RC1'
}
repositories {
jcenter()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
classpath("io.spring.gradle:dependency-management-plugin:${dependencyManagementPluginVersion}")
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
allprojects {
group = "com.example"
version '0.0.1-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
}
subprojects {
sourceCompatibility = 1.8
targetCompatibility = 1.8
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}")
// mavenBom("org.springframework.boot:spring-boot-starter-parent:${springBootVersion}")
}
}
}
The problem lies in the fact that buildscript block for subprojects was indeed configured well but... in a wrong place. This subprojects block relates to subprojects but it will be evaluated in the script it was declared, and there were no dependencies declared for the plugin it was trying to apply.

Spring Boot Gradle add Native library failed (java.lang.UnsatisfiedLinkError)

I am trying to add a native library( .dll file) to a basic spring gradle project.
I have tried many different settings and all of them didn't work in a basic java project , I have successfully ran that dll file by adding VM argument: java.library.path gradle
here's the script:
buildscript {
ext {
springBootVersion = '1.2.5.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'messaging'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-aop")
compile("org.springframework.boot:spring-boot-starter-amqp")
compile("org.springframework:spring-messaging")
compile('javax.inject:javax.inject:1')
compile('com.google.guava:guava:11.0.2')
compile('org.codehaus.jackson:jackson-core-asl:1.1.0')
compile('org.codehaus.jackson:jackson-mapper-asl:1.9.13')
testCompile("org.springframework.boot:spring-boot-starter-test")
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
I have tried:
add VM arguments on eclipse
add jvm properties
add gradle system property ( can not succeeded to add that parameter)
I couldn't understand why but when I follow the steps below, it worked:
1. I changed the java package name
2. rebuild the project with gradle
3. run the project

Resources