# Building Java Applications with libraries Sample

version 8.14.3-20250906041242+0000

- Groovy DSL

- Kotlin DSL

| You can open this sample in an IDE that supports Gradle.

To prepare your software project for growth, you can organize a Gradle project into multiple subprojects to modularize the software you are building. In this guide, you’ll learn how to structure such a project on the example of a Java application. However, the general concepts apply for any software you are building with Gradle. You can follow the guide step-by-step to create a new project from scratch or download the complete sample project using the links above.

## What you’ll build

You’ll build a Java application that consists of an application and multiple library projects.

## What you’ll need

- A text editor or IDE - for example IntelliJ IDEA

- A Java Development Kit (JDK), version 8 or higher - for example AdoptOpenJDK

- The latest Gradle distribution

## Create a project folder

Gradle comes with a built-in task, called `init`, that initializes a new Gradle project in an empty folder. The `init` task uses the (also built-in) `wrapper` task to create a Gradle wrapper script, `gradlew`.

The first step is to create a folder for the new project and change directory into it.

    $ mkdir demo
    $ cd demo

## Run the init task

From inside the new project directory, run the `init` task using the following command in a terminal: `gradle init`. When prompted, select the `1: application` project type and `1: Java` as the implementation language. Afterwards, select `2: Application and library project`. Next you can choose the DSL for writing buildscripts - `1 : Kotlin` or `2: Groovy`. For the other questions, press enter to use the default values.

The output will look like this:

    $ gradle init

    Select type of build to generate:
      1: Application
      2: Library
      3: Gradle plugin
      4: Basic (build structure only)
    Enter selection (default: Application) [1..4] 1

    Select implementation language:
      1: Java
      2: Kotlin
      3: Groovy
      4: Scala
      5: C++
      6: Swift
    Enter selection (default: Java) [1..6]  1

    Project name (default: demo):

    Enter target Java version (min: 7, default: 21):

    Select application structure:
      1: Single application project
      2: Application and library project
    Enter selection (default: Single application project) [1..2] 2

    Select build script DSL:
      1: Kotlin
      2: Groovy
    Enter selection (default: Kotlin) [1..2]

    Select test framework:
      1: JUnit 4
      2: TestNG
      3: Spock
      4: JUnit Jupiter
    Enter selection (default: JUnit Jupiter) [1..4]

    Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]

    BUILD SUCCESSFUL
    1 actionable task: 1 executed

The `init` task generates the new project with the following structure:

    ├── gradle (1)
    │   ├── libs.versions.toml (2)
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradlew (3)
    ├── gradlew.bat (3)
    ├── settings.gradle.kts (4)
    ├── buildSrc
    │   ├── build.gradle.kts (5)
    │   ├── settings.gradle.kts (5)
    │   └── src
    │       └── main
    │           └── kotlin (6)
    │               ├── buildlogic.java-application-conventions.gradle.kts
    │               ├── buildlogic.java-common-conventions.gradle.kts
    │               └── buildlogic.java-library-conventions.gradle.kts
    ├── app
    │   ├── build.gradle.kts (7)
    │   └── src
    │       ├── main (8)
    │       │   └── java
    │       │       └── demo
    │       │           └── app
    │       │               ├── App.java
    │       │               └── MessageUtils.java
    │       └── test (9)
    │           └── java
    │               └── demo
    │                   └── app
    │                       └── MessageUtilsTest.java
    ├── list
    │   ├── build.gradle.kts (7)
    │   └── src
    │       ├── main (8)
    │       │   └── java
    │       │       └── demo
    │       │           └── list
    │       │               └── LinkedList.java
    │       └── test (9)
    │           └── java
    │               └── demo
    │                   └── list
    │                       └── LinkedListTest.java
    └── utilities
        ├── build.gradle.kts (7)
        └── src
            └── main (8)
                └── java
                    └── demo
                        └── utilities
                            ├── JoinUtils.java
                            ├── SplitUtils.java
                            └── StringUtils.java

    ├── gradle (1)
    │   ├── libs.versions.toml (2)
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradlew (3)
    ├── gradlew.bat (3)
    ├── settings.gradle (4)
    ├── buildSrc
    │   ├── build.gradle (5)
    │   ├── settings.gradle (5)
    │   └── src
    │       └── main
    │           └── groovy (6)
    │               ├── buildlogic.java-application-conventions.gradle
    │               ├── buildlogic.java-common-conventions.gradle
    │               └── buildlogic.java-library-conventions.gradle
    ├── app
    │   ├── build.gradle (7)
    │   └── src
    │       ├── main (8)
    │       │   └── java
    │       │       └── demo
    │       │           └── app
    │       │               ├── App.java
    │       │               └── MessageUtils.java
    │       └── test (9)
    │           └── java
    │               └── demo
    │                   └── app
    │                       └── MessageUtilsTest.java
    ├── list
    │   ├── build.gradle (7)
    │   └── src
    │       ├── main (8)
    │       │   └── java
    │       │       └── demo
    │       │           └── list
    │       │               └── LinkedList.java
    │       └── test (9)
    │           └── java
    │               └── demo
    │                   └── list
    │                       └── LinkedListTest.java
    └── utilities
        ├── build.gradle (7)
        └── src
            └── main (8)
                └── java
                    └── demo
                        └── utilities
                            ├── JoinUtils.java
                            ├── SplitUtils.java
                            └── StringUtils.java

1 | Generated folder for wrapper files
2 | Generated version catalog
3 | Gradle wrapper start scripts
4 | Settings file to define build name and subprojects
5 | Build script of buildSrc to configure dependencies of the build logic
6 | Source folder for convention plugins written in Groovy or Kotlin DSL
7 | Build script of the three subprojects - `app`, `list` and `utilities`
8 | Java source folders in each of the subprojects
9 | Java test source folders in the subprojects

You now have the project setup to build a Java application which is modularized into multiple subprojects.

## Review the project files

The `settings.gradle(.kts)` file has two interesting lines:

settings.gradle.kts

    rootProject.name = "demo"
    include("app", "list", "utilities")

settings.gradle

    rootProject.name = 'demo'
    include('app', 'list', 'utilities')

- `rootProject.name` assigns a name to the build, which overrides the default behavior of naming the build after the directory it’s in. It’s recommended to set a fixed name as the folder might change if the project is shared - e.g. as root of a Git repository.

- `include("app", "list", "utilities")` defines that the build consists of three subprojects in the corresponding folders. More subprojects can be added by extending the list or adding more `include(…​)` statements.

Since our build consists of multiple-subprojects, we want to share build logic and configuration between them. For this, we utilize so-called convention plugins that are located in the `buildSrc` folder. Convention plugins in `buildSrc` are an easy way to utilise Gradle’s plugin system to write reusable bits of build configuration.

in this sample, we can find three such convention plugins that are based on each other:

buildSrc/src/main/kotlin/buildlogic.java-common-conventions.gradle.kts

    plugins {
        java (1)
    }

    repositories {
        mavenCentral() (2)
    }

    dependencies {
        constraints {
            implementation("org.apache.commons:commons-text:1.13.0") (3)
        }

        testImplementation("org.junit.jupiter:junit-jupiter:5.12.1") (4)

        testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    }

    tasks.named<Test>("test") {
        useJUnitPlatform() (5)
    }

buildSrc/src/main/groovy/buildlogic.java-common-conventions.gradle

    plugins {
        id 'java' (1)
    }

    repositories {
        mavenCentral() (2)
    }

    dependencies {
        constraints {
            implementation 'org.apache.commons:commons-text:1.13.0' (3)
        }

        testImplementation 'org.junit.jupiter:junit-jupiter:5.12.1' (4)

        testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    }

    tasks.named('test') {
        useJUnitPlatform() (5)
    }

The `java-common-conventions` defines some configuration that should be shared by all our Java project — independent of whether they represent a library or the actual application. First, we apply the Java Plugin (1) to have all functionality for building Java projects available. Then, we declare a repository —`mavenCentral()`— as source for external dependencies (2), define dependency constraints (3) as well as standard dependencies that are shared by all subprojects and set JUnit 5 as testing framework (4…​). Other shared settings, like compiler flags or JVM version compatibilities, could also be set here.

buildSrc/src/main/kotlin/buildlogic.java-library-conventions.gradle.kts

    plugins {
        id("buildlogic.java-common-conventions") (1)
        `java-library` (2)
    }

buildSrc/src/main/groovy/buildlogic.java-library-conventions.gradle

    plugins {
        id 'buildlogic.java-common-conventions' (1)
        id 'java-library' (2)
    }

buildSrc/src/main/kotlin/buildlogic.java-application-conventions.gradle.kts

    plugins {
        id("buildlogic.java-common-conventions") (1)
        application (2)
    }

buildSrc/src/main/groovy/buildlogic.java-application-conventions.gradle

    plugins {
        id 'buildlogic.java-common-conventions' (1)
        id 'application' (2)
    }

Both `java-library-conventions` and `java-application-conventions` apply the `java-common-conventions` plugin (1) so that the configuration performed there is shared by library and application projects alike. Next they apply the `java-library` or `application` plugin respectively (2) thus combining our common configuration logic with specifics for a library or application. While there is no more fine grained configuration in this example, library or application project specific build configuration can go into one of these convention plugin scripts.

Lets have a look at the `build.gradle(.kts)` files in the subprojects.

app/build.gradle.kts

    plugins {
        id("buildlogic.java-application-conventions")
    }

    dependencies {
        implementation("org.apache.commons:commons-text")
        implementation(project(":utilities"))
    }

    application {
        mainClass = "demo.app.App" (1)
    }

app/build.gradle

    plugins {
        id 'buildlogic.java-application-conventions'
    }

    dependencies {
        implementation 'org.apache.commons:commons-text'
        implementation project(':utilities')
    }

    application {
        mainClass = 'demo.app.App' (1)
    }

list/build.gradle.kts

    plugins {
        id("buildlogic.java-library-conventions")
    }

list/build.gradle

    plugins {
        id 'buildlogic.java-library-conventions'
    }

utilities/build.gradle.kts

    plugins {
        id("buildlogic.java-library-conventions")
    }

    dependencies {
        api(project(":list"))
    }

utilities/build.gradle

    plugins {
        id 'buildlogic.java-library-conventions'
    }

    dependencies {
        api project(':list')
    }

Looking at the build scripts, we can see that they include up to three blocks

- Every build script should have a `plugins {}` block to apply plugins. In a well-structured build, it may only apply one convention plugin as in this example. The convention plugin will then take care of applying and configuring core Gradle plugins (like `application` or `java-library`) other convention plugin or community plugins from the Plugin Portal.

- Second, if the project has dependencies, a `dependencies {}` block should be added. Dependencies can be external, such as the JUnit dependencies we add in `java-common-conventions`, or can point to other local subprojects. For this, the `project(…​)` notation is used. In our example, the `utilities` library requires the `list` library. And the `app` makes use of the `utilities` library. If local projects depend on each other, Gradle takes care of building dependent projects if (and only if) needed. To learn more, have a look at the documentation about dependency management in Gradle.

- Third, there may be one or multiple configuration blocks for plugins. These should only be used in build scripts directly if they configure something specific for the one project. Otherwise, such configurations also belong into a convention plugin. In this example, we use the `application {}` block, which is specific to the `application` plugin, to set the `mainClass` in our `app` project to `demo.app.App` (1).

The last build file we have is the `build.gradle(.kts)` file in `buildSrc`.

buildSrc/build.gradle.kts

    plugins {
        `kotlin-dsl` (1)
    }

    repositories {
        gradlePluginPortal() (2)
    }

buildSrc/build.gradle

    plugins {
        id 'groovy-gradle-plugin' (1)
    }

    repositories {
        gradlePluginPortal() (2)
    }

This file is setting the stage to build the convention plugins themselves. By applying one of the plugins for plugin development —`groovy-gradle-plugin` or `kotlin-dsl`—(1) we enable the support for writing convention plugins as build files in `buildSrc`. Which are the convention plugins we already inspected above. Furthermore, we add Gradle’s plugin portal as repository (2), which gives us access to community plugins. To use a plugin it needs to be declared as dependency in the `dependencies {}` block.

Apart from the Gradle build files, you can find example Java source code and test source code in the corresponding folders. Feel free to modify these generated sources and tests to explore how Gradle reacts to changes when running the build as described next.

## Run the tests

You can use `./gradlew check` to execute all tests in all subprojects. When you call Gradle with a plain task name like `check`, the task will be executed for all subprojects that provide it. To target only a specific subproject, you can use the full path to the task. For example `:app:check` will only execute the tests of the `app` project. However, the other subprojects will still be compiled in the case of this example, because `app` declares dependencies to them.

    $ ./gradlew check

    BUILD SUCCESSFUL
    9 actionable tasks: 9 executed

Gradle won’t print more output to the console if all tests passed successfully. You can find the test reports in the `<subproject>/build/reports` folders. Feel free to change some of the example code or tests and rerun `check` to see what happens if a test fails.

## Run the application

Thanks to the `application` plugin, you can run the application directly from the command line. The `run` task tells Gradle to execute the `main` method in the class assigned to the `mainClass` property.

    $ ./gradlew run

    > Task :app:run
    Hello world!

    BUILD SUCCESSFUL
    2 actionable tasks: 2 executed

| The first time you run the wrapper script, `gradlew`, there may be a delay while that version of `gradle` is downloaded and stored locally in your `~/.gradle/wrapper/dists` folder.

## Bundle the application

The `application` plugin also bundles the application, with all its dependencies, for you. The archive will also contain a script to start the application with a single command.

    $ ./gradlew build

    BUILD SUCCESSFUL in 0s
    7 actionable tasks: 7 executed

If you run a full build as shown above, Gradle will have produced the archive in two formats for you: `app/build/distributions/app.tar` and `app/build/distributions/app.zip`.

## Publish a Build Scan

The best way to learn more about what your build is doing behind the scenes, is to publish a build scan. To do so, just run Gradle with the `--scan` flag.

    $ ./gradlew build --scan

    BUILD SUCCESSFUL in 0s
    7 actionable tasks: 7 executed

    Publishing a build scan to scans.gradle.com requires accepting the Gradle Terms of Service defined at https://gradle.com/terms-of-service.
    Do you accept these terms? [yes, no] yes

    Gradle Terms of Service accepted.

    Publishing build scan...
    https://gradle.com/s/5u4w3gxeurtd2

Click the link and explore which tasks where executed, which dependencies where downloaded and many more details!

## Summary

That’s it! You’ve now successfully configured and built a Java application project with Gradle. You’ve learned how to:

- Initialize a project that produces a Java application

- Create a modular software project by combining multiple subprojects

- Share build configuration logic between subprojects using convention plugins in `buildSrc`

- Run similar named tasks in all subprojects

- Run a task in a specific subproject

- Build, bundle and run the application

## Next steps

When your project grows, you might be interested in more details how to configure JVM projects, structuring multi-project builds and dependency management:

- Building Java & JVM projects

- Working with multi-project builds

- Dependency management in Gradle