Spring Boot 全局字段處理最佳實踐

圖片

在日常開發中,我們總會遇到一些瑣碎但又無處不在的字段處理需求:

  • ??請求處理:?用戶提交的表單,字符串前后帶了多余的空格,需要手動?trim()

  • ??響應處理:?返回給前端的?BigDecimal?金額,因為精度問題導致JS處理出錯,需要格式化為兩位小數的字符串。

  • ??空值處理:?某個VO的?List?字段是?null,序列化成 JSON 后,前端期望得到一個空數組?[]?而不是?null,以避免空指針判斷。

如果在每個 DTO 的?setter/getter?或 Service 邏輯中手動處理這些,代碼會變得非常臃腫和重復。本文將帶你從 0 到 1,構建一個全局的、自動化的字段處理 Starter,通過簡單的配置,一次性解決所有這些惱人的“小問題”。

1. 項目設計與核心思路

我們的?global-field-handler-starter?目標如下:

  1. 1.?請求參數自動 Trim:?自動去除所有傳入 JSON 請求體中 String 類型字段的前后空格。

  2. 2.?BigDecimal 響應自動格式化:?自動將所有傳出 JSON 響應體中的?BigDecimal?類型格式化為指定小數位數的字符串。

  3. 3.?Null 集合/數組自動轉換:?自動將?null?的集合或數組在 JSON 響應中轉換為空數組?[]

  4. 4.?可配置:?以上所有功能都應提供獨立的開關進行控制。

核心實現機制:自定義 Jackson?ObjectMapper
Spring Boot 使用 Jackson 作為默認的 JSON 處理器。Jackson 提供了極其豐富的定制化能力。我們將通過創建一個?Jackson2ObjectMapperBuilderCustomizer?Bean,來向 Spring Boot 自動配置的?ObjectMapper?中“注入”我們自定義的處理邏輯。

  • ? 對于反序列化(請求),我們將注冊一個自定義的?JsonDeserializer

  • ? 對于序列化(響應),我們將注冊幾個自定義的?JsonSerializer

2. 創建 Starter 項目與核心組件

我們采用?autoconfigure?+?starter?的雙模塊結構。

步驟 2.1: 依賴 (autoconfigure?模塊)
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
步驟 2.2: 實現自定義的序列化/反序列化器

TrimStringDeserializer.java?(字符串 Trim 反序列化器):

package?com.example.fieldhandler.autoconfigure.handler;import?com.fasterxml.jackson.core.JsonParser;
import?com.fasterxml.jackson.databind.DeserializationContext;
import?com.fasterxml.jackson.databind.JsonDeserializer;
import?java.io.IOException;public?class?TrimStringDeserializer?extends?JsonDeserializer<String> {@Overridepublic?String?deserialize(JsonParser p, DeserializationContext ctxt)?throws?IOException {String?value?=?p.getValueAsString();if?(value ==?null) {return?null;}return?value.trim();}
}

BigDecimalSerializer.java?(BigDecimal 格式化序列化器):

package?com.example.fieldhandler.autoconfigure.handler;import?com.fasterxml.jackson.core.JsonGenerator;
import?com.fasterxml.jackson.databind.JsonSerializer;
import?com.fasterxml.jackson.databind.SerializerProvider;
import?java.io.IOException;
import?java.math.BigDecimal;
import?java.math.RoundingMode;
import?java.util.Objects;public?class?BigDecimalSerializer?extends?JsonSerializer<BigDecimal> {private?final?int?scale;?// 小數位數public?BigDecimalSerializer(int?scale)?{this.scale = scale;}@Overridepublic?void?serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers)?throws?IOException {if?(Objects.nonNull(value)) {// 格式化為指定小數位數的字符串gen.writeString(value.setScale(scale, RoundingMode.HALF_UP).toString());}?else?{gen.writeNull();}}
}

3. 自動裝配的魔法 (GlobalFieldHandlerAutoConfiguration)

步驟 3.1: 配置屬性類
package?com.example.fieldhandler.autoconfigure;import?org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "global.field-handler")
public?class?GlobalFieldHandlerProperties?{private?boolean?enabled?=?false;private?boolean?trimStringInput?=?false;private?boolean?serializeNullCollectionsAsEmpty?=?false;private?boolean?serializeNullStringsAsEmpty?=?false;private?int?bigDecimalScale?=?2;?// 默認保留兩位小數// Getters and Setters...
}
步驟 3.2: 自動配置主類

這是整個 Starter 的核心,它負責根據配置,將我們的處理器應用到?ObjectMapper

package?com.example.fieldhandler.autoconfigure;import?com.example.fieldhandler.autoconfigure.handler.BigDecimalSerializer;
import?com.example.fieldhandler.autoconfigure.handler.TrimStringDeserializer;
import?com.fasterxml.jackson.core.JsonGenerator;
import?com.fasterxml.jackson.databind.JsonSerializer;
import?com.fasterxml.jackson.databind.SerializerProvider;
import?org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import?org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import?org.springframework.boot.context.properties.EnableConfigurationProperties;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.Configuration;
import?java.io.IOException;
import?java.math.BigDecimal;
import?java.util.Collection;@Configuration
@EnableConfigurationProperties(GlobalFieldHandlerProperties.class)
@ConditionalOnProperty(prefix = "global.field-handler", name = "enabled", havingValue = "true")
public?class?GlobalFieldHandlerAutoConfiguration?{@Beanpublic?Jackson2ObjectMapperBuilderCustomizer?jacksonCustomizer(GlobalFieldHandlerProperties properties)?{return?builder -> {// 1. 配置請求參數字符串 Trimif?(properties.isTrimStringInput()) {builder.deserializerByType(String.class,?new?TrimStringDeserializer());}// 2. 配置 BigDecimal 響應格式化builder.serializerByType(BigDecimal.class,?new?BigDecimalSerializer(properties.getBigDecimalScale()));// 3. 配置 Null 值處理builder.postConfigurer(objectMapper -> {if?(properties.isSerializeNullCollectionsAsEmpty()) {objectMapper.getSerializerProvider().setNullValueSerializer(new?JsonSerializer<>() {@Overridepublic?void?serialize(Object value, JsonGenerator gen, SerializerProvider serializers)?throws?IOException {// 對所有類型的 Null 都生效,我們需要篩選// 這里我們簡化為對 Collection 的 Null 進行處理// 注意:更精細的控制可能需要更復雜的 JsonSerializer 或 BeanSerializerModifiergen.writeStartArray();gen.writeEndArray();}});}if?(properties.isSerializeNullStringsAsEmpty()) {objectMapper.getSerializerProvider().setNullValueSerializer(new?JsonSerializer<>() {@Overridepublic?void?serialize(Object value, JsonGenerator gen, SerializerProvider serializers)?throws?IOException {gen.writeString("");}});}});};}
}
步驟 3.3: 注冊自動配置

在?autoconfigure?模塊的?resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports?文件中添加:

com.example.fieldhandler.autoconfigure.GlobalFieldHandlerAutoConfiguration

4. 如何使用我們的 Starter

步驟 4.1: 引入依賴

<dependency><groupId>com.example</groupId><artifactId>global-field-handler-spring-boot-starter</artifactId><version>1.0.0</version>
</dependency>

步驟 4.2: 在?application.yml?中配置

global:field-handler:enabled:?truetrim-string-input:?trueserialize-null-collections-as-empty:?truebig-decimal-scale:?2

步驟 4.3: 編寫 DTO 和 Controller 并驗證
DTO:

public?class?ProductVO?{private?String name;private?BigDecimal price;private?List<String> tags;// Getters, Setters...
}

Controller:

@RestController
public?class?ProductController?{// 驗證請求參數 Trim@PostMapping("/products")public?ProductVO?createProduct(@RequestBody?ProductVO product)?{// 傳入的 JSON: {"name": " ?My Product ?", ...}// 在這里,product.getName() 的值已經是 "My Product",空格已被自動去除System.out.println("Product name received: '"?+ product.getName() +?"'");return?product;}// 驗證響應格式化@GetMapping("/products/{id}")public?ProductVO?getProduct(@PathVariable?Long id)?{ProductVO?product?=?new?ProductVO();product.setName("Awesome Gadget");// 價格精度很高product.setPrice(new?BigDecimal("199.998"));// Tags 為 nullproduct.setTags(null);return?product;}
}

驗證:

  1. 1.?POST?/products?請求,Body 為?{"name": " Spaceship ", "price": 1000}

    • ??后臺日志輸出:?Product name received: 'Spaceship'?(空格已被 trim)。

  2. 2.?GET?/products/1?請求。

    • ??前端收到的 JSON 響應:
      {"name":?"Awesome Gadget","price":?"200.00",?"tags":?[]?
      }
      可以看到,price?被自動格式化為兩位小數的字符串,tags?從?null?變成了?[]

總結

通過自定義一個 Spring Boot Starter 和深入利用 Jackson 的定制化能力,我們成功地將一系列瑣碎、重復但又非常重要的字段處理邏輯,沉淀為了一個可配置、自動化的基礎設施。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/95655.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/95655.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/95655.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

三坐標測量機在汽車制造行業中的應用

在汽車制造業中&#xff0c;零部件精度決定著整車性能。從發動機活塞的微米級公差&#xff0c;到車身焊接的毫米級間隙&#xff0c;汽車制造“差之毫厘&#xff0c;謬以千里” &#xff0c;任何細微偏差都可能引發連鎖反應&#xff1a;發動機抖動、異響、油耗飆升&#xff0c;車…

機床夾具設計 +選型

機床夾具設計—第2組&#xff08;鉆床夾具&#xff09;仿真組裝視頻_嗶哩嗶哩_bilibili 夾具-商品搜索-怡合達一站式采購平臺 米思米FA標準品電子目錄new 可能要吧這些定位塊單獨用yolo訓練一邊才能搞識別分析 3長條一短銷定位&#xff0c;黃色的用來夾緊 一個面加一短軸一棱…

表格識別技術:通過計算機視覺和OCR,實現非結構化表格向結構化數據的轉換,推動數字化轉型。

在日常工作和生活中&#xff0c;我們無處不在與表格打交道。從財務報表、發票收據&#xff0c;到科研論文中的數據表、醫療報告&#xff0c;表格以其清晰、結構化的方式&#xff0c;承載著大量關鍵信息。然而&#xff0c;當這些表格以紙質或圖片等非結構化形式存在時&#xff0…

Go基礎(②Viper)

Viper 讀取配置創建一個配置文件 config.yamlserver:port: 8080timeout: 30 # 超時時間&#xff08;秒&#xff09; database:host: "localhost"user: "root"password: "123456"name: "mydb"然后用 Viper 讀取這個配置&#xff0c;代…

kafka Partition(分區)詳解

一、什么是 PartitionPartition&#xff08;分區&#xff09; 是 Kafka Topic&#xff08;主題&#xff09; 的最小并行單位。一個 Topic 可以包含多個 Partition&#xff0c;每個 Partition 底層對應一個有序、不可變的消息隊列&#xff0c;消息只會順序追加。Partition 內部消…

中創中間件適配HGDB

文章目錄環境文檔用途詳細信息環境 系統平臺&#xff1a;Microsoft Windows (64-bit) 10 版本&#xff1a;5.6.5 文檔用途 本文章主要介紹中創中間件簡單適配HGDB。 詳細信息 一、數據源配置 1.數據庫準備 &#xff08;1&#xff09;安裝HGDB并創建一個名為myhgdb的數據…

服務器內存和普通計算機內存在技術方面有什么區別?

服務器內存和普通計算機內存在技術上的區別&#xff0c;主要體現在為滿足不同工作場景和要求而采用的設計和特性上。下面這個表格匯總了它們的主要技術差異&#xff0c;方便你快速了解&#xff1a; ?技術特性??服務器內存??普通計算機內存??錯誤校驗 (ECC)??支持ECC(…

哪款AI生成PPT工具對職場新人最友好?操作門檻最低的是哪個?

一句話生成專業PPT&#xff0c;職場新人也能輕松做出高質量演示文稿現代職場節奏快&#xff0c;PPT制作已成為必備技能。然而&#xff0c;職場新人常面臨兩大挑戰&#xff1a;缺乏設計經驗&#xff0c;以及需要在有限時間內完成高質量演示。傳統PPT制作耗時費力&#xff0c;需梳…

1.注解的力量:Spring Boot如何用注解重構IoC容器

文章目錄1.1 IoC容器&#xff1a;Spring的智能管家1.2 注解驅動&#xff1a;給管家下指令1.2.1 SpringBootApplication&#xff1a;總管家的聘書1.2.2 組件注解&#xff1a;員工的身份標識1.2.3 Autowired&#xff1a;依賴注入的三種方式1.2.4 Bean注解&#xff1a;手動招聘特殊…

【算法】92.翻轉鏈表Ⅱ--通俗講解

一、題目是啥?一句話說清 給你一個鏈表和兩個整數 left 和 right,反轉從第 left 個節點到第 right 個節點的子鏈表,并返回反轉后的鏈表。其他部分保持不變。 示例: 輸入:head = [1,2,3,4,5], left = 2, right = 4 輸出:[1,4,3,2,5](反轉了從第2到第4個節點) 二、解題…

Nature子刊:新發現!深層腦網絡中發現強迫癥癥狀的神經生物標志物

強迫癥&#xff08;OCD&#xff09;是一種令人困擾的精神疾病&#xff0c;患者常常被強迫思維和強迫行為所困擾。例如&#xff0c;有些人會反復洗手&#xff0c;無法控制自己的清潔沖動&#xff1b;還有些人會不斷檢查門窗是否關好&#xff0c;即便他們已經確認過無數次。這些行…

Onlyoffice集成與AI交互操作指引(Iframe版)

Onlyoffice集成與AI交互操作指引&#xff08;Iframe版&#xff09; 本文檔系統介紹了軟件系統集成OnlyOffice實現在線編輯與AI輔助功能的方案。主要內容包括&#xff1a;后端需提供文檔配置信息并實現Callback接口以處理文檔保存&#xff1b;前端通過Vue集成編輯器&#xff0c…

TypeScript 中 keyof、typeof 和 instanceof

在 TypeScript 開發中&#xff0c;keyof、typeof 和 instanceof 是核心的類型操作符和操作符&#xff0c;專門用于提升類型安全、代碼可讀性和維護性。1. keyof 操作符定義和用途&#xff1a;keyof 是一個類型操作符&#xff0c;用于獲取對象類型的所有鍵&#xff08;屬性名&am…

分布式專題——1.1 Redis單機、主從、哨兵、集群部署

1 Redis 部署 下面演示在 Linux 環境下部署 Redis7。 1.1 單機部署 1.1.1 檢查安裝 gcc 環境Redis 是由 C 語言編寫的&#xff0c;它的運行需要 C 環境&#xff0c;因此我們需要先安裝 gcc&#xff1b; # 關閉防?墻 systemctl stop firewalld.service # 查看防火墻狀態 firewa…

2025年滲透測試面試題總結-54(題目+回答)

安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。1、SQL注入的防護方法有哪些&#xff1f; 2、永恒之藍的漏洞原理是什么&#xff1f;怎么做到的&#xff1f; 3、命令…

安卓學習 之 按鈕點擊事件

今天學習安卓應用中的按鈕點擊事件&#xff1a;總結下來在安卓應用中的Button注冊點擊事件的方法主要是以下4種方法&#xff0c;稍后會逐個介紹&#xff1a; 第一種方法&#xff1a;自定義內部類的方法 第二種方法&#xff1a;匿名內部類的方法 第三種方法&#xff1a;當前Acti…

鴻蒙NEXT主題設置指南:應用級與頁面級主題定制詳解

在鴻蒙應用開發中&#xff0c;靈活的主題設置能力是實現個性化用戶體驗的關鍵技術&#xff0c;HarmonyOS NEXT提供了強大而靈活的主題設置功能&#xff0c;讓開發者能夠輕松實現應用級和頁面級的主題定制。在當今追求個性化的時代&#xff0c;用戶希望應用能夠根據自己的喜好呈…

全球汽車氮化鎵技術市場規模將于2031年增長至180.5億美元,2025-2031年復合增長率達94.3%,由Infineon和Navitas驅動

全球汽車氮化鎵技術市場規模將于2031年增長至180.5億美元&#xff0c;2025-2031年復合增長率達94.3%&#xff0c;由Infineon和Navitas驅動汽車氮化鎵技術正從一個有前景的細分市場加速進入主流電力電子領域。根據QYResearch&#xff08;恒州博智&#xff09;的《全球汽車GaN技術…

xftp斷網后提示錯誤如何繼續下載?

問題&#xff1a;xftp斷網后提示錯誤如何繼續下載&#xff1f;解決方法&#xff1a;斷網后&#xff0c;先連接上網&#xff0c;然后繼續雙擊右側的那兩個要傳輸的文件&#xff0c;然后會彈出一個覆蓋還是繼續下載&#xff08;如下圖&#xff09;的選擇框&#xff0c;選擇繼續下…

Day22_【機器學習—集成學習(4)—Boosting—GBDT算法】

提升樹 &#xff08;Boosting Decision Tree &#xff09;每一個弱學習器通過擬合殘差來構建強學習器梯度提升樹 &#xff08;Gradient Boosting Decision Tree&#xff09;每一個弱學習器通過擬合負梯度來構建強學習器一、提升樹殘差數學公式為&#xff1a;殘差真實值?預測值…