Back to blog

Tìm Hiểu Sâu: Java Modules và Công Cụ Build

javamavengradlebuild-toolsmodules
Tìm Hiểu Sâu: Java Modules và Công Cụ Build

Xây dựng các ứng dụng Java chuyên nghiệp đòi hỏi nhiều hơn việc chỉ viết code—bạn cần các công cụ build mạnh mẽ để quản lý dependencies, biên dịch dự án, chạy tests, và đóng gói sản phẩm. Trong bài tìm hiểu sâu này, chúng ta sẽ khám phá Maven và Gradle, hai công cụ build chiếm ưu thế trong hệ sinh thái Java, và tìm hiểu về hệ thống module Java 9+.

Đây là Giai đoạn 3 trong lộ trình Java của chúng ta: làm chủ hệ sinh thái build hỗ trợ phát triển Java doanh nghiệp.

Tại Sao Công Cụ Build Quan Trọng

Các dự án Java hiện đại có yêu cầu phức tạp:

  • Quản lý Dependencies: Quản lý hàng chục hoặc hàng trăm thư viện
  • Tự động hóa Build: Biên dịch, testing và đóng gói nhất quán
  • Dự án Đa Module: Tổ chức codebase lớn
  • Tích hợp IDE: Trải nghiệm phát triển liền mạch
  • Tích hợp CI/CD: Build và deployment tự động

Nếu không có công cụ build, bạn sẽ phải tải JAR thủ công, quản lý classpaths, và viết các shell script phức tạp. Maven và Gradle giải quyết các vấn đề này một cách tinh tế.

Maven: Convention Over Configuration

Maven đã là tiêu chuẩn de facto cho Java builds từ năm 2004. Nó tuân theo triết lý nghiêm ngặt "convention over configuration" với cấu trúc dự án được chuẩn hóa.

Cấu Trúc Dự Án

Maven áp dụng một layout thư mục chuẩn:

my-app/
├── pom.xml                    # Project Object Model (cấu hình build)
├── src/
│   ├── main/
│   │   ├── java/              # Mã nguồn ứng dụng
│   │   └── resources/         # File cấu hình, properties
│   └── test/
│       ├── java/              # Mã nguồn test
│       └── resources/         # Tài nguyên test
└── target/                    # Output build (được tạo)

Cấu trúc này là phổ quát—mọi dự án Maven trông giống nhau, giúp dễ dàng điều hướng các codebase chưa quen.

Hiểu Về pom.xml

File pom.xml là trái tim của dự án Maven. Đây là một ví dụ toàn diệ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 Coordinates (Maven GAV) -->
    <groupId>com.chanhle</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <!-- Project Metadata -->
    <name>My Application</name>
    <description>A sample Java application</description>
 
    <!-- Properties (for version management) -->
    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        
        <!-- Dependency Versions -->
        <spring.boot.version>3.2.1</spring.boot.version>
        <junit.version>5.10.1</junit.version>
    </properties>
 
    <!-- Dependencies -->
    <dependencies>
        <!-- Spring Boot Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
 
        <!-- Lombok (compile-time only) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
 
        <!-- JUnit 5 (test scope) -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <!-- Build Configuration -->
    <build>
        <plugins>
            <!-- Maven Compiler Plugin -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>21</source>
                    <target>21</target>
                </configuration>
            </plugin>
 
            <!-- Maven Surefire Plugin (for tests) -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.2.3</version>
            </plugin>
 
            <!-- Spring Boot Maven Plugin -->
            <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>
        </plugins>
    </build>
</project>

Maven Coordinates: GAV

Mọi artifact Maven được xác định bởi ba tọa độ (GAV):

  • GroupId: Tổ chức/namespace (ví dụ: com.chanhle)
  • ArtifactId: Tên dự án (ví dụ: my-app)
  • Version: Số version (ví dụ: 1.0.0-SNAPSHOT)

Dependency Scopes

Maven cung cấp một số dependency scopes:

ScopeBuildTestRuntimeMô tả
compile✅✅✅Scope mặc định, có sẵn ở mọi nơi
provided✅✅❌Được cung cấp bởi runtime (ví dụ: servlet API)
runtime❌✅✅Không cần cho compilation (ví dụ: JDBC drivers)
test❌✅❌Chỉ dành cho testing (ví dụ: JUnit)
system✅✅✅Các file JAR local (tránh nếu có thể)
importN/AN/AN/AImport quản lý dependency từ BOM

Ví dụ:

<!-- Compile scope (mặc định) -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>
 
<!-- Provided scope (ví dụ: Lombok) -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>
 
<!-- Test scope (ví dụ: thư viện testing) -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.8.0</version>
    <scope>test</scope>
</dependency>

Maven Build Lifecycle

Maven có ba vòng đời tích hợp, mỗi vòng có các phases:

1. Default Lifecycle (Build & Deploy)

# Các phase chính (theo thứ tự):
mvn validate      # Validate cấu trúc dự án
mvn compile       # Biên dịch mã nguồn
mvn test          # Chạy unit tests
mvn package       # Tạo JAR/WAR
mvn verify        # Chạy integration tests
mvn install       # Cài đặt vào repository local (~/.m2/repository)
mvn deploy        # Deploy lên repository remote

2. Clean Lifecycle

mvn clean         # Xóa thư mục target/

3. Site Lifecycle

mvn site          # Tạo tài liệu dự án

Phase Chaining:

Mỗi phase thực thi tất cả các phase trước đó:

mvn package       # Chạy: validate → compile → test → package
mvn clean install # Chạy: clean → validate → compile → test → package → install

Maven Plugins

Plugins mở rộng chức năng của Maven. Đây là các plugin cần thiết:

Compiler Plugin:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.11.0</version>
    <configuration>
        <source>21</source>
        <target>21</target>
        <compilerArgs>
            <arg>--enable-preview</arg> <!-- Bật preview features -->
        </compilerArgs>
    </configuration>
</plugin>

JAR Plugin (với Manifest):

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.3.0</version>
    <configuration>
        <archive>
            <manifest>
                <mainClass>com.chanhle.Main</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

Shade Plugin (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>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.chanhle.Main</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

Maven Profiles

Profiles cho phép cấu hình theo môi trường cụ thể:

<profiles>
    <!-- Development Profile -->
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <env>development</env>
            <database.url>jdbc:postgresql://localhost:5432/dev_db</database.url>
        </properties>
    </profile>
 
    <!-- Production Profile -->
    <profile>
        <id>prod</id>
        <properties>
            <env>production</env>
            <database.url>jdbc:postgresql://prod-server:5432/prod_db</database.url>
        </properties>
        <build>
            <plugins>
                <!-- Bật code obfuscation trong production -->
                <plugin>
                    <groupId>com.github.wvengen</groupId>
                    <artifactId>proguard-maven-plugin</artifactId>
                    <version>2.6.0</version>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

Sử dụng:

mvn clean install              # Sử dụng dev profile (mặc định)
mvn clean install -Pprod       # Kích hoạt prod profile

Gradle: Linh Hoạt và Mạnh Mẽ

Gradle là công cụ build hiện đại nhấn mạnh vào tính linh hoạt và hiệu suất. Đây là công cụ build mặc định cho Android và ngày càng phổ biến cho Java phía server.

Lợi Thế Chính

  • Hiệu suất: Incremental builds, build cache, thực thi song song
  • Linh hoạt: Build scripts theo chương trình (Groovy hoặc Kotlin DSL)
  • Ngắn gọn: Ít verbose hơn Maven XML
  • Mạnh mẽ: Có thể mô hình hóa logic build phức tạp

Cấu Trúc Dự Án

Gradle sử dụng cùng cấu trúc thư mục như Maven nhưng thêm:

my-app/
├── build.gradle               # Cấu hình build (Groovy)
├── settings.gradle            # Cài đặt multi-project
├── gradlew                    # Gradle Wrapper (Unix)
├── gradlew.bat                # Gradle Wrapper (Windows)
├── gradle/
│   └── wrapper/
│       └── gradle-wrapper.properties
├── src/
│   ├── main/java/
│   └── test/java/
└── build/                     # Output build (như target/)

Groovy DSL vs Kotlin DSL

Gradle hỗ trợ hai DSL:

Groovy DSL (build.gradle):

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.1'
}
 
group = 'com.chanhle'
version = '1.0.0-SNAPSHOT'
 
java {
    sourceCompatibility = JavaVersion.VERSION_21
}
 
repositories {
    mavenCentral()
}
 
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok:1.18.30'
    annotationProcessor 'org.projectlombok:lombok:1.18.30'
    
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
 
test {
    useJUnitPlatform()
}

Kotlin DSL (build.gradle.kts):

plugins {
    java
    id("org.springframework.boot") version "3.2.1"
}
 
group = "com.chanhle"
version = "1.0.0-SNAPSHOT"
 
java {
    sourceCompatibility = JavaVersion.VERSION_21
}
 
repositories {
    mavenCentral()
}
 
dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    compileOnly("org.projectlombok:lombok:1.18.30")
    annotationProcessor("org.projectlombok:lombok:1.18.30")
    
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}
 
tasks.test {
    useJUnitPlatform()
}

Khuyến nghị: Sử dụng Kotlin DSL cho type safety và autocomplete của IDE.

Gradle Configuration Scopes

Gradle sử dụng tên khác cho dependency scopes:

GradleTương đương MavenMô tả
implementationcompileCó sẵn tại compile và runtime
apicompileGiống implementation nhưng transitive
compileOnlyprovidedChỉ compile-time
runtimeOnlyruntimeChỉ runtime
testImplementationtestTest compile và runtime
testCompileOnlytest (provided)Chỉ test compile
testRuntimeOnlytest (runtime)Chỉ test runtime

Quan trọng: Sử dụng implementation thay vì api trừ khi bạn cần transitive exposure.

Gradle Tasks

Tasks là đơn vị công việc trong Gradle:

# Liệt kê tất cả tasks
./gradlew tasks
 
# Các tasks phổ biến
./gradlew clean               # Xóa build/
./gradlew compileJava         # Biên dịch nguồn
./gradlew test                # Chạy tests
./gradlew build               # Full build (compile + test + package)
./gradlew bootRun             # Chạy Spring Boot app
./gradlew bootJar             # Tạo JAR thực thi

Custom Gradle Tasks

Bạn có thể định nghĩa custom tasks trong build.gradle:

task hello {
    doLast {
        println 'Hello, Gradle!'
    }
}
 
task copyResources(type: Copy) {
    from 'src/main/resources'
    into 'build/custom-resources'
}
 
task printClasspath {
    doLast {
        configurations.runtimeClasspath.each { println it }
    }
}

Chạy custom tasks:

./gradlew hello
./gradlew copyResources

Gradle Wrapper

Gradle Wrapper (gradlew) là một game-changer:

  • Tính nhất quán Version: Mọi developer sử dụng cùng phiên bản Gradle
  • Không cần cài đặt: Tự động tải Gradle
  • CI/CD Ready: Không cần cài Gradle trong môi trường CI

Luôn sử dụng wrapper:

./gradlew build       # Unix/Linux/macOS
gradlew.bat build     # Windows

Khởi tạo wrapper:

gradle wrapper --gradle-version=8.5

Tìm Hiểu Sâu Về Quản Lý Dependencies

Transitive Dependencies

Khi bạn khai báo một dependency, bạn cũng nhận được các dependencies của nó (transitive dependencies).

Ví dụ:

<!-- Bạn khai báo Spring Boot Web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
 
<!-- Maven tự động bao gồm:
     - spring-boot-starter
     - spring-boot
     - spring-core
     - spring-web
     - spring-webmvc
     - tomcat-embed-core
     - jackson-databind
     - ... và nhiều hơn nữa
-->

Xem dependency tree:

# Maven
mvn dependency:tree
 
# Gradle
./gradlew dependencies

Version Conflicts

Khi nhiều dependencies yêu cầu các phiên bản khác nhau của cùng một thư viện, bạn gặp phải xung đột version.

Chiến lược giải quyết của Maven:

  • Nearest Definition: Sử dụng version gần nhất với dự án của bạn trong dependency tree
  • First Declaration: Nếu cách đều, sử dụng version được khai báo đầu tiên

Chiến lược giải quyết của Gradle:

  • Highest Version: Mặc định, sử dụng version cao nhất

Ép buộc Version Cụ thể (Maven):

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>32.1.3-jre</version>
        </dependency>
    </dependencies>
</dependencyManagement>

Ép buộc Version Cụ thể (Gradle):

configurations.all {
    resolutionStrategy {
        force 'com.google.guava:guava:32.1.3-jre'
    }
}

Dependency Exclusions

Loại trừ transitive dependencies bạn không muốn:

Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
 
<!-- Sử dụng Jetty thay thế -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Gradle:

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web') {
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
    }
    implementation 'org.springframework.boot:spring-boot-starter-jetty'
}

Bill of Materials (BOM)

BOM quản lý các version cho các dependencies liên quan:

Maven:

<dependencyManagement>
    <dependencies>
        <!-- Spring Boot BOM -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>3.2.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
 
<dependencies>
    <!-- Version kế thừa từ BOM -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

Gradle:

dependencies {
    // Import BOM
    implementation platform('org.springframework.boot:spring-boot-dependencies:3.2.1')
    
    // Version kế thừa từ BOM
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

Dự Án Đa Module

Các dự án lớn thường được chia thành các modules để tổ chức tốt hơn.

Maven Multi-Module Project

Cấu trúc dự án:

my-app/
├── pom.xml                    # Parent POM
├── my-app-core/
│   └── pom.xml
├── my-app-api/
│   └── pom.xml
└── my-app-web/
    └── pom.xml

Parent POM (pom.xml):

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.chanhle</groupId>
    <artifactId>my-app-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
 
    <!-- Modules -->
    <modules>
        <module>my-app-core</module>
        <module>my-app-api</module>
        <module>my-app-web</module>
    </modules>
 
    <!-- Common Properties -->
    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
    </properties>
 
    <!-- Dependency Management for Children -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter</artifactId>
                <version>5.10.1</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

Child Module POM (my-app-api/pom.xml):

<project>
    <modelVersion>4.0.0</modelVersion>
    
    <!-- Parent Reference -->
    <parent>
        <groupId>com.chanhle</groupId>
        <artifactId>my-app-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
 
    <artifactId>my-app-api</artifactId>
 
    <dependencies>
        <!-- Internal Dependency -->
        <dependency>
            <groupId>com.chanhle</groupId>
            <artifactId>my-app-core</artifactId>
            <version>${project.version}</version>
        </dependency>
 
        <!-- External Dependencies (version từ parent) -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Build tất cả modules:

mvn clean install              # Từ thư mục parent
mvn clean install -pl my-app-api  # Build module cụ thể

Gradle Multi-Module Project

Cấu trúc dự án:

my-app/
├── build.gradle
├── settings.gradle            # Khai báo subprojects
├── my-app-core/
│   └── build.gradle
├── my-app-api/
│   └── build.gradle
└── my-app-web/
    └── build.gradle

settings.gradle:

rootProject.name = 'my-app'
include 'my-app-core', 'my-app-api', 'my-app-web'

Root build.gradle:

plugins {
    id 'java' apply false
}
 
subprojects {
    apply plugin: 'java'
 
    group = 'com.chanhle'
    version = '1.0.0-SNAPSHOT'
 
    repositories {
        mavenCentral()
    }
 
    java {
        sourceCompatibility = JavaVersion.VERSION_21
    }
 
    dependencies {
        testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1'
    }
 
    test {
        useJUnitPlatform()
    }
}

Subproject build.gradle (my-app-api/build.gradle):

dependencies {
    // Internal dependency
    implementation project(':my-app-core')
 
    // External dependencies
    implementation 'org.springframework.boot:spring-boot-starter-web:3.2.1'
}

Java 9+ Module System (JPMS)

Java Platform Module System (JPMS) cung cấp đóng gói mạnh mẽ hơn và dependencies rõ ràng.

module-info.java

Mỗi module có file module-info.java tại source root:

Cấu trúc dự án:

my-app/
└── src/main/java/
    ├── module-info.java          # Module descriptor
    └── com/chanhle/myapp/
        └── Main.java

module-info.java cơ bản:

module com.chanhle.myapp {
    // Các modules mà module này phụ thuộc vào
    requires java.base;          // Ngầm định, tất cả modules yêu cầu điều này
    requires java.sql;           // Truy cập JDBC
    requires com.google.gson;    // Thư viện bên thứ ba
 
    // Packages mà module này exports (làm public)
    exports com.chanhle.myapp;
    exports com.chanhle.myapp.api;
 
    // Packages được export chỉ cho các modules cụ thể
    exports com.chanhle.myapp.internal to com.chanhle.myapp.web;
 
    // Mở packages cho reflection (ví dụ: cho frameworks)
    opens com.chanhle.myapp.model to com.fasterxml.jackson.databind;
 
    // Service provider interface
    uses com.chanhle.myapp.spi.PluginService;
 
    // Service implementation
    provides com.chanhle.myapp.spi.PluginService 
        with com.chanhle.myapp.impl.DefaultPluginService;
}

Module Directives

DirectiveMô tả
requiresKhai báo dependency vào module khác
requires transitiveTransitive dependency (người gọi cũng có quyền truy cập)
requires staticOptional dependency (chỉ compile-time)
exportsLàm package public cho tất cả modules
exports...toQualified export (chỉ cho modules cụ thể)
opensCho phép truy cập reflection
opens...toQualified opens
usesKhai báo service consumer
provides...withKhai báo service provider

Automatic Modules

Các JAR không modular trên module path trở thành "automatic modules":

module com.chanhle.myapp {
    // Tham chiếu automatic module bằng tên JAR
    requires commons.lang3;  // commons-lang3-3.12.0.jar → commons.lang3
}

Best Practice: Sử dụng modular JARs khi có sẵn.

Ví dụ Module: Spring Boot

module-info.java cho Spring Boot app:

module com.chanhle.myapp {
    requires spring.boot;
    requires spring.boot.autoconfigure;
    requires spring.web;
    requires spring.context;
 
    // Mở package cho component scanning của Spring
    opens com.chanhle.myapp to spring.core, spring.beans, spring.context;
    opens com.chanhle.myapp.controller to spring.core, spring.beans, spring.web;
 
    // Export API packages
    exports com.chanhle.myapp.api;
}

Lưu ý: Nhiều frameworks (Spring, Hibernate) sử dụng reflection, yêu cầu directives opens.

Maven vs Gradle: So Sánh

Tính năngMavenGradle
Cấu hìnhXML (verbose)Groovy/Kotlin (ngắn gọn)
Hiệu suấtChậm hơnNhanh hơn (incremental builds, caching)
Linh hoạtDựa trên conventionRất linh hoạt
Đường cong học tậpDễ hơn cho người mớiBan đầu khó hơn
Tốc độ BuildCơ bảnNhanh hơn 2-10 lần
Incremental BuildsHạn chếXuất sắc
Dependency CacheChỉ localLocal + remote
Multi-ModuleTốtXuất sắc
AndroidKhông hỗ trợMặc định
Chấp nhận Doanh nghiệpRất caoTăng trưởng nhanh
Hỗ trợ IDEXuất sắcXuất sắc

Khi Nào Sử Dụng Maven

  • Sự quen thuộc của Team: Team biết Maven rất tốt
  • Dự án đơn giản: Ứng dụng Java tiêu chuẩn
  • Tiêu chuẩn công ty: Tổ chức bắt buộc Maven
  • Hệ sinh thái trưởng thành: Thư viện plugin mở rộng

Khi Nào Sử Dụng Gradle

  • Hiệu suất quan trọng: Codebase lớn cần builds nhanh
  • Cần linh hoạt: Logic build phức tạp
  • Đa ngôn ngữ: Dự án trộn Java, Kotlin, Groovy
  • Android Development: Bắt buộc cho Android
  • Cách tiếp cận hiện đại: Bắt đầu dự án greenfield mới

Tích Hợp Spring Boot

Cả Maven và Gradle đều có hỗ trợ Spring Boot xuất sắc.

Spring Boot với Maven

pom.xml:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.1</version>
</parent>
 
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
 
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Lệnh:

mvn spring-boot:run              # Chạy ứng dụng
mvn package                      # Tạo JAR thực thi
java -jar target/my-app-1.0.0-SNAPSHOT.jar  # Chạy JAR

Spring Boot với Gradle

build.gradle:

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.1'
    id 'io.spring.dependency-management' version '1.1.4'
}
 
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Lệnh:

./gradlew bootRun               # Chạy ứng dụng
./gradlew bootJar               # Tạo JAR thực thi
java -jar build/libs/my-app-1.0.0-SNAPSHOT.jar  # Chạy JAR

Thiết Lập Dự Án Chuyên Nghiệp

Hãy tạo một dự án Java chuyên nghiệp từ đầu với các best practices.

1. Khởi tạo với Maven

mvn archetype:generate \
  -DgroupId=com.chanhle \
  -DartifactId=my-app \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DarchetypeVersion=1.4 \
  -DinteractiveMode=false
 
cd my-app

2. Khởi tạo với Gradle

mkdir my-app && cd my-app
gradle init --type java-application --dsl kotlin --test-framework junit-jupiter

3. Template pom.xml Chuyên Nghiệp

<?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.chanhle</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>My Professional App</name>
    <description>Production-ready Java application</description>
    <url>https://github.com/yourusername/my-app</url>
 
    <properties>
        <!-- Java Version -->
        <java.version>21</java.version>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 
        <!-- Dependency Versions -->
        <spring.boot.version>3.2.1</spring.boot.version>
        <lombok.version>1.18.30</lombok.version>
        <junit.version>5.10.1</junit.version>
        <mockito.version>5.8.0</mockito.version>
        <assertj.version>3.24.2</assertj.version>
 
        <!-- Plugin Versions -->
        <maven.compiler.plugin.version>3.11.0</maven.compiler.plugin.version>
        <maven.surefire.plugin.version>3.2.3</maven.surefire.plugin.version>
        <jacoco.version>0.8.11</jacoco.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>
 
    <dependencies>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
 
        <!-- Testing -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>${assertj.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <!-- Compiler -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
 
            <!-- Testing -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surefire.plugin.version}</version>
            </plugin>
 
            <!-- Code Coverage -->
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>report</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
 
            <!-- Spring Boot Plugin -->
            <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>
        </plugins>
    </build>
</project>

4. Template build.gradle.kts Chuyên Nghiệp

plugins {
    java
    id("org.springframework.boot") version "3.2.1"
    id("io.spring.dependency-management") version "1.1.4"
    jacoco
}
 
group = "com.chanhle"
version = "1.0.0-SNAPSHOT"
 
java {
    sourceCompatibility = JavaVersion.VERSION_21
}
 
configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
}
 
repositories {
    mavenCentral()
}
 
dependencies {
    // Spring Boot
    implementation("org.springframework.boot:spring-boot-starter-web")
    
    // Lombok
    compileOnly("org.projectlombok:lombok")
    annotationProcessor("org.projectlombok:lombok")
    
    // Testing
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.assertj:assertj-core:3.24.2")
}
 
tasks.test {
    useJUnitPlatform()
    finalizedBy(tasks.jacocoTestReport)
}
 
tasks.jacocoTestReport {
    dependsOn(tasks.test)
    reports {
        xml.required.set(true)
        html.required.set(true)
    }
}
 
tasks.bootJar {
    archiveFileName.set("${project.name}-${project.version}.jar")
}

Best Practices

1. Quản Lý Version

Sử dụng Properties/Variables:

<!-- Maven -->
<properties>
    <spring.version>6.1.2</spring.version>
</properties>
 
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.version}</version>
</dependency>
// Gradle
ext {
    springVersion = '6.1.2'
}
 
dependencies {
    implementation "org.springframework:spring-core:$springVersion"
}

2. Dependency Locking

Maven Versions Plugin:

mvn versions:display-dependency-updates
mvn versions:use-latest-releases

Gradle Dependency Locking:

./gradlew dependencies --write-locks

3. Reproducible Builds

  • Pin tất cả versions (không dùng LATEST, RELEASE, hoặc version ranges)
  • Sử dụng dependency lock files
  • Commit lock files vào version control
  • Sử dụng Gradle wrapper (gradlew) hoặc Maven wrapper

4. Tổ Chức Multi-Module

Cấu trúc enterprise điển hình:

my-app/
├── my-app-domain/        # Domain models
├── my-app-service/       # Business logic
├── my-app-repository/    # Data access
├── my-app-api/           # REST controllers
└── my-app-web/           # Web application

5. Tránh Dependency Hell

  • Sử dụng BOM để căn chỉnh version
  • Loại trừ transitive dependencies gây xung đột
  • Xem lại dependency tree thường xuyên
  • Giữ dependencies cập nhật (security patches)

6. Quản Lý Repository

Sử dụng private repository (Nexus, Artifactory) cho:

  • Builds nhanh hơn (cache local)
  • Bảo mật (scan dependencies)
  • Kiểm soát (chỉ các thư viện được phê duyệt)
  • Độ tin cậy (mirrors của Maven Central)

Xử Lý Sự Cố Thường Gặp

Maven: Không Tìm Thấy Dependency

# Xóa cache local
rm -rf ~/.m2/repository
 
# Ép buộc cập nhật
mvn clean install -U

Gradle: Vấn Đề Build Cache

# Xóa build cache
./gradlew clean --no-build-cache
 
# Xóa global cache
rm -rf ~/.gradle/caches

Version Conflicts

# Maven: Tìm các version xung đột
mvn dependency:tree -Dverbose
 
# Gradle: Tìm các version xung đột
./gradlew dependencyInsight --dependency guava

Các Bước Tiếp Theo

Bây giờ bạn đã có nền tảng vững chắc về các công cụ build Java! Để tiếp tục hành trình của bạn:

  1. Thực hành: Thiết lập dự án Spring Boot đa module
  2. Khám phá Plugins: Maven Shade, Gradle Shadow cho fat JARs
  3. Tích hợp CI/CD: GitHub Actions, Jenkins với Maven/Gradle
  4. Học Testing: Unit tests, integration tests, coverage reports
  5. Bảo mật Dependency: OWASP Dependency Check, Snyk

Trong bài viết tiếp theo, chúng ta sẽ đi sâu vào Java Testing với JUnit 5, Mockito, và AssertJ.


Thuộc series Lộ Trình Học Java

Quay lại: Giai Đoạn 3: Core Java APIs

Các Deep Dive Liên Quan:

Bước Tiếp Theo: Bắt Đầu Với Spring Boot

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.