高效!Spring Boot 3.3 BOM 助你輕松簡化微服務開發
在微服務架構的開發中,服務模塊化和獨立性的要求使得項目的依賴管理變得愈加復雜。每個服務可能依賴多個第三方庫,并且隨著時間的推移,庫的版本更新往往會導致兼容性問題。對于開發者來說,確保所有服務中的依賴項版本一致、避免版本沖突、提升開發效率是一個重要的挑戰。
為了解決這個問題,Spring Boot 3.3 引入了 BOM(Bill of Materials,材料清單)功能。BOM 可以幫助開發者通過一個統一的版本管理策略來解決微服務中的依賴沖突問題,使開發者能夠更專注于業務邏輯開發,而不必為復雜的依賴管理操心。
在本文中,我們將深入探討 BOM 在 Spring Boot 3.3 中的作用,并結合一個典型的微服務示例,展示如何通過 BOM 實現依賴管理的簡化。我們將演示如何構建一個簡單的庫存管理系統(Inventory Management System),實現商品的增刪查改(CRUD)操作,前端通過 jQuery 發起異步請求(AJAX)來與后端交互。通過此項目展示 Spring Boot BOM 如何在微服務架構下簡化開發流程。
什么是材料清單(BOM)?
BOM(Bill of Materials)是一種用于聲明和管理依賴庫版本的機制。通過 BOM,項目的所有依賴庫版本可以統一由 BOM 文件來定義和管理。BOM 的核心作用是提供一組經過測試和兼容的依賴庫版本,避免開發者在項目中手動指定每個庫的版本。
BOM 的特點如下:
- 統一版本控制:BOM 可以統一管理項目中的所有依賴項版本,從而避免不同庫版本之間的沖突。
- 簡化依賴聲明:引入 BOM 后,開發者可以省略每個依賴項的版本聲明,BOM 會自動提供版本信息。
- 保障項目穩定性:使用 BOM 可以確保依賴項之間的版本兼容性,減少不兼容問題導致的系統故障。
在 Spring Boot 中,BOM 是通過 dependencyManagement 元素來引入的,提供了對 Spring 生態系統和其他常用庫的統一版本管理。
dependencyManagement 是 Maven 項目中用于管理依賴版本的一個關鍵元素。它主要用于定義一組共享的依賴版本,而不必在子模塊中重復定義這些依賴的版本號。這對于大型項目,尤其是微服務架構中多個模塊共享相同依賴時非常有用。
使用 dependencyManagement 可以集中管理依賴版本,確保在多模塊項目中所有子模塊依賴同一個版本的庫,避免由于版本沖突導致的問題。
dependencyManagement 的主要功能
- 統一版本控制:通過在父 pom.xml 中定義依賴項及其版本號,所有子模塊可以引用這些依賴項,而無需單獨指定版本號。
- 靈活性:dependencyManagement 并不會自動將依賴引入子模塊。它只提供版本信息,具體的依賴還需要在子模塊中顯式地聲明。
- 避免重復:在多模塊項目中,不需要在每個子模塊的 pom.xml 文件中重復定義依賴項及其版本號,只需要在父模塊的 pom.xml 文件中進行統一管理。
為什么要在微服務中使用 BOM?
在微服務架構中,多個服務通常使用相似的依賴庫,但由于服務獨立開發和部署,不同服務中可能會使用不同版本的庫。這種版本差異可能會導致服務之間的兼容性問題,甚至引發系統故障。
使用 BOM 在微服務架構中有以下好處:
- 避免版本沖突:通過 BOM,所有微服務的依賴項可以使用相同版本的庫,從而避免版本沖突導致的兼容性問題。
- 簡化維護:在多個微服務項目中,如果依賴項版本發生變化,開發者只需在 BOM 中進行一次修改,所有服務都會自動繼承新的依賴版本。
- 提升開發效率:BOM 提供了預先配置好的、經過測試的依賴版本組合,開發者可以專注于功能開發,而不必為版本管理分心。
運行效果:
圖片
若想獲取項目完整代碼以及其他文章的項目源碼,且在代碼編寫時遇到問題需要咨詢交流,歡迎加入下方的知識星球。
項目示例:庫存管理系統
為了更好地演示 BOM 的作用,我們將開發一個典型的微服務示例——庫存管理系統。該系統允許用戶通過前端界面添加、刪除、修改和查詢商品的庫存信息。系統包括以下主要功能:
- 前端:使用 Thymeleaf 模板引擎結合 jQuery 和 Bootstrap 實現界面交互。
- 后端:Spring Boot 提供 RESTful API 進行 CRUD 操作。
- 數據庫:使用 H2 內存數據庫進行數據存儲。
項目 pom.xml 配置
在項目的 pom.xml 文件中,我們首先引入 Spring Boot 3.3 的 BOM 以管理項目依賴:
<?xml versinotallow="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.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>inventory-management</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>inventory-management</name>
<description>Demo project for Spring Boot</description>
<!-- 引入 Spring Boot 3.3 的 BOM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.3.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Thymeleaf 模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- H2 數據庫 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</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>
配置文件 application.yml
我們使用 application.yml 來配置應用的端口和其他基礎設置。
server:
port: 8080
app:
name: 庫存管理系統
description: 使用 Spring Boot 3.3 BOM 簡化開發
讀取配置類
通過 @ConfigurationProperties 注解,我們可以將配置文件中的值映射到 Java 類中。
package com.icoderoad.inventory.management.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private String description;
}
商品實體類
使用 Lombok 簡化實體類的編寫,定義商品的基本屬性:
package com.icoderoad.inventory.management.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private Long id;
private String name;
private int quantity;
private double price;
}
控制器和 API 實現
控制器提供增刪查改的 RESTful API,并返回商品數據。示例代碼如下:
package com.icoderoad.inventory.management.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.icoderoad.inventory.management.entity.Product;
@RestController
@RequestMapping("/api/products")
public class ProductController {
private List<Product> products = new ArrayList<>();
private AtomicLong counter = new AtomicLong();
@GetMapping
public List<Product> getAllProducts() {
return products;
}
@GetMapping("/{id}")
public Product getProductById(@PathVariable Long id) {
return products.stream().filter(p -> p.getId().equals(id)).findFirst().orElse(null);
}
@PostMapping
public Product addProduct(@RequestBody Product product) {
product.setId(counter.incrementAndGet());
products.add(product);
return product;
}
@PutMapping("/{id}")
public Product updateProduct(@PathVariable Long id, @RequestBody Product updatedProduct) {
Product product = products.stream().filter(p -> p.getId().equals(id)).findFirst().orElse(null);
if (product != null) {
product.setName(updatedProduct.getName());
product.setQuantity(updatedProduct.getQuantity());
product.setPrice(updatedProduct.getPrice());
}
return product;
}
@DeleteMapping("/{id}")
public void deleteProduct(@PathVariable Long id) {
products.removeIf(p -> p.getId().equals(id));
}
}
前端 Thymeleaf 模板及 jQuery 調用
前端頁面通過 jQuery 發起 AJAX 請求,展示商品列表并支持增刪改操作。
在 src/main/resources/templates 目錄下創建 index.html 文件:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>庫存管理系統</title>
<link rel="stylesheet" >
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
</head>
<body>
<div class="container mt-4">
<h1>庫存管理系統</h1>
<!-- 添加商品按鈕 -->
<button class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#productModal" id="addProductBtn">
添加商品
</button>
<!-- 商品表格 -->
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>商品名稱</th>
<th>數量</th>
<th>價格</th>
<th>操作</th>
</tr>
</thead>
<tbody id="productTable"></tbody>
</table>
<!-- 商品模態框 -->
<div class="modal fade" id="productModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalTitle">添加商品</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="productForm">
<input type="hidden" id="productId">
<div class="mb-3">
<label for="productName" class="form-label">商品名稱</label>
<input type="text" class="form-control" id="productName" required>
</div>
<div class="mb-3">
<label for="productQuantity" class="form-label">數量</label>
<input type="number" class="form-control" id="productQuantity" required>
</div>
<div class="mb-3">
<label for="productPrice" class="form-label">價格</label>
<input type="number" step="0.01" class="form-control" id="productPrice" required>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">關閉</button>
<button type="button" class="btn btn-primary" id="saveProductBtn">保存</button>
</div>
</div>
</div>
</div>
</div>
<script>
// 加載商品列表
function loadProducts() {
$.getJSON('/api/products', function (data) {
let rows = '';
data.forEach(function (product) {
rows += `<tr>
<td>${product.id}</td>
<td>${product.name}</td>
<td>${product.quantity}</td>
<td>${product.price}</td>
<td>
<button class="btn btn-warning btn-sm edit-btn" data-id="${product.id}">編輯</button>
<button class="btn btn-danger btn-sm delete-btn" data-id="${product.id}">刪除</button>
</td>
</tr>`;
});
$('#productTable').html(rows);
});
}
// 添加商品按鈕
$('#addProductBtn').click(function () {
$('#modalTitle').text('添加商品');
$('#productForm')[0].reset();
$('#productId').val('');
});
// 保存商品(添加或編輯)
$('#saveProductBtn').click(function () {
const id = $('#productId').val();
const productData = {
name: $('#productName').val(),
quantity: $('#productQuantity').val(),
price: $('#productPrice').val()
};
if (id) {
// 編輯商品
$.ajax({
url: '/api/products/' + id,
type: 'PUT',
contentType: 'application/json',
data: JSON.stringify(productData),
success: function () {
$('#productModal').modal('hide');
loadProducts();
}
});
} else {
// 添加商品
$.ajax({
url: '/api/products',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(productData),
success: function () {
$('#productModal').modal('hide');
loadProducts();
}
});
}
});
// 編輯按鈕點擊事件
$(document).on('click', '.edit-btn', function () {
const id = $(this).data('id');
$.getJSON('/api/products/' + id, function (product) {
$('#modalTitle').text('編輯商品');
$('#productId').val(product.id);
$('#productName').val(product.name);
$('#productQuantity').val(product.quantity);
$('#productPrice').val(product.price);
$('#productModal').modal('show');
});
});
// 刪除商品
$(document).on('click', '.delete-btn', function () {
const id = $(this).data('id');
$.ajax({
url: '/api/products/' + id,
type: 'DELETE',
success: function () {
loadProducts();
}
});
});
// 頁面加載時加載商品列表
$(document).ready(function () {
loadProducts();
});
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
總結
通過 Spring Boot 3.3 的 BOM,我們成功簡化了庫存管理系統的依賴管理流程。在微服務開發中,BOM 的引入能夠統一管理依賴項的版本,避免版本沖突,提升項目的可維護性。在前后端的代碼示例中,我們展示了如何通過 RESTful API 實現增刪查改操作,并結合 jQuery 和 Thymeleaf 實現簡單而直觀的用戶界面。