10

I have the following project structure:

RootProject
├── lib
     └── src
          └── main
               └── java
                    └── ...
├── test-project
     └── src
          └── main
               └── java
                    └── ...

└── build.gradle

I trying to follow Gradle best practices for multi project builds, and therefore I describing the whole build process in single build.gradle file.

"RootProject" is just a root project without any code and its single purpose is to provide a single build.gradle for its subprojects: "lib" and "test-project".

"test-project" is a SpringBoot project (micro service) using "lib" as compile dependency.

My common build.gradle looks as the following:

plugins{
    id 'java'
    id 'org.springframework.boot' version '2.1.1.RELEASE'
    id 'io.spring.dependency-management' version '1.0.6.RELEASE'
}


allprojects {
    repositories {
        jcenter()
    }
}


subprojects {

    apply plugin: 'io.spring.dependency-management'
    apply plugin: 'org.springframework.boot'

    group = 'com.xxx.yyy'
    version = '1.0'

    sourceCompatibility = 1.8
    targetCompatibility = 1.8
}


project(":lib"){

    apply plugin: 'java-library'

    dependencies {

        implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
        implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'
        testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
    }

    bootJar { enabled = false }
    jar { enabled = true }
}

project(":test-project"){

    apply plugin: 'java'

    dependencies {
        implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
        implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'
        implementation project(":lib")
        implementation ('mysql:mysql-connector-java')
    }
}

Both "lib" and "test-project" are built successfully. However, when I trying to execute "bootJar" task on root project (which supposed to trigger "build" task on all subprojects) I getting the following error:

Main class name has not been configured and it could not be resolved

Any ideas?

1
  • if the main class is in your lib project and the spring-boot plugin in the test-project you need to configure the main class in the plugin. and just to be sure: the main class has the @SpringBootApplication annotation on it? Commented Jan 10, 2019 at 18:41

1 Answer 1

21

An intro on multi-project build best practices

When you say:

I trying to follow Gradle best practices for multi project builds, and therefore I describing the whole build process in single build.gradle file.

I don't think this is actually is a good practice, and this is not what is really recommended inthe guide you mentioned.

In the guide intro:

While each subproject could configure itself in complete isolation of the other subprojects, it is common that subprojects share common traits. It is then usually preferable to share configurations among projects, so the same configuration affects several subprojects.

=> Indeed, common traits shared between subprojects should be configured in a central place in root project build.

Later in the guide (in this section ) there is a example with subprojects specific configuration defined in the root project script , but there is a note in the example description:

All the build logic is in the build script of the root project.(2)

(2): We do this here, as it makes the layout a bit easier. We usually put the project specific stuff into the build script of the respective projects.

Regarding your issue

I make assumption that your lib project is a plain "simple" java lib project (no Spring or SpringBoot dependencies), and test-project is the project containing the SpringBoot application classes (specially the @SpringBootApplication main application class). Following best practices described above, you should then have three distinct project build scripts:

  • root project: used to defined common configuration share in subproject (java plugin configuration, repositories, shared dependencies, etc...)
  • lib project : specific configuration for this lib project (java-library plugin, specific dependencies, ...)
    • there is no reason to apply spring-boot plugin to this sub-project, and this is actually the cause for your error "cannot find main class"
    • if this project has dependencies on Spring-core modules , you could simply apply the io.spring.dependency-management plugin (in this cas it would make sense to apply this dependecy plugin in the root project build, to use same Spring version in both subprojects)
  • test-projectproject: configuration related to Spring Boot application (SpringBoot plugin and dependencies)

Example

root project build

ext{
    slf4jVersion = "1.7.25"
}

subprojects {
    apply plugin: "java"
    // if both subprojects share some Spring related dependencies, apply the Spring `io.spring.dependency-management` here.

    group = 'com.xxx.yyy'
    version = '1.0'

    sourceCompatibility = 1.8
    targetCompatibility = 1.8

    repositories {
        jcenter()
    }
}

'lib' project build

plugins{
    id 'java-library'
}

dependencies {
    implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion
    // lib dependencies
}

'test-project' build

plugins {     
    id "org.springframework.boot"  version "2.1.1.RELEASE"
}
apply plugin: 'io.spring.dependency-management'

dependencies {
    implementation project(":lib")
    implementation('org.springframework.boot:spring-boot-starter-web')
    testImplementation('org.springframework.boot:spring-boot-starter-test')
    implementation ('mysql:mysql-connector-java')
    // ...
}

EDIT as per your comment: if lib project requires Spring Data related stuff, apply the io.spring.dependency-management as follows ( see more details in Spring reference guide)

plugins{
    id 'java-library'
    id "org.springframework.boot"  version "2.1.1.RELEASE" apply false
}
apply plugin: 'io.spring.dependency-management'

dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}

dependencies {
    implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4jVersion
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 
    // lib dependencies
}
Sign up to request clarification or add additional context in comments.

4 Comments

"lib" is a library having "common" (e.g reusable) code dealing with JPA. So, "lib" needs to have dependency in Spring but it not intended to be executable. The second project ("test-project") is referencing ("lib") as a compile dependency and intended to be executable (spring boot)
As I sais in my answer, in case you need some Spring related deps in lib you simply have to apply the io.spring.dependency-management plugin , not the 'spring-boot' plugin. I updated my answer.
"dependencyManagement" part was missing, now it does compile. Thank you
excellent answer of separation of concerns. and putting (only) the abstract SLF4J abstract jar in the "lower projects". thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.