← Back to Articles

Gradle Hero Course Notes

Tom Gregory
Tom Gregory
Jan 8, 202619 min read

Code, docs, and other details for Gradle Hero.

The Gradle plugin

Base plugin documentation.

Practical: creating a simple Gradle project

Installing Gradle

Windows Gradle Installation

Let's start by validating your Java installation.

  • open the Windows command prompt (open the Start menu, type cmd, then hit Enter)

  • run java -version

    it should print out details of your current Java installation. Gradle supports versions 8 and above.

Now it's time to download Gradle.

  • go to https://gradle.org/releases
  • scroll down and choose the most recent Gradle release. At the time of recording this was 6.8.
  • choose the binary-only option. Click the link to download the Gradle zip file to your computer.

You'll now create a Gradle directory on your hard drive, and extract the zip file into that directory.

  • open File Explorer (open the Start menu, type file, then hit Enter).
  • navigate to your hard drive root directory then right click, go to New > Folder, then enter the name gradle.
  • navigate to where you downloaded the Gradle zip file, copy the zip file, then paste it in the new gradle directory
  • right click the zip file and select Extract all to extract the zip file using Windows. Or use 7zip which is faster than Windows.
  • you should now have an additional directory. Go into that directory, go into the bin directory, and copy the path from the address bar at the top of the File Explorer window and keep it safe for later on.

Now configure your PATH variable so that you can run Gradle commands from wherever you are in the command prompt

  • go to the Start menu, type environment, then hit Enter when Edit the system environment variables appears.
  • on the dialog that appears, click Environment Variables, then under System variables double click the Path variable
  • click New, then paste in the location of the Gradle bin directory which you copied earlier
  • hit Enter, select OK, then OK again, then OK again
  • close the Windows command prompt, and open a new one (Start menu, type cmd, hit Enter)

Now let's validate your Gradle installation:

  • Run gradle --version

    you should see some output showing that a specific version of Gradle is installed

If you’re a Windows user, well done! That’s all you need to do before moving onto the next lesson.

Linux Gradle Installation

  • validate your Java installation by running java -version

    it should print out details of your current Java installation. Gradle supports versions 8 and above.

  • download the latest version of Gradle using the curl command:

    curl https://downloads.gradle-dn.com/distributions/gradle-6.8-bin.zip --output ~/gradle.zip

  • unzip the file using the unzip command:

    sudo unzip -d /opt/gradle gradle.zip

    if you’re prompted for your password, enter it because we’re running this command as the root user

  • look at the contents of the Gradle installation using the ls command:

    ls /opt/gradle/gradle-6.8

    you should see some files and directories

  • setup the PATH environment variable:

    echo 'export PATH="$PATH:/opt/gradle/gradle-6.8/bin"' >> ~/.bashrc

  • close the terminal and open a new one

  • show that Gradle has been successfully installed.

    gradle --version

    you should see some output showing that a specific version of Gradle is installed

If you’re a Linux user, well done! That’s all you need to do before moving onto the next lesson.

Other installation options

Mac

brew install gradle

SDKMAN!

sdk install gradle 6.8

Creating the project

  1. create new project directory theme-park-rides

  2. in project directory create file settings.gradle

  3. edit file and add single line rootProject.name = 'theme-park-rides'

  4. in project directory create directory descriptions

  5. in descriptions create rollercoaster.txt and add contents At @THEME_PARK_NAME@ thrill seekers should head straight to the rollercoaster. Hands in the air now!

  6. in descriptions create log-flume.txt and add contents At @THEME_PARK_NAME@ take a ride on the log flume on a hot sunny day. You'll certainly find it refreshing!

  7. in descriptions create teacups.txt and add contents At @THEME_PARK_NAME@ if you want some leisurely fun, head to the teacups. Teatime will never be the same again!

  8. in root of project directory create file build.gradle

  9. in build.gradle add

    import org.apache.tools.ant.filters.ReplaceTokens
    plugins {
        id 'base'
    }
     
    tasks.register('generateDescriptions', Copy) {
        from 'descriptions'
        into "$buildDir/descriptions"
        filter(ReplaceTokens, tokens: [THEME_PARK_NAME: "Grelephant's Wonder World"])
    }
  10. on the command line in root of project directory run gradle generateDescriptions

  11. observe that description files have been created in build/descriptions

You can find a sample project in the Gradle Hero GitHub repository.

In Chapter 3 you'll learn how to create a Kotlin version of theme-park-rides.

"Zip" challenge

Gradle Zip task documentation

You can find a sample project in the Gradle Hero GitHub repository.

Command line interaction

In Gradle 8+ running ./gradlew generateDescriptions zipDescriptions produces an error about task dependencies. See the Chapter 3 lesson Task dependencies and ordering to learn how to fix this.

Download the Gradle command line cheat sheet.

Practical "start from scratch"

Creating the project

The following commands are for a Linux environment. Within Windows use an equivalent.

  1. Create the project directory

    mkdir theme-park-rides-from-scratch

  2. Navigate to the project directory

    cd theme-park-rides-from-scratch

  3. Start the Gradle init wizard

    gradle init

  4. Hit Enter 3 times to accept the defaults

  5. Copy the descriptions directory from theme-park-rides project from chapter 1

    cp -r ../theme-park-rides/descriptions/ .

  6. Copy build.gradle from theme-park-rides project from chapter 1

    cp ../theme-park-rides/build.gradle .

  7. Run the generateDescriptions task using the Gradle wrapper and abbreviated task name

    ./gradlew gD

Committing the project

  1. Initialize the project as a Git repository

    git init

  2. Stage all files to be committed

    git add .

  3. Check the files to be committed. .gradle and build should NOT be committed.

    git status

  4. Commit the files

    git commit -m "First commit of project."

  5. Double check everything has been committed

    git status

Opening the project in an IDE

The following instructions are for IntelliJ IDEA.

  1. from the welcome screen select Open. Optionally use File > Open.
  2. Select the theme-park-rides-from-scratch directory and hit OK
  3. Once the project has loaded, open the Gradle panel
  4. Open tasks > build and double click clean
  5. Open tasks > other and double click generateDescriptions
  6. In the Project dialog check the generated descriptions in build/descriptions

You can find a sample project in the Gradle Hero GitHub repository.

Groovy build scripts

Download the Gradle Groovy DSL cheat sheet.

Here are some code examples for the 4 main Groovy language features used by Gradle.

1. Run code as a script

Groovy

println('Hello from Groovy world')

Java equivalent

package com.tomgregory;
 
public class PrintMessage {
   public static void main(String[] args) {
       System.out.println("Hello from Java world");
   }
}

Gradle

println('Hello from Gradle world')

2. Optional brackets

Groovy

def multiply(first, second) {
   println first * second
}
 
multiply 2, 3

Java equivalent

package com.tomgregory;
 
public class Multiplier {
    public void multiply(int first, int second) {
        System.out.println(first * second);
    }
 
    public static void main(String[] args) {
        new Multiplier().multiply(2, 3);
    }
}

Gradle

plugins {
  id 'base'
}

3. Supports closures

Groovy 1

def callMeLater = {
   println 'Ring ring'
}
 
callMeLater()

Groovy 2

def pickupPhone(name, phoneNoise) {
   phoneNoise()
   println 'Hello? ' + name + ' here'
}
 
pickupPhone('Tom', {
   println 'Ring ring'
})

Java equivalent

No equivalent

Gradle

tasks.register('closuresInAction') {
  doLast {
     println 'Task definition using closures'
  }
}

4. Pass closure outside brackets

Groovy

def pickupPhone(name, phoneNoise) {
   phoneNoise()
   println 'Hello? ' + name + ' here'
}
 
pickupPhone('Tom') {
   println 'Ring ring'
}

Java equivalent

No equivalent

Gradle

tasks.register('closuresInAction') {
  doLast {
     println 'Task definition using closures'
  }
}

Kotlin build scripts

Check out the theme-park-rides-kotlin project created in this lesson.

Groovy or Kotlin. Which to choose?

Gradle have documentation on Migrating build logic from Groovy to Kotlin.

Configuring your project

Check out the Gradle Project API documentation.

Defining tasks

  • task documentation showing what you can configure on all tasks (group, description, doLast etc.).
  • search for task types in this documentation to see all the pre-packaged/built in tasks that are available.

Task dependencies & ordering

In Gradle 8+, you must setup a task dependency as shown in the video to execute two tasks which are dependent in some way, otherwise you get an error.

Task inputs & outputs

Check out the sample project which includes using inputs & outputs to link tasks.

Repositories & dependencies

Browse these Maven repositories built into Gradle:

Practical: building a Java project"

Creating the project

  1. create a new project directory theme-park-rides-status
  2. cd into that directory
  3. run gradle init
  4. select basic project
  5. select Groovy DSL
  6. accept default project name
  7. open project in IDE

Adding production code

  1. Create directory src/main/java
  2. In there create directory com/gradlehero/themepark
  3. In there create RideStatusService.java
  4. Paste the following class
package com.gradlehero.themepark;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
 
public class RideStatusService {
    public static void main(String[] args) {
        if (args.length != 1) {
            System.out.println("A single ride name must be passed");
            System.exit(1);
        }
 
        String rideName = args[0];
        String rideStatus = getRideStatus(rideName);
 
        System.out.printf("Current status of %s is '%s'%n", rideName, rideStatus);
    }
 
    public static String getRideStatus(String ride) {
        List<String> rideStatuses = readFile(String.format("%s.txt", ride));
        return rideStatuses.get(new Random().nextInt(rideStatuses.size()));
    }
 
    private static List<String> readFile(String filename) {
        InputStream resourceStream = RideStatusService.class.getClassLoader().getResourceAsStream(filename);
        if (resourceStream == null) {
            throw new IllegalArgumentException("Ride not found");
        }
 
        List<String> result = new ArrayList<>();
        try (BufferedReader bufferedInputStream = new BufferedReader(new InputStreamReader(resourceStream, StandardCharsets.UTF_8))) {
            while (bufferedInputStream.ready()) {
                result.add(bufferedInputStream.readLine());
            }
        } catch (IOException exception) {
            throw new RuntimeException("Couldn't read file", exception);
        }
 
        return result;
    }
}

Adding production resources

  1. Create directory src/main/resources
  2. In there create these files

rollercoaster.txt

at the station
climbing to the top
on the precipice
generating screams

logflume.txt

at the station
climbing to the top
on the precipice
soaking passengers

teacups.txt

not spinning
spinning
super-spin vomit mode

Building the application

  1. In build.gradle apply java plugin

    plugins {
        id 'java'
    }
  2. Reload the project in IDE

  3. Run ./gradlew tasks to see new Build tasks

  4. Run ./gradlew tasks --all to see all tasks including compileJava and processResources

  5. Run ./gradlew compileJava

  6. Verify this file exists build/classes/java/main/com/gradlehero/themepark/RideStatusService.class

  7. Run ./gradlew processResources

  8. Verify text files exist in build/resources/main

  9. Run ./gradlew jar

  10. Verify you have the file build/libs/theme-park-rides-status.jar

Running the application

  1. Run java -jar build/libs/theme-park-rides-status.jar rollercoaster

    Oops! This fails with error no main manifest attribute.

  2. Add this to build.gradle below plugin.

    tasks.named('jar') {
        manifest {
            attributes('Main-Class': 'com.gradlehero.themepark.RideStatusService')
        }
    }
  3. Run ./gradlew jar

  4. Run again java -jar build/libs/theme-park-rides-status.jar rollercoaster. Now it should succeed.

  5. Run again with another ride name (e.g. logflume or teacups)

Testing the application

  1. Create src/test/java

  2. In there create package com.gradlehero.themepark

  3. In there create class RideStatusServiceTest.java

  4. Insert this code

    package com.gradlehero.themepark;
     
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.params.ParameterizedTest;
    import org.junit.jupiter.params.provider.ValueSource;
     
    import static org.junit.jupiter.api.Assertions.assertNotNull;
    import static org.junit.jupiter.api.Assertions.assertThrows;
     
    public class RideStatusServiceTest {
        @ParameterizedTest(name = "{index} gets {0} ride status")
        @ValueSource(strings = {"rollercoaster", "logflume", "teacups"})
        public void getsRideStatus(String ride) {
            RideStatusService rideStatusService = new RideStatusService();
            String rideStatus = rideStatusService.getRideStatus(ride);
            assertNotNull(rideStatus);
        }
     
        @Test
        public void unknownRideCausesFailure() {
            RideStatusService rideStatusService = new RideStatusService();
     
            assertThrows(IllegalArgumentException.class, () -> {
                rideStatusService.getRideStatus("dodgems");
            });
        }
    }
  5. Insert the following dependencies at bottom of build.gradle

    dependencies {
        testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
        testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.2'
        testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
    }
  6. Add repositories before dependencies

    repositories {
        mavenCentral()
    }
  7. Run ./gradlew test

  8. View tests report build/reports/tests/test/index.html in browser

    Ooops! No tests have run.

  9. Insert the following at bottom of build.gradle

    tasks.withType(Test).configureEach {
        useJUnitPlatform()
    }
  10. Run ./gradlew test again

  11. Refresh test report. 4 tests should now be successful.

Commit project

  1. Run git init
  2. If using IntelliJ IDEA add .idea to .gitignore file
  3. Run git add .
  4. Run git status and verify files to be committed
  5. Run git commit -m "Create Java project for ride status service."

Congratulations! You've just created, built, and tested a Java project with Gradle.

You can find a sample project in the Gradle Hero GitHub repository.

Build customisations

Check out the following documentation on customising how your Java application is built.

Testing Java applications

Learn more about filtering tests with --tests in these docs.

Adding integration tests to a project

JVM Test Suite plugin for Gradle versions >= 7.3:

  • Plugin documentation

  • to add a dependency from integration tests to the production code, use this syntax with brackets in Gradle 7.6+

    implementation project()

TestSets plugin for Gradle versions < 7.3:

Practical: building Spring Boot applications

Creating the project

  1. create a new project directory theme-park-api
  2. cd into that directory
  3. run gradle init
  4. select basic project
  5. select Groovy DSL
  6. accept default project name
  7. open project in IDE

Configure Gradle build

  1. delete comments in build.gradle

  2. add the following build configuration

    plugins {
        id 'java'
    }
     
    repositories {
        mavenCentral()
    }
     
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-web:2.5.3'
    }
     
    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(16)
        }
    }

You can find the latest version of spring-boot-starter-web at mvnrepository.com.

Add Java code

  1. create directory src/main/java

  2. create package com.gradlehero.themepark

  3. add class ThemeParkApplication.java with the following code.

    package com.gradlehero.themepark;
     
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
     
    @SpringBootApplication
    public class ThemeParkApplication {
        public static void main(String[] args) {
            SpringApplication.run(ThemeParkApplication.class);
        }
    }
  4. add class ThemeParkRide.java with the following code.

    package com.gradlehero.themepark;
     
    public record ThemeParkRide(String name, String description) {
    }
  5. add class ThemeParkController.java with the following code.

    package com.gradlehero.themepark;
     
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    import java.util.Arrays;
    import java.util.Iterator;
     
    @RestController
    public class ThemeParkRideController {
        @GetMapping(path = "/rides")
        public Iterator<ThemeParkRide> getRides() {
            return Arrays.asList(
                    new ThemeParkRide("Rollercoaster", "Train ride that speeds you along."),
                    new ThemeParkRide("Log flume", "Boat ride with plenty of splashes."),
                    new ThemeParkRide("Teacups", "Spinning ride in a giant tea-cup.")
            ).iterator();
        }
    }

Run the application

Intellij IDEA

  1. right click ThemeParkApplication and select Run
  2. in a browser go to http://localhost:8080/rides and check for a successful JSON response with 3 rides

Application plugin

  1. swap java plugin for application plugin

  2. add this configuration to end of build.gradle

    application {
        mainClass = 'com.gradlehero.themepark.ThemeParkApplication'
    }
  3. stop any running applications

  4. run ./gradlew run

  5. verify application is still running at http://localhost:8080/rides

Apply Spring Boot Gradle plugin

  1. revert application plugin back to java

  2. remove application plugin configuration

  3. apply the following plugin, using the same version as your spring-boot-starter-web dependency version

    id 'org.springframework.boot' version '2.5.3'
  4. run ./gradlew tasks to see new bootRun and bootJar tasks.

  5. run ./gradlew bootRun to start the application.

Using executable jar file

  1. run ./gradlew assemble and look for theme-park-api.jar in build/libs
  2. run the jar file with java -jar build/libs/theme-park-api.jar (ensure you're using Java 16+)
  3. verify http://localhost:8080/rides still works properly

Applying Spring Dependency Management plugin

  1. apply the following plugin

    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
  2. remove the version from the spring-boot-starter-web dependency. e.g.

    implementation 'org.springframework.boot:spring-boot-starter-web'

    Looks nicer!

  3. run again with ./gradlew bootRun

  4. verify http://localhost:8080/rides still works properly

Commit the changes

  1. Run git init
  2. Add .idea to .gitignore if using IntelliJ IDEA
  3. Run git add .
  4. Run git status
  5. If you're happy run git commit -m "Initial commit of theme park API"

You've just created a Spring Boot application in Gradle following best practices. Well done!

You can find a sample project in the Gradle Hero GitHub repository.

Deployment using .war files

Generate a war file

Follow these steps to modify the theme-park-api project to generate a war rather than jar file.

  1. update the Spring Boot application class to extend SpringBootServletInitializer

    package com.gradlehero.themepark;
     
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
     
    @SpringBootApplication
    public class ThemeParkApplication extends SpringBootServletInitializer {
        public static void main(String[] args) {
            SpringApplication.run(ThemeParkApplication.class);
        }
    }
  2. in the build script replace java plugin with war plugin

    plugins {
        id 'war'
        // other plugins
    }
  3. Run ./gradlew assemble and you have an executable war file in build/libs.

You can run it with java -jar build/libs/theme-park-api.war and access the application at http://localhost:8080/rides.

Deploy to a standalone Tomcat

Follow these steps to deploy the war file generated in the previous section to a standalone Tomcat server.

  1. create a file Dockerfile in the root of your Gradle project.

    FROM tomcat:9-jdk16
    COPY build/libs/theme-park-api.war /usr/local/tomcat/webapps/
  2. build the Docker image with docker build --tag theme-park-api:latest .

  3. run a Docker container with docker run --name theme-park-api -p 8080:8080 --rm theme-park-api:latest

You can now access the application at http://localhost:8080/theme-park-api/rides.

Advanced dependency resolution

Spring Boot documentation showing module replacement recommendation.

Project properties

See what non-project Gradle properties can be passed in these docs.

Practical: creating a multi-project build

Create and initialise project

  1. create directory theme-park-manager

  2. cd into theme-park-manager

  3. run gradle init and create basic Groovy project

  4. copy project from previous building Spring Boot applications practical e.g. cp -R ../theme-park-api/ .

  5. copy project from previous building a Java project practical e.g. cp -R ../theme-park-rides-status/ .

  6. in each subproject directory run:

    rm -rf .git .gradle/ .idea build gradle .gitattributes .gitignore gradlew gradlew.bat settings.gradle

  7. rename theme-park-api to api

  8. rename theme-park-rides-status to service

  9. open theme-park-manager in IDE

  10. in settings.gradle add at bottom include 'api', 'service'

  11. in service/build.gradle change java plugin to java-library plugin

Verify project

  1. run ./gradlew build --console=verbose
  2. run ./gradlew bootRun
  3. verify you can access http://localhost:8080/rides

Return ride status and image path

  1. in build.gradle of api subproject add implementation project(':service') to dependencies

  2. edit ThemeParkRide to include String status, String imagePath

    package com.gradlehero.themepark;
     
    public record ThemeParkRide(String name, String description, String status, String imagePath) {
    }
  3. edit ThemeParkRideController to call RideStatusService and include an image path

    package com.gradlehero.themepark;
     
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    import java.util.Arrays;
    import java.util.Iterator;
     
    @RestController
    public class ThemeParkRideController {
        @GetMapping(path = "/rides")
        public Iterator<ThemeParkRide> getRides() {
            return Arrays.asList(
                    new ThemeParkRide("Rollercoaster", "Train ride that speeds you along.", RideStatusService.getRideStatus("rollercoaster"), "images/rollercoaster.jpg"),
                    new ThemeParkRide("Log flume", "Boat ride with plenty of splashes.", RideStatusService.getRideStatus("logflume"), "images/logflume.jpg"),
                    new ThemeParkRide("Teacups", "Spinning ride in a giant tea-cup.", RideStatusService.getRideStatus("teacups"), "images/teacups.jpg")
            ).iterator();
        }
    }
  4. restart application and verify API response includes status and imagePath

Setup ui project

  1. download ui.zip containing ui project
  2. unzip ui.zip in theme-park-manager
  3. delete ui.zip
  4. add ui to list of includes in settings.gradle
  5. in build.gradle of api subproject add implementation project(':ui') to dependencies
  6. restart the application
  7. access http://localhost:8080
  8. hit the Get rides! button, see the dynamic ride data, and view the images!

Commit the project

  1. run git init
  2. if using IntelliJ IDEA add .idea to .gitignore file
  3. run git add .
  4. run git status
  5. run git commit -m "Initial commit of theme-park-manager."

Well done! You've just created a Gradle multi-project build consisting of an application with 3 different layers.

For a sample implementation, clone the Gradle Hero GitHub repository, and run the application from the theme-park-manager subdirectory.

Working with files

Documentation for:

  • passing files to from of the Copy and Zip tasks (docs)
  • passing files to into of the Copy and Zip tasks (docs)

Creating custom tasks

  • Javadoc for Property
  • subtypes of Property (e.g. RegularFileProperty)
  • docs about the different task input annotations

Sample code from this lesson

buildSrc/src/main/groovy/com/gradlehero/themepark/FileDiffTask.groovy

package com.gradlehero.themepark
 
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.TaskAction
 
abstract class FileDiffTask extends DefaultTask {
    @InputFile
    abstract RegularFileProperty getFile1()
    @InputFile
    abstract RegularFileProperty getFile2()
 
    @TaskAction
    def diff() {
        if (file1.get().asFile.size() == file2.get().asFile.size()) {
            println "${file1.get().asFile.name} and ${file2.get().asFile.name} have the same size"
        } else if (file1.get().asFile.size() > file2.get().asFile.size()) {
            println "${file1.get().asFile.name} was larger"
        } else {
            println "${file2.get().asFile.name} was larger"
        }
    }
}

Addition to ui/build.gradle

import com.gradlehero.themepark.FileDiffTask
 
tasks.register('fileDiff', FileDiffTask) {
    file1 = file('src/main/resources/static/images/teacups.jpg')
    file2 = file('src/main/resources/static/images/logflume.jpg')
}

Creating custom plugins

Sharing custom plugins

To view the plugin code in more detail, see the file-diff-plugin directory within the Gradle Hero GitHub repository.

Testing custom plugins

To view the plugin test code in more detail, see the file-diff-plugin directory within the Gradle Hero GitHub repository.

Viewing the task graph

Use the taskinfo plugin to print the task graph.

Code coverage analysis with Jacoco

Check out the accompanying code-coverage project on GitHub.

Code style validation with Checkstyle

Code quality validation with PMD

Build & push Docker images

Version catalogs

Use the IntelliJ IDEA TOML plugin for syntax highlighting of TOML files.

Below is the code used within this lesson.

libs.versions.toml

[versions]
junit5 = "5.7.2"
spring-boot = "2.5.3"
 
[libraries]
junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit5" }
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit5" }
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5" }
spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web", version = "" }
 
[plugins]
spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }
spring-dependency-management = { id = "io.spring.dependency-management", version = "1.0.8.RELEASE" }
 
[bundles]
junit-jupiter-implementation = ["junit-jupiter-api", "junit-jupiter-params"]

settings.gradle

rootProject.name = 'theme-park-manager'
 
include 'api', 'service', 'ui'
 
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version('junit5', '5.7.2')
            library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit5')
            library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit5')
            library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit5')
            plugin('spring-boot', 'org.springframework.boot').version('2.5.3')
            plugin('spring-dependency-management', 'io.spring.dependency-management').version('1.0.8.RELEASE')
            library('spring-boot-starter-web', 'org.springframework.boot', 'spring-boot-starter-web').withoutVersion()
        }
    }
}

service build script dependencies

dependencies {
    testImplementation libs.bundles.junit.jupiter.implementation
    testRuntimeOnly libs.junit.jupiter.engine
}

api build script plugins

plugins {
    id 'java'
    alias libs.plugins.spring.boot
    alias libs.plugins.spring.dependency.management
}

api build script dependencies

dependencies {
    implementation project(':service')
    implementation project(':ui')
    implementation libs.spring.boot.starter.web
}

Conclusion

Congratulations on finishing the course!

Lack of Gradle knowledge slowing you down?

A broken build is easy enough to fix. But a misconfigured build is the silent time killer that costs hours every week.

Slow builds. Slow tests. Slow development.

But the official Gradle docs are confusing. Most developers never properly configure their projects—and as codebases grow, builds become bottlenecks.

This guide fixes that. It's a step-by-step walkthrough for Java developers who want to master Gradle fast.

  • Unlock the mysteries of the build script.
  • Speed up your workflow.
  • Make development fun again.