Build Tools - Công cụ Build trong Java

Tổng quan về Build Tools

Build tools là những công cụ tự động hóa quá trình biên dịch, đóng gói, và quản lý dependencies trong dự án Java. Chúng đóng vai trò quan trọng trong việc standardize quy trình phát triển và deployment.

Maven

Khái niệm cơ bản

Maven là một project management tool sử dụng Project Object Model (POM) để quản lý build lifecycle, dependencies, và documentation.

Cấu trúc Project Maven

my-app/
├── pom.xml
└── src/
    ├── main/
    │   ├── java/
    │   └── resources/
    └── test/
        ├── java/
        └── resources/

File pom.xml cơ bản

<?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 Information -->
    <groupId>com.example</groupId>
    <artifactId>my-application</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <name>My Application</name>
    <description>Example Spring Boot Application</description>

    <!-- Properties -->
    <properties>
        <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>
        <junit.version>5.10.0</junit.version>
    </properties>

    <!-- Dependencies -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>

        <!-- Test Dependencies -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <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>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <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>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <includes>
                        <include>**/*Test.java</include>
                        <include>**/*Tests.java</include>
                    </includes>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <!-- Profiles -->
    <profiles>
        <profile>
            <id>dev</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <spring.profiles.active>dev</spring.profiles.active>
            </properties>
        </profile>

        <profile>
            <id>prod</id>
            <properties>
                <spring.profiles.active>prod</spring.profiles.active>
            </properties>
        </profile>
    </profiles>
</project>

Maven Lifecycle và Commands

# Compile source code
mvn compile

# Run tests
mvn test

# Package project (tạo JAR/WAR)
mvn package

# Install vào local repository
mvn install

# Deploy lên remote repository
mvn deploy

# Clean project
mvn clean

# Full build cycle
mvn clean install

# Skip tests
mvn clean install -DskipTests

# Run specific profile
mvn clean install -P prod

# Run application
mvn spring-boot:run

Multi-module Maven Project

<!-- Parent POM -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>multi-module-app</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>common</module>
        <module>service</module>
        <module>web</module>
    </modules>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <spring.boot.version>3.2.0</spring.boot.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

Gradle

Khái niệm cơ bản

Gradle là một build automation tool sử dụng Groovy hoặc Kotlin DSL thay vì XML như Maven. Gradle nhanh hơn và linh hoạt hơn Maven.

Cấu trúc Project Gradle

my-app/
├── build.gradle
├── settings.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src/
    ├── main/
    │   ├── java/
    │   └── resources/
    └── test/
        ├── java/
        └── resources/

File build.gradle (Groovy)

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'
sourceCompatibility = '17'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    // Spring Boot Starters
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-validation'

    // Database
    runtimeOnly 'mysql:mysql-connector-java'
    runtimeOnly 'com.h2database:h2'

    // Utils
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    // Test Dependencies
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.springframework.security:spring-security-test'
    testImplementation 'org.testcontainers:junit-jupiter'
    testImplementation 'org.testcontainers:mysql'
}

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

    // Test configuration
    testLogging {
        events "passed", "skipped", "failed"
        exceptionFormat "full"
    }

    // Memory settings
    minHeapSize = "256m"
    maxHeapSize = "2g"
}

// Custom tasks
task copyDependencies(type: Copy) {
    from configurations.runtimeClasspath
    into 'build/dependencies'
}

task generateDocumentation(type: Javadoc) {
    source = sourceSets.main.allJava
    classpath = configurations.compileClasspath
    destinationDir = file("$buildDir/docs/javadoc")
}

// Build configuration
jar {
    archiveBaseName = 'my-application'
    archiveVersion = '1.0.0'
    enabled = false
}

bootJar {
    archiveBaseName = 'my-application'
    archiveVersion = '1.0.0'

    // Include additional files
    from('src/main/resources') {
        include 'static/**'
        include 'templates/**'
    }
}

// Profiles equivalent
if (project.hasProperty('prod')) {
    bootRun {
        args = ['--spring.profiles.active=prod']
    }
} else {
    bootRun {
        args = ['--spring.profiles.active=dev']
    }
}

File build.gradle.kts (Kotlin DSL)

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"
java.sourceCompatibility = JavaVersion.VERSION_17

configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-security")

    runtimeOnly("mysql:mysql-connector-java")

    compileOnly("org.projectlombok:lombok")
    annotationProcessor("org.projectlombok:lombok")

    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.springframework.security:spring-security-test")
}

tasks.withType<Test> {
    useJUnitPlatform()
}

tasks.withType<JavaCompile> {
    options.encoding = "UTF-8"
}

Gradle Commands

# Build project
./gradlew build

# Run tests
./gradlew test

# Run application
./gradlew bootRun

# Clean project
./gradlew clean

# Generate JAR
./gradlew jar

# Generate executable JAR
./gradlew bootJar

# Check dependencies
./gradlew dependencies

# Run specific task
./gradlew copyDependencies

# Run with profile
./gradlew bootRun -Pprod

# Skip tests
./gradlew build -x test

# Continuous build
./gradlew build --continuous

# Build info
./gradlew projects
./gradlew tasks

Multi-project Gradle Setup

// settings.gradle.kts
rootProject.name = "multi-module-app"

include(
    "common",
    "service",
    "web"
)

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

allprojects {
    group = "com.example"
    version = "1.0.0"

    repositories {
        mavenCentral()
    }
}

subprojects {
    apply(plugin = "java")

    java.sourceCompatibility = JavaVersion.VERSION_17

    dependencies {
        testImplementation("org.junit.jupiter:junit-jupiter")
    }

    tasks.withType<Test> {
        useJUnitPlatform()
    }
}

So sánh Maven vs Gradle

Performance

  • Gradle: Nhanh hơn nhờ incremental builds, build cache, parallel execution
  • Maven: Chậm hơn nhưng ổn định

Flexibility

  • Gradle: Rất linh hoạt với Groovy/Kotlin DSL
  • Maven: Ít linh hoạt hơn với XML

Learning Curve

  • Maven: Dễ học hơn, documentation tốt
  • Gradle: Khó học hơn nhưng mạnh mẽ hơn

Ecosystem

  • Maven: Ecosystem lớn, nhiều plugin
  • Gradle: Ecosystem đang phát triển mạnh

Best Practices

Maven Best Practices

  1. Sử dụng Properties cho versions
<properties>
    <spring.version>5.3.23</spring.version>
    <junit.version>5.9.1</junit.version>
</properties>
  1. Dependency Management
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>${spring.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  1. Profile Management
<profiles>
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
</profiles>

Gradle Best Practices

  1. Sử dụng Gradle Wrapper
./gradlew build  # Thay vì gradle build
  1. Optimize Build Performance
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configureondemand=true
  1. Version Catalogs (Gradle 7+)
// gradle/libs.versions.toml
[versions]
spring = "6.0.0"
junit = "5.9.1"

[libraries]
spring-core = { module = "org.springframework:spring-core", version.ref = "spring" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }

[plugins]
spring-boot = { id = "org.springframework.boot", version = "3.2.0" }

Các vấn đề thường gặp

Maven Issues

  1. Dependency Conflicts
mvn dependency:tree
mvn dependency:analyze
  1. Plugin Version Issues
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.11.0</version>
</plugin>

Gradle Issues

  1. Build Cache Issues
./gradlew clean --build-cache
  1. Dependency Resolution
configurations.all {
    resolutionStrategy {
        force("com.google.guava:guava:30.1-jre")
    }
}

Cả Maven và Gradle đều là những công cụ mạnh mẽ cho việc build Java projects. Việc chọn lựa phụ thuộc vào team size, project complexity, và performance requirements của dự án.