Web Analytics

Build Tools: Maven and Gradle

Advanced ~20 min read

Build tools automate compilation, testing, packaging, and dependency management. Maven and Gradle are the two dominant build tools in the Java ecosystem.

Maven vs Gradle

Feature Maven Gradle
Config Format XML (pom.xml) Groovy/Kotlin (build.gradle)
Build Speed Slower Faster (incremental builds)
Learning Curve Easier (convention) Steeper (more flexible)
Flexibility Convention over config Highly customizable
IDE Support Excellent Excellent

Maven

Maven uses pom.xml (Project Object Model) for configuration.

Project Structure

my-project/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/          # Application source code
│   │   └── resources/     # Config files, properties
│   └── test/
│       ├── java/          # Test source code
│       └── resources/     # Test resources
└── target/                # Compiled output (generated)

Basic 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-app</artifactId>
    <version>1.0.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>
        <!-- JUnit 5 for testing -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.0</version>
            <scope>test</scope>
        </dependency>

        <!-- SLF4J for logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.9</version>
        </dependency>
    </dependencies>
</project>

Common Maven Commands

Command Description
mvn clean Delete target directory
mvn compile Compile source code
mvn test Run unit tests
mvn package Create JAR/WAR file
mvn install Install to local repository
mvn clean install Clean, compile, test, and install
mvn dependency:tree Show dependency hierarchy

Dependency Scopes

<!-- compile (default): Available everywhere -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.2-jre</version>
</dependency>

<!-- provided: Available at compile, not packaged (e.g., servlet API) -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

<!-- test: Only for testing -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.0</version>
    <scope>test</scope>
</dependency>

<!-- runtime: Not needed for compilation, only at runtime -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
    <scope>runtime</scope>
</dependency>

Gradle

Gradle uses Groovy or Kotlin DSL for configuration.

Basic build.gradle

plugins {
    id 'java'
    id 'application'
}

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

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

repositories {
    mavenCentral()
}

dependencies {
    // Implementation dependencies
    implementation 'com.google.guava:guava:32.1.2-jre'
    implementation 'org.slf4j:slf4j-api:2.0.9'

    // Test dependencies
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

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

test {
    useJUnitPlatform()
}

Common Gradle Commands

Command Description
./gradlew build Compile, test, and package
./gradlew clean Delete build directory
./gradlew test Run unit tests
./gradlew run Run the application
./gradlew dependencies Show dependency tree
./gradlew tasks List available tasks
Gradle Wrapper: Always use ./gradlew (or gradlew.bat on Windows) instead of gradle. The wrapper ensures everyone uses the same Gradle version.

Creating Executable JARs

Maven (with shade plugin)

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.5.0</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals><goal>shade</goal></goals>
                    <configuration>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.example.Main</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Gradle (fat JAR)

jar {
    manifest {
        attributes 'Main-Class': 'com.example.Main'
    }
    // Include all dependencies in the JAR
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

Multi-Module Projects

Maven Parent POM

<!-- parent/pom.xml -->
<project>
    <groupId>com.example</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>core</module>
        <module>web</module>
        <module>api</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <!-- Version managed here, used in children -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>3.1.5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

Summary

  • Maven: Convention-based, XML config, great for standard projects
  • Gradle: Flexible, faster builds, Groovy/Kotlin DSL
  • Use dependency scopes to control classpath
  • Use the Gradle Wrapper for consistent builds
  • Shade/Fat JARs bundle dependencies for deployment
  • Multi-module projects share configuration via parent