Gradle Hero Course Notes

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 -versionit should print out details of your current Java installation. Gradle supports versions
8and 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 --versionyou 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 -versionit should print out details of your current Java installation. Gradle supports versions
8and above. -
download the latest version of Gradle using the
curlcommand: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.zipif 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
lscommand:ls /opt/gradle/gradle-6.8you should see some files and directories
-
setup the
PATHenvironment 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 --versionyou 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
-
create new project directory theme-park-rides
-
in project directory create file settings.gradle
-
edit file and add single line
rootProject.name = 'theme-park-rides' -
in project directory create directory descriptions
-
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! -
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! -
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! -
in root of project directory create file build.gradle
-
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"]) } -
on the command line in root of project directory run
gradle generateDescriptions -
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
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.
-
Create the project directory
mkdir theme-park-rides-from-scratch -
Navigate to the project directory
cd theme-park-rides-from-scratch -
Start the Gradle init wizard
gradle init -
Hit Enter 3 times to accept the defaults
-
Copy the descriptions directory from theme-park-rides project from chapter 1
cp -r ../theme-park-rides/descriptions/ . -
Copy build.gradle from theme-park-rides project from chapter 1
cp ../theme-park-rides/build.gradle . -
Run the generateDescriptions task using the Gradle wrapper and abbreviated task name
./gradlew gD
Committing the project
-
Initialize the project as a Git repository
git init -
Stage all files to be committed
git add . -
Check the files to be committed. .gradle and build should NOT be committed.
git status -
Commit the files
git commit -m "First commit of project." -
Double check everything has been committed
git status
Opening the project in an IDE
The following instructions are for IntelliJ IDEA.
- from the welcome screen select Open. Optionally use File > Open.
- Select the theme-park-rides-from-scratch directory and hit OK
- Once the project has loaded, open the Gradle panel
- Open tasks > build and double click clean
- Open tasks > other and double click generateDescriptions
- 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, 3Java 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,doLastetc.). - 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
- create a new project directory theme-park-rides-status
cdinto that directory- run
gradle init - select basic project
- select Groovy DSL
- accept default project name
- open project in IDE
Adding production code
- Create directory src/main/java
- In there create directory com/gradlehero/themepark
- In there create RideStatusService.java
- 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
- Create directory src/main/resources
- In there create these files
rollercoaster.txt
at the station
climbing to the top
on the precipice
generating screamslogflume.txt
at the station
climbing to the top
on the precipice
soaking passengersteacups.txt
not spinning
spinning
super-spin vomit modeBuilding the application
-
In build.gradle apply java plugin
plugins { id 'java' } -
Reload the project in IDE
-
Run
./gradlew tasksto see new Build tasks -
Run
./gradlew tasks --allto see all tasks including compileJava and processResources -
Run
./gradlew compileJava -
Verify this file exists build/classes/java/main/com/gradlehero/themepark/RideStatusService.class
-
Run
./gradlew processResources -
Verify text files exist in build/resources/main
-
Run
./gradlew jar -
Verify you have the file build/libs/theme-park-rides-status.jar
Running the application
-
Run
java -jar build/libs/theme-park-rides-status.jar rollercoasterOops! This fails with error no main manifest attribute.
-
Add this to build.gradle below plugin.
tasks.named('jar') { manifest { attributes('Main-Class': 'com.gradlehero.themepark.RideStatusService') } } -
Run
./gradlew jar -
Run again
java -jar build/libs/theme-park-rides-status.jar rollercoaster. Now it should succeed. -
Run again with another ride name (e.g. logflume or teacups)
Testing the application
-
Create src/test/java
-
In there create package com.gradlehero.themepark
-
In there create class RideStatusServiceTest.java
-
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"); }); } } -
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' } -
Add repositories before dependencies
repositories { mavenCentral() } -
Run
./gradlew test -
View tests report build/reports/tests/test/index.html in browser
Ooops! No tests have run.
-
Insert the following at bottom of build.gradle
tasks.withType(Test).configureEach { useJUnitPlatform() } -
Run
./gradlew testagain -
Refresh test report. 4 tests should now be successful.
Commit project
- Run
git init - If using IntelliJ IDEA add .idea to .gitignore file
- Run
git add . - Run
git statusand verify files to be committed - 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:
-
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
- create a new project directory theme-park-api
cdinto that directory- run
gradle init - select basic project
- select Groovy DSL
- accept default project name
- open project in IDE
Configure Gradle build
-
delete comments in build.gradle
-
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
-
create directory src/main/java
-
create package com.gradlehero.themepark
-
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); } } -
add class ThemeParkRide.java with the following code.
package com.gradlehero.themepark; public record ThemeParkRide(String name, String description) { } -
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
- right click ThemeParkApplication and select Run
- in a browser go to http://localhost:8080/rides and check for a successful JSON response with 3 rides
Application plugin
-
swap java plugin for application plugin
-
add this configuration to end of build.gradle
application { mainClass = 'com.gradlehero.themepark.ThemeParkApplication' } -
stop any running applications
-
run
./gradlew run -
verify application is still running at http://localhost:8080/rides
Apply Spring Boot Gradle plugin
-
revert application plugin back to java
-
remove application plugin configuration
-
apply the following plugin, using the same version as your spring-boot-starter-web dependency version
id 'org.springframework.boot' version '2.5.3' -
run
./gradlew tasksto see new bootRun and bootJar tasks. -
run
./gradlew bootRunto start the application.
Using executable jar file
- run
./gradlew assembleand look for theme-park-api.jar in build/libs - run the jar file with
java -jar build/libs/theme-park-api.jar(ensure you're using Java 16+) - verify http://localhost:8080/rides still works properly
Applying Spring Dependency Management plugin
-
apply the following plugin
id 'io.spring.dependency-management' version '1.0.11.RELEASE' -
remove the version from the spring-boot-starter-web dependency. e.g.
implementation 'org.springframework.boot:spring-boot-starter-web'Looks nicer!
-
run again with
./gradlew bootRun -
verify http://localhost:8080/rides still works properly
Commit the changes
- Run
git init - Add .idea to .gitignore if using IntelliJ IDEA
- Run
git add . - Run
git status - 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.
-
update the Spring Boot application class to extend
SpringBootServletInitializerpackage 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); } } -
in the build script replace java plugin with war plugin
plugins { id 'war' // other plugins } -
Run
./gradlew assembleand 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.
-
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/ -
build the Docker image with
docker build --tag theme-park-api:latest . -
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
-
create directory theme-park-manager
-
cdinto theme-park-manager -
run
gradle initand create basic Groovy project -
copy project from previous building Spring Boot applications practical e.g.
cp -R ../theme-park-api/ . -
copy project from previous building a Java project practical e.g.
cp -R ../theme-park-rides-status/ . -
in each subproject directory run:
rm -rf .git .gradle/ .idea build gradle .gitattributes .gitignore gradlew gradlew.bat settings.gradle -
rename theme-park-api to api
-
rename theme-park-rides-status to service
-
open theme-park-manager in IDE
-
in settings.gradle add at bottom
include 'api', 'service' -
in service/build.gradle change
javaplugin tojava-libraryplugin
Verify project
- run
./gradlew build --console=verbose - run
./gradlew bootRun - verify you can access http://localhost:8080/rides
Return ride status and image path
-
in build.gradle of api subproject add
implementation project(':service')to dependencies -
edit
ThemeParkRideto includeString status, String imagePathpackage com.gradlehero.themepark; public record ThemeParkRide(String name, String description, String status, String imagePath) { } -
edit
ThemeParkRideControllerto callRideStatusServiceand include an image pathpackage 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(); } } -
restart application and verify API response includes status and imagePath
Setup ui project
- download ui.zip containing ui project
- unzip ui.zip in theme-park-manager
- delete ui.zip
- add ui to list of includes in settings.gradle
- in build.gradle of api subproject add
implementation project(':ui')to dependencies - restart the application
- access http://localhost:8080
- hit the Get rides! button, see the dynamic ride data, and view the images!
Commit the project
- run
git init - if using IntelliJ IDEA add .idea to .gitignore file
- run
git add . - run
git status - 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
fromof theCopyandZiptasks (docs) - passing files to
intoof theCopyandZiptasks (docs)
Creating custom tasks
Documentation featured in this lesson
- 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
Documentation featured in this lesson
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.