Back to Portfolio
Java / Build Automation

Java Build Mastery: Maven & Gradle

The ultimate guide to Java build tools. Master dependency management, build lifecycles, plugins, multi-module projects, and advanced configurations.

By Ramjee PrasadDecember 10, 202525 min read

1. Maven vs Gradle Comparison

FeatureMavenGradle
ConfigurationXML (pom.xml)Groovy/Kotlin DSL
Build SpeedSlower (no caching)Faster (incremental builds + cache)
Learning CurveEasier (convention-based)Steeper (more flexible)
CustomizationPlugin-based onlyFull scripting capability
IDE SupportExcellentExcellent
AndroidLimited supportOfficial build tool
DaemonNo daemonBackground daemon
RepositoryCentral, JCenterCentral, JCenter, custom

2. Maven Fundamentals

Maven uses a Project Object Model (POM) defined in pom.xml. It follows "Convention over Configuration" principle.

Standard Directory Structure

Maven Project Structure
my-project/
├── pom.xml                    # Project configuration
├── src/
│   ├── main/
│   │   ├── java/             # Application source code
│   │   │   └── com/example/
│   │   │       └── App.java
│   │   └── resources/        # Configuration files
│   │       └── application.properties
│   └── test/
│       ├── java/             # Test source code
│       │   └── com/example/
│       │       └── AppTest.java
│       └── resources/        # Test resources
└── target/                   # Build output (generated)

Complete pom.xml Example

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>
    
    <!-- Project Coordinates -->
    <groupId>com.example</groupId>
    <artifactId>my-application</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <name>My Application</name>
    <description>A sample Maven project</description>
    
    <!-- Properties (Variables) -->
    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.boot.version>3.2.0</spring.boot.version>
    </properties>
    
    <!-- Dependencies -->
    <dependencies>
        <!-- Spring Boot Starter Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
        
        <!-- Lombok for boilerplate reduction -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
        
        <!-- Testing Dependencies -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>${spring.boot.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <!-- Build Configuration -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.version}</version>
            </plugin>
        </plugins>
    </build>
    
</project>

3. Maven Essential Commands

Create New Project

# Quick Start (Interactive)
mvn archetype:generate

# Non-Interactive with specific archetype
mvn archetype:generate \
  -DgroupId=com.example \
  -DartifactId=my-app \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DarchetypeVersion=1.4 \
  -DinteractiveMode=false

Creates a new Maven project from an archetype template.

Clean Project

mvn clean

Removes the target/ directory containing all build outputs.

Compile Source Code

mvn compile

Compiles source code in src/main/java to target/classes.

Run Tests

# Run all tests
mvn test

# Run specific test class
mvn test -Dtest=UserServiceTest

# Run specific test method
mvn test -Dtest=UserServiceTest#testCreateUser

# Run tests with pattern
mvn test -Dtest=*ServiceTest

Executes unit tests using the configured test framework.

Package Application

# Create JAR/WAR
mvn package

# Skip tests during packaging
mvn package -DskipTests

# Also skip test compilation
mvn package -Dmaven.test.skip=true

Packages compiled code into distributable format (JAR/WAR) in target/.

Install to Local Repository

mvn install

Installs the package to ~/.m2/repository for use by other local projects.

Deploy to Remote Repository

mvn deploy

Deploys the package to a remote repository (requires distributionManagement config).

View Dependency Tree

# Full dependency tree
mvn dependency:tree

# With specific scope
mvn dependency:tree -Dscope=compile

# Output to file
mvn dependency:tree -DoutputFile=deps.txt

Displays the project's dependency tree showing all transitive dependencies.

Check for Dependency Updates

# Show available updates
mvn versions:display-dependency-updates

# Show plugin updates
mvn versions:display-plugin-updates

# Update dependencies to latest versions
mvn versions:use-latest-versions

Checks Maven Central for newer versions of dependencies.

Download Dependencies

# Download all dependencies
mvn dependency:resolve

# Download sources
mvn dependency:sources

# Download javadocs
mvn dependency:resolve -Dclassifier=javadoc

Downloads all dependencies to local repository for offline access.

Generate Project Site

mvn site

Generates project documentation site with reports in target/site/.

4. Maven Build Lifecycle

Maven has three built-in lifecycles: default (build), clean, and site.

Default Lifecycle Phases

validate
Validate project
compile
Compile source code
test
Run unit tests
package
Create JAR/WAR
verify
Run integrations
install
Install to local
deploy
Deploy to remote

💡 Important: Each phase executes all previous phases. Running mvn install will execute: validate → compile → test → package → verify → install.

5. Maven Dependency Scopes

compile (default)

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>6.1.0</version>
    <!-- scope is 'compile' by default -->
</dependency>

Available in all classpaths. Transitive dependencies included.

provided

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.0.0</version>
    <scope>provided</scope>
</dependency>

Available at compile time but expected to be provided at runtime (e.g., by servlet container).

runtime

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.2.0</version>
    <scope>runtime</scope>
</dependency>

Not needed for compilation but required at runtime (e.g., JDBC drivers).

test

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>

Only available during test compilation and execution.

6. Maven Plugins

Common Maven Plugins Configuration
<build>
    <plugins>
        <!-- Compiler Plugin - Set Java Version -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>17</source>
                <target>17</target>
            </configuration>
        </plugin>
        
        <!-- Surefire Plugin - Unit Tests -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.2.2</version>
            <configuration>
                <includes>
                    <include>**/*Test.java</include>
                </includes>
            </configuration>
        </plugin>
        
        <!-- Failsafe Plugin - Integration Tests -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>3.2.2</version>
            <executions>
                <execution>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        
        <!-- JAR Plugin - Customize JAR -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.example.App</mainClass>
                        <addClasspath>true</addClasspath>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
        
        <!-- Shade Plugin - Create Fat JAR -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.5.1</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        
        <!-- Spring Boot Plugin -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>3.2.0</version>
        </plugin>
    </plugins>
</build>

7. Maven Profiles

Profiles allow you to customize builds for different environments (dev, test, prod).

pom.xml - Profile Configuration
<profiles>
    <!-- Development Profile (Active by default) -->
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <env>development</env>
            <db.url>jdbc:h2:mem:devdb</db.url>
        </properties>
    </profile>
    
    <!-- Production Profile -->
    <profile>
        <id>prod</id>
        <properties>
            <env>production</env>
            <db.url>jdbc:postgresql://prod-server:5432/db</db.url>
        </properties>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <optimize>true</optimize>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
    
    <!-- Integration Tests Profile -->
    <profile>
        <id>integration</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-failsafe-plugin</artifactId>
                    <executions>
                        <execution>
                            <goals>
                                <goal>integration-test</goal>
                                <goal>verify</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

Activate Profile

# Activate single profile
mvn package -Pprod

# Activate multiple profiles
mvn package -Pdev,integration

# List active profiles
mvn help:active-profiles

Use profiles to customize builds for different environments.

8. Gradle Fundamentals

Gradle uses build.gradle (Groovy) or build.gradle.kts (Kotlin DSL). It offers flexibility, speed, and powerful scripting.

Standard Directory Structure

Gradle Project Structure
my-project/
├── build.gradle(.kts)        # Build script
├── settings.gradle(.kts)     # Settings (multi-project)
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew                   # Unix wrapper script
├── gradlew.bat               # Windows wrapper script
├── src/
│   ├── main/
│   │   ├── java/
│   │   └── resources/
│   └── test/
│       ├── java/
│       └── resources/
└── build/                    # Build output (generated)

Groovy DSL (build.gradle)

build.gradle
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.0'
    id 'io.spring.dependency-management' version '1.1.4'
}

group = 'com.example'
version = '1.0.0-SNAPSHOT'

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
    // Maven Local
    mavenLocal()
    // Custom repository
    maven { url 'https://repo.example.com/maven2' }
}

dependencies {
    // Implementation dependencies
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    
    // Compile-only (like Maven's provided)
    compileOnly 'org.projectlombok:lombok:1.18.30'
    annotationProcessor 'org.projectlombok:lombok:1.18.30'
    
    // Runtime only
    runtimeOnly 'com.mysql:mysql-connector-j:8.2.0'
    
    // Test dependencies
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}

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

// Custom configuration
tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

Kotlin DSL (build.gradle.kts)

build.gradle.kts
plugins {
    java
    id("org.springframework.boot") version "3.2.0"
    id("io.spring.dependency-management") version "1.1.4"
}

group = "com.example"
version = "1.0.0-SNAPSHOT"

java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    
    compileOnly("org.projectlombok:lombok:1.18.30")
    annotationProcessor("org.projectlombok:lombok:1.18.30")
    
    runtimeOnly("com.mysql:mysql-connector-j:8.2.0")
    
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.test {
    useJUnitPlatform()
}

9. Gradle Essential Commands

💡 Best Practice: Always use the Gradle Wrapper (./gradlew or gradlew.bat) instead of system-installed Gradle for consistent builds.

Initialize New Project

# Interactive initialization
gradle init

# Java application
gradle init --type java-application

# Java library
gradle init --type java-library

# With Kotlin DSL
gradle init --type java-application --dsl kotlin

Creates a new Gradle project with the specified type.

Generate Gradle Wrapper

# Generate wrapper files
gradle wrapper

# With specific version
gradle wrapper --gradle-version 8.5

# Verify wrapper
./gradlew --version

Creates wrapper scripts for consistent Gradle version across machines.

Clean Build

# Clean build directory
./gradlew clean

# Clean and build
./gradlew clean build

# Force clean
./gradlew clean --no-build-cache

Removes the build/ directory containing all build outputs.

Build Project

# Build (compile + test + jar)
./gradlew build

# Build without tests
./gradlew build -x test

# Build with stacktrace
./gradlew build --stacktrace

# Build with debug info
./gradlew build --debug

Compiles, tests, and creates the project artifacts.

Run Tests

# Run all tests
./gradlew test

# Run specific test class
./gradlew test --tests "UserServiceTest"

# Run tests matching pattern
./gradlew test --tests "*ServiceTest"

# Run single test method
./gradlew test --tests "UserServiceTest.testCreate"

# Rerun tests (ignore cache)
./gradlew test --rerun-tasks

Executes tests with JUnit Platform support.

Run Spring Boot Application

# Run application
./gradlew bootRun

# With arguments
./gradlew bootRun --args='--server.port=9090'

# With JVM args
./gradlew bootRun -Dspring.profiles.active=dev

Starts the Spring Boot application with the embedded server.

View Dependencies

# All dependencies
./gradlew dependencies

# Specific configuration
./gradlew dependencies --configuration implementation

# Dependency insight for specific dependency
./gradlew dependencyInsight --dependency spring-core

Displays the dependency tree for the project.

List All Tasks

# List available tasks
./gradlew tasks

# All tasks including hidden
./gradlew tasks --all

# Tasks for specific group
./gradlew tasks --group "build"

Shows all available tasks with their descriptions.

Refresh Dependencies

# Force refresh dependencies
./gradlew build --refresh-dependencies

# Clear Gradle cache
rm -rf ~/.gradle/caches

Forces Gradle to re-download all dependencies.

Gradle Daemon

# Check daemon status
./gradlew --status

# Stop all daemons
./gradlew --stop

# Run without daemon
./gradlew build --no-daemon

Manage the Gradle daemon for faster subsequent builds.

10. Gradle Tasks & Custom Tasks

build.gradle - Custom Tasks
// Simple task
tasks.register('hello') {
    group = 'Custom'
    description = 'Prints a greeting'
    doLast {
        println 'Hello from Gradle!'
    }
}

// Task with dependencies
tasks.register('printVersion') {
    dependsOn 'build'
    doLast {
        println "Version: ${project.version}"
    }
}

// Copy task
tasks.register('copyResources', Copy) {
    from 'src/main/resources'
    into 'build/config'
    include '**/*.properties'
}

// Delete task
tasks.register('cleanLogs', Delete) {
    delete fileTree('logs') {
        include '**/*.log'
    }
}

// Task with inputs/outputs (incremental)
tasks.register('processData') {
    inputs.file 'data/input.json'
    outputs.file 'build/output.json'
    doLast {
        // Processing logic
    }
}

// Task ordering
tasks.register('deploy') {
    dependsOn 'build'
    mustRunAfter 'test'
    finalizedBy 'cleanup'
    doLast {
        println 'Deploying application...'
    }
}

// Exec task - run external command
tasks.register('runScript', Exec) {
    workingDir 'scripts'
    commandLine 'bash', 'deploy.sh'
}

// Run with: ./gradlew hello

Kotlin DSL Custom Tasks

build.gradle.kts - Custom Tasks
tasks.register("hello") {
    group = "Custom"
    description = "Prints a greeting"
    doLast {
        println("Hello from Gradle with Kotlin DSL!")
    }
}

tasks.register<Copy>("copyResources") {
    from("src/main/resources")
    into("build/config")
    include("**/*.properties")
}

tasks.register<Delete>("cleanLogs") {
    delete(fileTree("logs").matching {
        include("**/*.log")
    })
}

11. Gradle Dependencies

implementation

dependencies {
    implementation 'org.springframework:spring-core:6.1.0'
}

Compile-time and runtime dependency. NOT exposed to consumers (API doesn't leak).

api

dependencies {
    api 'org.springframework:spring-core:6.1.0'
}

Compile-time and runtime. EXPOSED to consumers. Use for library modules.

compileOnly

dependencies {
    compileOnly 'org.projectlombok:lombok:1.18.30'
    annotationProcessor 'org.projectlombok:lombok:1.18.30'
}

Only for compilation, not included in runtime (like Maven's 'provided').

runtimeOnly

dependencies {
    runtimeOnly 'com.mysql:mysql-connector-j:8.2.0'
}

Only needed at runtime, not for compilation (e.g., JDBC drivers).

testImplementation

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
    testImplementation 'org.mockito:mockito-core:5.8.0'
}

Only for test compilation and execution.

12. Gradle Multi-Project Builds

Project Structure
my-project/
├── build.gradle
├── settings.gradle
├── app/
│   └── build.gradle
├── core/
│   └── build.gradle
└── web/
    └── build.gradle
settings.gradle
rootProject.name = 'my-project'

include 'app'
include 'core'
include 'web'
Root build.gradle
// Common configuration for all subprojects
subprojects {
    apply plugin: 'java'
    
    group = 'com.example'
    version = '1.0.0'
    
    java {
        sourceCompatibility = JavaVersion.VERSION_17
    }
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
    }
    
    tasks.named('test') {
        useJUnitPlatform()
    }
}

// Configure specific project
project(':web') {
    dependencies {
        implementation project(':core')
    }
}
Subproject build.gradle (app/build.gradle)
plugins {
    id 'application'
}

dependencies {
    implementation project(':core')
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'
}

application {
    mainClass = 'com.example.App'
}

13. Troubleshooting Guide

Maven Troubleshooting

Force Update Snapshots

mvn clean install -U

Forces Maven to check for updated snapshots on remote repositories.

Debug Mode

mvn clean install -X

Enables debug output for troubleshooting build issues.

Offline Mode

mvn clean install -o

Runs Maven in offline mode using only cached dependencies.

Clear Local Repository

# Remove specific artifact
rm -rf ~/.m2/repository/com/example/my-artifact

# Clear entire cache (use with caution)
rm -rf ~/.m2/repository

Clears cached dependencies when you have corruption issues.

Analyze Dependencies

# Find unused/undeclared dependencies
mvn dependency:analyze

# Resolve conflicts
mvn dependency:tree -Dverbose -Dincludes=groupId:artifactId

Helps identify dependency issues and conflicts.

Gradle Troubleshooting

Build Scan

./gradlew build --scan

Generates a detailed build report to diagnose issues.

Stacktrace and Info

./gradlew build --stacktrace --info

Shows full stacktrace and additional info for debugging.

Clean Cache

# Stop daemon first
./gradlew --stop

# Clear project build cache
rm -rf .gradle build

# Clear global cache
rm -rf ~/.gradle/caches

Clears Gradle caches when experiencing strange build behaviors.

Check Build Configuration

# Show all properties
./gradlew properties

# Show specific project info
./gradlew projects

# Configuration report
./gradlew buildEnvironment

Displays build configuration for debugging.

14. Best Practices

Maven Best Practices

  • Use Maven Wrapper (mvnw) for consistency
  • Define versions in <properties> section
  • Use dependencyManagement for multi-module
  • Keep pom.xml organized and well-commented
  • Use profiles for different environments
  • Pin plugin versions explicitly
  • Use BOM for dependency version management

Gradle Best Practices

  • Always use Gradle Wrapper (gradlew)
  • Prefer Kotlin DSL for type safety
  • Enable build cache for faster builds
  • Use implementation over compile (deprecated)
  • Configure parallel execution
  • Use version catalogs for dependency management
  • Leverage the Gradle daemon

💡 Which Tool Should You Choose?

Choose Maven When:
  • • Your team is familiar with XML
  • • You want convention over configuration
  • • Using enterprise Java with stable setup
  • • Project structure is simple and standard
Choose Gradle When:
  • • Building Android applications
  • • Need complex multi-module projects
  • • Build performance is critical
  • • Need custom build logic flexibility

Performance Optimization Tips

Maven
# Enable parallel builds
mvn -T 4 clean install

# Skip tests for faster builds
mvn package -DskipTests

# Use specific reactor options
mvn install -pl module-name -am
Gradle
# gradle.properties
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx2g

Written by Ramjee Prasad • Backend Developer

← Back to Portfolio