Skip to content

Java Build Tools — Maven & Gradle Guide

DodaTech Updated 2026-06-20 9 min read

In this tutorial, you'll learn about Java Build Tools. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Java build tools automate compiling code, managing dependencies, running tests, and packaging — replacing manual javac commands with reproducible build scripts.

Why Build Tools Matter

Imagine compiling a project with 200 dependencies, each with its own version. Doing that by hand is impossible. Build tools resolve transitive dependencies, run tests in the right order, package JARs and WARs, and integrate with CI/CD pipelines. DodaTech's Doda Browser uses Gradle with 150+ modules, producing distinct artifacts for desktop, mobile, and server targets. Every build is reproducible, auditable, and automated. Related concepts include Java testing for build-integrated test suites and Java I/O for resource handling during builds.

Learning Path

graph LR
    A[Java Basics] --> B[Maven Fundamentals]
    B --> C[Build Lifecycle & Plugins]
    C --> D[Dependency Management]
    D --> E[Multi-Module Projects]
    E --> F[Gradle & Groovy/Kotlin DSL]
    F --> G[CI/CD Integration]
    style B fill:#f59e0b,color:#fff,stroke-width:3px

Maven: Convention Over Configuration

Maven uses an XML file (pom.xml) and follows a strict convention: source code goes in src/main/java, tests in src/test/java, resources in src/main/resources. This convention means any developer can open any Maven project and know exactly where to find things.

Project Object Model (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.dodatech</groupId>
    <artifactId>file-scanner</artifactId>
    <version>2.1.0</version>
    <packaging>jar</packaging>
    
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.11.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.4.1</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.dodatech.scanner.Main</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Every POM has a groupId (your organization), artifactId (project name), and version. Dependencies declare their coordinates plus an optional scopecompile (default, available everywhere), test (only during testing), provided (supplied by the runtime environment). Maven downloads dependencies from Maven Central, the largest Java package registry.

Maven Build Lifecycle

Maven's build lifecycle has three built-in phases sequences: default (build and deploy), clean (delete build artifacts), and site (generate documentation). The default lifecycle includes:

# Validate: check project structure
mvn validate

# Compile: compile source code
mvn compile

# Test: run tests with surefire plugin
mvn test

# Package: create JAR/WAR
mvn package

# Verify: run integration tests
mvn verify

# Install: copy artifact to local repository
mvn install

# Deploy: copy artifact to remote repository
mvn deploy

Each phase executes all previous phases. mvn test also runs validate and compile automatically. This is called phase ordering — you never skip intermediary steps.

Expected output from mvn clean package:

[INFO] Scanning for projects...
[INFO] Building file-scanner 2.1.0
[INFO] --- maven-clean-plugin:3.2.0:clean (default-clean) ---
[INFO] --- maven-resources-plugin:3.3.1:resources (default-resources) ---
[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) ---
[INFO] --- maven-resources-plugin:3.3.1:testResources (default-testResources) ---
[INFO] --- maven-compiler-plugin:3.11.0:testCompile (default-testCompile) ---
[INFO] --- maven-surefire-plugin:3.2.5:test (default-test) ---
[INFO] --- maven-jar-plugin:3.4.1:jar (default-jar) ---
[INFO] BUILD SUCCESS

Gradle: Flexibility and Performance

Gradle uses a Groovy or Kotlin DSL instead of XML. Build scripts are shorter, and Gradle's incremental build tracks which files changed since the last build — unchanged files aren't recompiled.

Gradle Build Script

plugins {
    id 'java'
    id 'application'
}

group = 'com.dodatech'
version = '2.1.0'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.google.code.gson:gson:2.11.0'
    testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'
}

application {
    mainClass = 'com.dodatech.scanner.Main'
}

tasks.named('test') {
    useJUnitPlatform()
    testLogging {
        events 'passed', 'failed', 'skipped'
    }
}

Gradle's DSL reads like code. implementation replaces Maven's compile scope. testImplementation replaces test scope. The java plugin adds compilation, testing, and JAR tasks. Gradle doesn't require a fixed directory structure — you can configure source sets.

# Build the project
gradle build

# Run tests with detailed output
gradle test --info

# Run the application
gradle run

# Clean build artifacts
gradle clean

Expected output from gradle build:

> Task :compileJava
> Task :processResources
> Task :classes
> Task :jar
> Task :assemble
> Task :compileTestJava
> Task :processTestResources
> Task :testClasses
> Task :test
> Task :check
> Task :build

BUILD SUCCESSFUL in 3s
3 actionable tasks: 3 executed

Maven vs Gradle at a Glance

Aspect Maven Gradle
Build file XML (pom.xml) Groovy/Kotlin DSL
Performance Good (no cache by default) Faster (incremental builds, build cache)
Learning curve Moderate (XML is verbose but simple) Steeper (DSL is powerful but complex)
Convention Strict (opinionated directory layout) Flexible (configurable source sets)
Dependency resolution Centralized (Maven Central) Centralized + dynamic versions
Multi-module Parent POM with modules Root project with subprojects
Plugin ecosystem Mature (thousands of plugins) Growing (Gradle Plugin Portal)
CI integration Excellent (supported everywhere) Excellent (Gradle Build Scans)

Multi-Module Maven Project

Real projects split into modules — a shared library, a REST API, a CLI client. Maven handles this with a parent POM.

<!-- Parent pom.xml -->
<project>
    <groupId>com.dodatech</groupId>
    <artifactId>platform-parent</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    
    <modules>
        <module>shared-lib</module>
        <module>rest-api</module>
        <module>cli-client</module>
    </modules>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
                <version>2.11.0</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

<dependencyManagement> defines versions in one place. Child modules omit the version:

<!-- cli-client/pom.xml -->
<project>
    <parent>
        <groupId>com.dodatech</groupId>
        <artifactId>platform-parent</artifactId>
        <version>1.0.0</version>
    </parent>
    
    <artifactId>cli-client</artifactId>
    
    <dependencies>
        <dependency>
            <groupId>com.dodatech</groupId>
            <artifactId>shared-lib</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <!-- version inherited from parent -->
        </dependency>
    </dependencies>
</project>

Expected output from mvn install in the parent directory:

[INFO] Reactor Build Order:
[INFO]   platform-parent (pom) [1.0.0]
[INFO]   shared-lib (jar)      [1.0.0]
[INFO]   rest-api (jar)        [1.0.0]
[INFO]   cli-client (jar)      [1.0.0]
[INFO] BUILD SUCCESS

Maven builds modules in dependency order. shared-lib builds first because cli-client depends on it. This reactor build is why Maven is the standard for enterprise monorepos.

Common Errors in Build Tools

Error Cause Fix
Could not find artifact Dependency doesn't exist or Repository is missing Add correct Repository (Maven Central, JitPack) and check GAV coordinates
Failed to execute goal compile Java version mismatch between source and compiler Set maven.compiler.source and maven.compiler.target correctly
Cannot resolve symbol in IDE IDE uses wrong build tool or module graph Re-import project: IntelliJ → Reimport, Eclipse → Update Project
Duplicate classes Two dependencies contain the same class Use mvn dependency:tree to find the conflict and exclude one
Task with path 'run' not found in Gradle Missing application plugin Add id 'application' and configure mainClass
Could not determine Java version JDK not set or Gradle wrapper wrong Run java --version to check JDK, update gradle-wrapper.properties
Build failed with an exception (Gradle daemon) Gradle daemon is corrupted Kill daemon: gradle --stop, then rebuild

Security Angle: Dependency Vulnerability Scanning

Build tools are a security vector. A compromised dependency injects malware into your application. DodaTech's CI pipeline includes:

<!-- Maven OWASP Dependency Check plugin -->
<plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>10.0.0</version>
    <configuration>
        <failBuildOnCVSS>7</failBuildOnCVSS>
        <formats>
            <format>HTML</format>
            <format>JSON</format>
        </formats>
    </configuration>
</plugin>

Run it with:

mvn dependency-check:check

This plugin queries the National Vulnerability Database (NVD) and fails the build if any dependency has a known vulnerability with CVSS score above 7. DodaTech also runs gradle dependencies --scan to generate build scans that visualize the entire dependency tree, revealing hidden transitive dependencies that might carry vulnerabilities.

Practice Questions

  1. What is the difference between Maven's compile and test dependency scopes?
  2. How does Gradle's incremental build improve performance?
  3. What is the purpose of dependencyManagement in a parent POM?
  4. Why does mvn test also run compile?
  5. How does the OWASP Dependency Check plugin improve security?

Answers:

  1. compile scope makes dependencies available during compilation and at runtime. test scope restricts them to test compilation and execution only — they're excluded from the final JAR/WAR.
  2. Gradle tracks inputs and outputs of each task. If a source file hasn't changed since the last build, Gradle skips its compilation. This makes subsequent builds 10-100x faster than Maven's full rebuild.
  3. dependencyManagement centralizes dependency versions for all child modules. Child modules declare dependencies without versions, reducing duplication and ensuring every module uses the same version.
  4. Maven phases are ordered. test comes after compile in the default lifecycle. Running test triggers all previous phases first, ensuring code is compiled before tests attempt to run.
  5. It queries the NVD to find known CVEs in dependencies. With failBuildOnCVSS, it breaks the build when a critical vulnerability is found, preventing vulnerable code from reaching production.

Challenge

Create a multi-module Maven project with:

  1. A shared-lib module containing a StringUtils utility class
  2. A rest-api module that depends on shared-lib and uses JAX-RS
  3. A cli-client module that depends on shared-lib and uses Picocli
  4. A parent POM with dependencyManagement for JUnit 5, Gson, and JAX-RS
  5. The OWASP Dependency Check plugin configured in the parent

Real-World Task: CI/CD Build Pipeline

Set up a GitHub Actions workflow for a multi-module Maven project that:

  1. Caches the Maven local Repository for faster builds
  2. Runs mvn verify with OWASP dependency check
  3. Publishes test reports as artifacts
  4. Deploys the JAR to GitHub Packages on tags

DodaTech uses this exact pipeline across all its Java projects, processing over 200 builds per week with zero regressions.

Should I use Maven or Gradle for my project?

Use Maven if you want a stable, convention-based build that's easy to onboard new developers to. Use Gradle if you need fast incremental builds, custom build logic, or a multi-module project with complex dependencies. Both are production-proven — DodaTech uses Gradle for Doda Browser (client) and Maven for backend services.

What is the Maven local repository and how does it work?

The local Repository (~/.m2/Repository) is a cache on your machine where Maven stores downloaded dependencies. When you declare a dependency, Maven first checks the local repo. If missing, it downloads from remote repos (Maven Central). mvn install copies your built artifact to the local repo, making it available to other local projects.

How do I manage transitive dependency conflicts?

Use mvn dependency:tree to see the full dependency tree. If two versions conflict, Maven uses the "nearest definition" strategy. To force a specific version, declare it explicitly in your POM. Or use <exclusions> in a dependency to remove a transitive version. Gradle's equivalent is gradle dependencies and the force = true flag.

Related tutorials: Java I/O — File Handling & NIO Guide, Java Testing — JUnit 5 Complete Guide

Next lesson: Java Module System (JPMS) — Complete Guide

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro