Spring Boot Dependency Management: Maven & Gradle

Giới Thiệu
Mọi Spring Boot project đều bắt đầu với một build file — pom.xml hoặc build.gradle. Những file này define xem application của bạn depend vào library nào, cách resolve chúng, và cách project được build. Làm đúng dependency management nghĩa là ít version conflict hơn, build nhanh hơn, và codebase dễ maintain hơn.
Trong bài hướng dẫn chi tiết này, chúng ta sẽ cover:
- Cách Maven và Gradle handle dependency
- Spring Boot Starters và auto-configuration
- Spring Boot BOM (Bill of Materials)
- Quản lý transitive dependency và version conflict
- Setup multi-module project
- Dependency security scanning
- Best practices cho production-ready build
Yêu cầu: Quen thuộc với Java và các concept cơ bản của Spring Boot. Nếu bạn mới bắt đầu, hãy đọc Getting Started with Spring Boot trước.
Maven vs Gradle: Chọn Build Tool Nào?
Cả Maven và Gradle đều là lựa chọn tuyệt vời. So sánh nhanh:
| Feature | Maven | Gradle |
|---|---|---|
| Configuration | XML (pom.xml) | Groovy/Kotlin DSL (build.gradle) |
| Build Speed | Tốt (với -T parallel) | Nhanh hơn (incremental + cache) |
| Learning Curve | Thấp hơn (convention-heavy) | Trung bình (flexible DSL) |
| Ecosystem | Plugin ecosystem lớn nhất | Đang phát triển, Kotlin-first |
| Spring Boot Support | First-class | First-class |
| Dependency Locking | Qua plugin | Built-in |
| Build Cache | Chỉ local | Local + remote |
Recommendation: Dùng Maven nếu team đã quen. Chọn Gradle cho project mới mà build speed quan trọng — đặc biệt trong CI/CD pipeline.
Hiểu Spring Boot Starters
Spring Boot Starters là những dependency bundle được curate sẵn, pull vào mọi thứ bạn cần cho một feature cụ thể. Thay vì manually add 5-10 library riêng lẻ, bạn chỉ cần thêm một starter duy nhất.
Cách Starters Hoạt Động
Thêm spring-boot-starter-web là bạn có ngay embedded Tomcat server, Jackson cho JSON, Spring MVC, và toàn bộ transitive dependency — với version tương thích được đảm bảo.
Bảng Tham Khảo Các Starter Phổ Biến
| Starter | Bao Gồm |
|---|---|
spring-boot-starter-web | Spring MVC, Tomcat, Jackson |
spring-boot-starter-data-jpa | Hibernate, Spring Data JPA, HikariCP |
spring-boot-starter-security | Spring Security, authentication filter |
spring-boot-starter-validation | Hibernate Validator, Jakarta Validation |
spring-boot-starter-actuator | Health check, metric, monitoring endpoint |
spring-boot-starter-cache | Spring Cache abstraction |
spring-boot-starter-mail | JavaMail, Spring mail integration |
spring-boot-starter-test | JUnit 5, Mockito, AssertJ, MockMvc |
spring-boot-starter-webflux | Reactive web với Netty |
spring-boot-starter-amqp | RabbitMQ integration |
spring-boot-starter-data-redis | Redis client, Spring Data Redis |
Những Gì KHÔNG Phải Là Starter
Không phải mọi Spring Boot dependency đều là starter. Đây là những standalone library bạn cần add riêng:
<!-- Những cái này KHÔNG phải starter — add riêng -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>Maven Dependency Management
Setup Project với Spring Initializr
Cách dễ nhất để bắt đầu là Spring Initializr. Nó generate pom.xml với Spring Boot parent POM và các starter bạn chọn.
Spring Boot Parent POM
Mọi Maven-based Spring Boot project đều inherit từ spring-boot-starter-parent:
<?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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- Inherit Spring Boot defaults -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>my-app</name>
<description>My Spring Boot Application</description>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- Không cần version! Inherit từ parent -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</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>Điểm chính:
- Không cần version number cho Spring Boot starter — parent POM quản lý hết
spring-boot-starter-parentextendspring-boot-dependenciesBOM- Hơn 400+ version library được define sẵn và test cùng nhau
spring-boot-maven-pluginpackage app thành executable JAR
Chuỗi Parent POM
Parent POM cung cấp:
- Dependency version: Toàn bộ Spring ecosystem + common third-party library
- Plugin default: Compiler version, resource filtering, packaging
- Profile support: Production-ready default
- Property override: Dễ dàng customize version qua
<properties>
Dùng BOM Mà Không Cần Parent POM
Nếu project đã có parent POM riêng (ví dụ corporate parent), import Spring Boot BOM thay thế:
<project>
<!-- Corporate parent của bạn -->
<parent>
<groupId>com.company</groupId>
<artifactId>company-parent</artifactId>
<version>2.0.0</version>
</parent>
<dependencyManagement>
<dependencies>
<!-- Import Spring Boot BOM -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.4.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- Vẫn không cần version -->
</dependency>
</dependencies>
</project>Dependency Scope Trong Maven
Maven scope kiểm soát khi nào dependency available:
| Scope | Compile | Test | Runtime | Packaged |
|---|---|---|---|---|
compile (default) | Yes | Yes | Yes | Yes |
provided | Yes | Yes | No | No |
runtime | No | Yes | Yes | Yes |
test | No | Yes | No | No |
system | Yes | Yes | No | No |
<dependencies>
<!-- compile scope (default) — luôn available -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- runtime — chỉ cần lúc runtime, không cần cho compilation -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- provided — available lúc compile, container cung cấp lúc runtime -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- test — chỉ dùng cho testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>Override Version Được Quản Lý
Cần version khác của managed dependency? Override qua properties:
<properties>
<java.version>21</java.version>
<!-- Override managed version -->
<jackson.version>2.16.1</jackson.version>
<snakeyaml.version>2.2</snakeyaml.version>
<postgresql.version>42.7.1</postgresql.version>
</properties>Để tìm property nào control version dependency, check spring-boot-dependencies POM.
Xem Dependency Tree
Dependency tree là debug tool tốt nhất của bạn:
# Full dependency tree
mvn dependency:tree
# Filter cho artifact cụ thể
mvn dependency:tree -Dincludes=com.fasterxml.jackson
# Hiện conflict và duplicate
mvn dependency:tree -Dverbose
# Output ra file cho project lớn
mvn dependency:tree -DoutputFile=deps.txtOutput ví dụ:
[INFO] com.example:my-app:jar:0.0.1-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:3.4.3:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:3.4.3:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:3.4.3:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:3.4.3:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:3.4.3:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:3.4.3:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-databind:jar:2.16.1:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:3.4.3:compile
[INFO] | +- org.springframework:spring-webmvc:jar:6.2.3:compileExclude Transitive Dependency
Đôi khi starter pull vào library bạn không muốn:
<!-- Đổi từ Tomcat sang Jetty -->
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency><!-- Exclude default logging (Logback) để dùng Log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>Maven Profile Cho Dependency Theo Môi Trường
<profiles>
<!-- Development profile -->
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
</dependencies>
</profile>
<!-- Production profile -->
<profile>
<id>prod</id>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
</profiles>Activate profile:
# Development (default)
mvn spring-boot:run
# Production
mvn spring-boot:run -Pprod
# Nhiều profile
mvn spring-boot:run -Pprod,monitoringGradle Dependency Management
Setup Project
Spring Boot project dùng Gradle sử dụng Spring Boot Gradle plugin:
// build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.3'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'org.postgresql:postgresql'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}Gradle Kotlin DSL
Project hiện đại thường dùng Kotlin DSL cho type-safe build script:
// build.gradle.kts
plugins {
java
id("org.springframework.boot") version "3.4.3"
id("io.spring.dependency-management") version "1.1.7"
}
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("org.postgresql:postgresql")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<Test> {
useJUnitPlatform()
}Dependency Configuration Trong Gradle
Gradle dùng configuration thay vì scope của Maven:
| Configuration | Maven Tương Đương | Khi Nào Available |
|---|---|---|
implementation | compile | Compile + runtime, ẩn khỏi consumer |
api | compile | Compile + runtime, expose cho consumer |
compileOnly | provided | Chỉ lúc compile |
runtimeOnly | runtime | Chỉ lúc runtime |
testImplementation | test | Test compile + runtime |
annotationProcessor | N/A | Annotation processing lúc compile |
implementation vs api: Dùng implementation mặc định. Chỉ dùng api khi dependency là phần public API của module. Điều này cải thiện build performance vì thay đổi implementation dependency không trigger recompilation của downstream module.
Override Version Trong Gradle
// Cách 1: Qua ext properties (giống Maven properties)
ext['jackson.version'] = '2.16.1'
ext['snakeyaml.version'] = '2.2'
// Cách 2: Dependency constraint
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
// Force version cụ thể
constraints {
implementation('com.fasterxml.jackson.core:jackson-databind:2.16.1') {
because 'CVE fix cần version mới hơn'
}
}
}
// Cách 3: Resolution strategy
configurations.all {
resolutionStrategy {
force 'com.google.guava:guava:33.0.0-jre'
}
}Xem Dependency Trong Gradle
# Full dependency tree
./gradlew dependencies
# Configuration cụ thể
./gradlew dependencies --configuration runtimeClasspath
# Tìm dependency cụ thể
./gradlew dependencyInsight --dependency jackson-databind
# HTML report
./gradlew htmlDependencyReportExclude Dependency Trong Gradle
dependencies {
// Exclude từ dependency cụ thể
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'
// Global exclusion
configurations.all {
exclude group: 'commons-logging', module: 'commons-logging'
}
}Dependency Locking Trong Gradle
Lock version dependency cho reproducible build:
// Enable dependency locking
dependencyLocking {
lockAllConfigurations()
}# Generate lock file
./gradlew dependencies --write-locks
# Update lock
./gradlew dependencies --update-locks org.springframework.boot:*Lệnh này tạo gradle.lockfile trong project root — commit nó vào version control.
Quản Lý Transitive Dependency
Transitive Dependency Là Gì?
Khi bạn add spring-boot-starter-web, nó không chỉ add chính nó — nó pull vào hàng chục transitive dependency:
Cách Resolve Version Conflict
Khi hai dependency yêu cầu version khác nhau của cùng một library:
Chiến lược của Maven: Nearest definition wins (đường đi ngắn nhất trong dependency tree)
A → B → C:1.0
A → D → E → C:2.0
Maven chọn C:1.0 (gần A hơn)Chiến lược của Gradle: Highest version wins
A → B → C:1.0
A → D → E → C:2.0
Gradle chọn C:2.0 (version cao hơn)Chẩn Đoán Version Conflict
Maven:
# Hiện verbose tree với conflict marker
mvn dependency:tree -Dverbose
# Phân tích dependency issue
mvn dependency:analyze
# Hiện: unused declared dependency, used undeclared dependencyGradle:
# Hiện dependency insight với conflict resolution
./gradlew dependencyInsight --dependency jackson-databind --configuration runtimeClasspathOutput ví dụ cho conflict resolution:
com.fasterxml.jackson.core:jackson-databind:2.16.1
Variant runtimeElements:
Selected by rule: Spring Boot BOM
Requested: 2.15.0 → 2.16.1
com.fasterxml.jackson.core:jackson-databind:2.15.0 -> 2.16.1
\--- com.some-library:some-lib:1.0.0
\--- runtimeClasspathFlag Optional
Đánh dấu dependency là optional khi chúng không bắt buộc cho consumer của library:
<!-- Maven -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>// Gradle
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'Optional dependency KHÔNG được include transitively. Consumer phải explicitly add chúng nếu cần.
Multi-Module Project
Với application lớn hơn, chia project thành các module:
Maven Multi-Module Structure
my-app/
├── pom.xml (parent POM)
├── my-app-common/
│ └── pom.xml (shared DTO, utility)
├── my-app-domain/
│ └── pom.xml (entity, repository)
├── my-app-service/
│ └── pom.xml (business logic)
└── my-app-web/
└── pom.xml (REST controller, main app)Parent POM (pom.xml):
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
</parent>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>my-app-common</module>
<module>my-app-domain</module>
<module>my-app-service</module>
<module>my-app-web</module>
</modules>
<properties>
<java.version>21</java.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
</properties>
<!-- Shared dependency management cho non-Spring library -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>Domain module (my-app-domain/pom.xml):
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>my-app-domain</artifactId>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-app-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
</project>Web module (my-app-web/pom.xml):
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>my-app-web</artifactId>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-app-service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>Gradle Multi-Module Structure
settings.gradle.kts:
rootProject.name = "my-app"
include("my-app-common")
include("my-app-domain")
include("my-app-service")
include("my-app-web")Root build.gradle.kts:
plugins {
java
id("org.springframework.boot") version "3.4.3" apply false
id("io.spring.dependency-management") version "1.1.7" apply false
}
subprojects {
apply(plugin = "java")
apply(plugin = "io.spring.dependency-management")
group = "com.example"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
repositories {
mavenCentral()
}
// Apply BOM cho tất cả subproject
the<io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension>().apply {
imports {
mavenBom("org.springframework.boot:spring-boot-dependencies:3.4.3")
}
}
dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
}Web module build.gradle.kts:
plugins {
id("org.springframework.boot")
}
dependencies {
implementation(project(":my-app-service"))
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-actuator")
}Dependency Security
Tại Sao Quan Trọng
Third-party dependency có thể đưa vào security vulnerability. Một transitive dependency bị lỗ hổng duy nhất có thể expose toàn bộ application. Scan thường xuyên là điều cần thiết.
OWASP Dependency-Check
Maven:
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>9.0.9</version>
<configuration>
<!-- Fail build khi CVSS >= 7 -->
<failBuildOnCVSS>7</failBuildOnCVSS>
</configuration>
</plugin>
</plugins>
</build>mvn dependency-check:checkGradle:
plugins {
id 'org.owasp.dependencycheck' version '9.0.9'
}
dependencyCheck {
failBuildOnCVSS = 7.0f
analyzers {
assemblyEnabled = false
}
}./gradlew dependencyCheckAnalyzeTự Động Update Dependency
Dependabot (tích hợp sẵn trong GitHub):
Tạo .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
labels:
- "dependencies"
groups:
spring-boot:
patterns:
- "org.springframework.boot*"
- "org.springframework*"
jackson:
patterns:
- "com.fasterxml.jackson*"Renovate (thay thế Dependabot với nhiều feature hơn):
Tạo renovate.json:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":automergeMinor",
"group:springBoot"
],
"packageRules": [
{
"matchPackagePatterns": ["org.springframework"],
"groupName": "Spring Framework",
"automerge": false
}
]
}Security Tích Hợp Sẵn Của Spring Boot
Spring Boot đã giảm thiểu nhiều dependency risk:
- BOM-managed version: Tất cả dependency được test cùng nhau cho compatibility
- CVE patching: Security patch được đưa vào nhanh chóng trong Spring Boot release
- Dependency convergence: BOM đảm bảo version nhất quán trên toàn bộ dependency tree
Các Pattern và Recipe Phổ Biến
Recipe 1: Thêm Custom BOM
Khi dùng thêm library ecosystem (ví dụ AWS SDK, Google Cloud):
<!-- Maven -->
<dependencyManagement>
<dependencies>
<!-- AWS SDK BOM -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.24.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Google Cloud BOM -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.31.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Không cần version — managed bởi AWS BOM -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
</dependency>
</dependencies>// Gradle
dependencies {
implementation(platform("software.amazon.awssdk:bom:2.24.0"))
implementation("software.amazon.awssdk:s3")
implementation("software.amazon.awssdk:dynamodb")
}Recipe 2: Thay Embedded Server
<!-- Đổi từ Tomcat sang Undertow -->
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
</dependencies>Recipe 3: Dependency Chỉ Cho Test
<!-- Maven -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>// Gradle
dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.testcontainers:postgresql")
testImplementation("org.springframework.boot:spring-boot-testcontainers")
testImplementation("io.rest-assured:rest-assured")
}Recipe 4: Annotation Processor
<!-- Maven — Lombok + MapStruct cùng nhau -->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.5.5.Final</version>
</path>
<!-- Lombok-MapStruct binding -->
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>// Gradle — đơn giản hơn nhiều
dependencies {
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
implementation("org.mapstruct:mapstruct:1.5.5.Final")
annotationProcessor("org.mapstruct:mapstruct-processor:1.5.5.Final")
annotationProcessor("org.projectlombok:lombok-mapstruct-binding:0.2.0")
}Best Practices
1. Để BOM Quản Lý Version
<!-- ✅ Đúng — version managed bởi Spring Boot BOM -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- ❌ Sai — hardcode version có thể gây conflict -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.0</version>
</dependency>Chỉ override version khi bạn có lý do cụ thể (security fix, bug fix, feature mới).
2. Dùng Đúng Scope
<!-- ✅ Đúng — PostgreSQL driver chỉ cần lúc runtime -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- ❌ Sai — default compile scope expose driver API không cần thiết -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>3. Giữ Dependency Tối Thiểu
<!-- ✅ Đúng — chỉ những gì bạn cần -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ❌ Sai — pull vào mọi thứ "phòng khi cần" -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>4. Audit Thường Xuyên
# Maven: tìm unused và undeclared dependency
mvn dependency:analyze
# Output:
# [WARNING] Unused declared dependencies:
# org.springframework.boot:spring-boot-starter-mail:jar:3.4.3:compile
# [WARNING] Used undeclared dependencies:
# org.springframework:spring-context:jar:6.2.3:compile- Unused declared: Bạn declare nhưng không dùng — xóa đi
- Used undeclared: Bạn dùng nhưng chỉ có được qua transitive — declare rõ ràng
5. Pin Version Cho Third-Party Library
Với library KHÔNG được Spring Boot BOM quản lý:
<!-- ✅ Đúng — version rõ ràng trong properties -->
<properties>
<springdoc.version>2.3.0</springdoc.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
</dependencies>6. Upgrade Spring Boot Có Hệ Thống
# 1. Check version hiện tại
mvn help:evaluate -Dexpression=project.parent.version -q -DforceStdout
# 2. Update parent version trong pom.xml (ví dụ 3.3.x → 3.4.x)
# 3. Check deprecated API
mvn compile -Xlint:deprecation
# 4. Chạy full test suite
mvn test
# 5. Check dependency tree cho conflict
mvn dependency:tree -Dverbose
# 6. Review release note cho breaking change7. Dùng Maven Wrapper hoặc Gradle Wrapper
Luôn commit wrapper để đảm bảo build tool version nhất quán trong team:
# Maven — generate wrapper
mvn wrapper:wrapper -Dmaven=3.9.6
# Dùng wrapper thay vì global Maven
./mvnw clean install
./mvnw spring-boot:run# Gradle — generate wrapper
gradle wrapper --gradle-version 8.5
# Dùng wrapper thay vì global Gradle
./gradlew clean build
./gradlew bootRunXử Lý Sự Cố Thường Gặp
"ClassNotFoundException" hoặc "NoClassDefFoundError"
Nguyên nhân: Thiếu dependency hoặc sai scope.
# Check xem class có tồn tại trong dependency tree không
mvn dependency:tree -Dincludes=*:*:*:*:* # full tree
# Tìm package/class trong dependencyCách sửa: Add dependency rõ ràng hoặc đổi scope từ test/provided sang compile.
"Multiple Bindings" Trong SLF4J
Nguyên nhân: Nhiều logging implementation trên classpath.
mvn dependency:tree -Dincludes=org.slf4j
mvn dependency:tree -Dincludes=ch.qos.logback
mvn dependency:tree -Dincludes=org.apache.logging.log4jCách sửa: Exclude logging framework không muốn:
<dependency>
<groupId>some-library</groupId>
<artifactId>some-artifact</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>Lỗi "Bean Definition Override"
Nguyên nhân: Hai starter cung cấp cùng auto-configuration bean.
Cách sửa: Check starter nào conflict và exclude một cái, hoặc set:
# application.properties
spring.main.allow-bean-definition-overriding=trueDependency Convergence Failure
Nguyên nhân: Version khác nhau của cùng dependency trong tree.
Maven — enforcer plugin:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>enforce-dependency-convergence</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<dependencyConvergence/>
<requireJavaVersion>
<version>[21,)</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>Tham Khảo Nhanh: Template pom.xml Hoàn Chỉnh
Đây là template pom.xml production-ready với các Spring Boot dependency phổ biế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
https://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>3.4.3</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>my-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>21</java.version>
<springdoc.version>2.3.0</springdoc.version>
<mapstruct.version>1.5.5.Final</mapstruct.version>
</properties>
<dependencies>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Data -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
<!-- Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Monitoring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- API Documentation -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- Utility -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
</dependency>
<!-- Development -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>Tổng Kết
Dependency management của Spring Boot đơn giản hóa một trong những phần khó nhất của Java development — giữ cho library tương thích và cập nhật.
Những điểm chính:
✅ Dùng Spring Boot Starter để có dependency bundle được cấu hình sẵn
✅ Để BOM quản lý version — đừng hardcode
✅ Dùng mvn dependency:tree hoặc ./gradlew dependencies để debug conflict
✅ Exclude transitive dependency khi đổi implementation (ví dụ Tomcat → Jetty)
✅ Set đúng scope (runtime, test, provided) để giữ classpath sạch
✅ Scan vulnerability với OWASP Dependency-Check
✅ Tự động hóa update với Dependabot hoặc Renovate
✅ Luôn dùng Maven/Gradle wrapper cho reproducible build
Bài viết liên quan:
📬 Đă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.