Cách Sử dụng Maven để xây dựng dự án Spring Boot Cha-Con - i88win

| May 11, 2025 min read

12 tháng 3 năm 2023 | Công nghệ thông tin

Bài viết này sẽ hướng dẫn cách sử dụng Maven để tạo ra một dự án Spring Boot với cấu trúc cha-con, giúp ích khi bạn xây dựng các dịch vụ vi mô (microservices) bằng Spring Boot.

Khi xây dựng các dự án Spring Boot microservices sử dụng Maven, cách tiếp cận trực tiếp nhất là thêm tham chiếu đến dự án cha của Spring Boot Starter org.springframework.boot:spring-boot-starter-parent trong mỗi file pom.xml, và cấu hình các thư viện cần thiết. Tuy nhiên, nếu có nhiều dự án microservices, việc nâng cấp phiên bản các thư viện trong từng file pom.xml sẽ rất mất thời gian và dễ gây lỗi. Vì vậy, giải pháp tốt hơn là trích xuất các thư viện chung vào một dự án cha, sau đó các dự án con sẽ tham chiếu đến dự án cha này. Điều này cho phép quản lý tập trung các thư viện, và khi cần nâng cấp, chỉ cần cập nhật phiên bản của dự án cha.

Trong bài viết này, chúng ta sẽ tạo một ví dụ về dự án cha Spring Boot và thử nghiệm nó với một dự án con.

Phiên bản được sử dụng trong bài viết này:

  • Java: 1.8
  • Maven: 3.9.0
  • Spring Boot: 2.7.9

1. Tạo Dự Án Cha

Đầu tiên, chúng ta bắt đầu bằng việc tạo dự án cha starter-parent. Dự án cha có thể bao gồm nhiều module con, nhưng trong ví dụ này, chúng ta chỉ tạo một module con duy nhất tên là common-utils, dùng để lưu trữ các lớp công cụ chung.

Chúng ta sẽ sử dụng lệnh mvn archetype:generate để tạo khung cơ bản cho dự án. Đầu tiên, tạo dự án rỗng starter-parent, sau đó xóa thư mục src không cần thiết và chỉnh sửa <packaging> trong file pom.xml thành pom.

mvn archetype:generate \
  -DgroupId=com.leileiluoluo \
  -DartifactId=starter-parent \
  -Dversion=1.0-SNAPSHOT \
  -DinteractiveMode=false

Sau đó, chuyển vào thư mục starter-parent và tạo module con common-utils:

cd starter-parent/
mvn archetype:generate \
  -DgroupId=com.leileiluoluo \
  -DartifactId=common-utils \
  -Dversion=1.0-SNAPSHOT \
  -DinteractiveMode=false

Như vậy, chúng ta đã có cấu trúc dự án như sau:

starter-parent
 |-- common-utils
 |  |-- src/main/java
 |  `-- pom.xml
 `-- pom.xml

Tiếp theo, chỉnh sửa hai file pom.xml để đơn giản hóa cấu hình, đồng thời thêm một class công cụ DataUtil.java vào module common-utils.

Cấu trúc cuối cùng của dự án sẽ như sau:

starter-parent
 |-- common-utils
 |  |-- src/main/java
 |  |  `-- com/leileiluoluo/common/util/DataUtil.java
 |  `-- pom.xml
 `-- pom.xml

Giờ hãy xem qua mã nguồn của một số file quan trọng trong dự án này.

1.1. pom.xml

File POM gốc, chú ý rằng <parent> tham chiếu đến spring-boot-starter-parent; <packaging> được đặt là pom; <dependencyManagement> định nghĩa tất cả các thư viện chung; <pluginManagement> định nghĩa tất cả các plugin chung.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.9</version>
        <relativePath/>
    </parent>
    <groupId>com.leileiluoluo</groupId>
    <artifactId>starter-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
        <spring.boot.version>2.7.9</spring.boot.version>
    </properties>
    <modules>
        <module>common-utils</module>
    </modules>
    <dependencyManagement>
        <dependencies>
            <!-- Các module -->
            <dependency>
                <groupId>com.leileiluoluo</groupId>
                <artifactId>common-utils</artifactId>
                <version>${version}</version>
            </dependency>
            <!-- Thư viện khởi động Spring Boot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <!-- Test -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>${spring.boot.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter-api</artifactId>
                <version>5.9.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

1.2. common-utils/pom.xml

File POM của module con common-utils, chú ý rằng <groupId><parent> giống nhau nên có thể bỏ qua; <version> của các thư viện đã được định nghĩa trong POM cha nên cũng có thể bỏ qua.

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.leileiluoluo</groupId>
        <artifactId>starter-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>common-utils</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

1.3. DataUtil.java trong common-utils

Class DataUtil.java nằm trong module con common-utils, cung cấp chức năng cho các dự án khác sử dụng.

package com.leileiluoluo.common.util;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class DateUtil {
    public static String getCurrentTimeStr() [i88win](/post/boyouquan-introduction/)  {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return ZonedDateTime.now().format(formatter);
    }
}

Tiếp theo, chúng ta sẽ thử tạo một dự án con để tham chiếu đến dự án cha vừa tạo. Trước khi thử, hãy sử dụng Maven để đóng gói và cài đặt dự án này vào kho cục bộ. Lệnh thực thi:

mvn clean install
...
[INFO] Reactor Summary for starter-parent 1.0-SNAPSHOT:
[INFO]
[INFO] starter-parent ..................................... SUCCESS [ 0.170 s]
[INFO] common-utils ....................................... SUCCESS [ 1.330 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.658 s
[INFO] Finished at: 2023-03-14T10:34:43+08:00
[INFO] ------------------------------------------------------------------------

2. Sử dụng Dự Án Cha

Giờ chúng ta sẽ tạo dự án con app-child để sử dụng dự án cha starter-parent mà chúng ta đã tạo trước đó.

Cấu trúc của dự án con sau khi tạo sẽ như sau:

app-child
 |-- src/main/java
 |  `-- com/leileiluoluo/app/DemoApplication.java
 `-- pom.xml

Sau đây là nội dung của một số file quan trọng trong dự án này, cùng với việc thử nghiệm chạy và kiểm tra dự án.

2.1. pom.xml

File POM của dự án con, chú ý rằng <parent> tham chiếu đến dự án cha starter-parent mà chúng ta đã tạo; <packaging> sử dụng kiểu mặc định jar; các thư viện chung được tham chiếu trong <dependencies> không cần chỉ định <version>; các plugin chung được tham chiếu trong <plugins> cũng không cần chỉ định <version>.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>
    <parent>
        <groupId>com.leileiluoluo</groupId>
        <artifactId>starter-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>app-child</artifactId>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.leileiluoluo</groupId>
            <artifactId>common-utils</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2.2. DemoApplication.java

Chỉ định một API hello, trong phương thức này gọi đến phương thức DateUtil.getCurrentTimeStr() từ module common-utils của dự án cha.

package com.leileiluoluo.app;

import com.leileiluoluo.common.util.DateUtil;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

    public static void main(String[] [win 911](/post/8207/)  args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @GetMapping("/hello")
    public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
        return String.format("Hello %s! %s", name, DateUtil.getCurrentTimeStr());
    }
}

2.3. Khởi động và Kiểm tra

Sử dụng Maven để đóng gói dự án, sau đó chạy file JAR đã đóng gói. Lệnh thực thi:

cd app-child/
mvn clean package
java -jar target/app-child-1.0-SNAPSHOT.jar

Sau khi khởi động hoàn tất, sử dụng curl để gửi yêu cầu, kết quả nhận được sẽ như sau:

curl 'http://localhost:8080/hello?name=Larry'
Hello Larry! 2023-03-13 16:31:23

Như vậy, bài viết này đã hướng dẫn cách sử dụng Maven để xây dựng một dự án Spring Boot với cấu trúc cha-con, giúp tối ưu quá trình phát triển các dịch vụ vi mô Spring Boot. Mã nguồn đầy đủ của dự án cha starter-parent và dự án con app-child đã được lưu trữ trên GitHub, mời bạn quan tâm hoặc fork.