SpringBoot中通用工具類庫(kù)(Utils)封裝與使用實(shí)踐
一、涉及的技術(shù)知識(shí)點(diǎn)
1.1 空值判斷與防御性編程
| 知識(shí)點(diǎn) | 說明 |
|---|---|
| 多類型空值判斷 | 對(duì) String/Collection/Map/Array/Optional/Object 統(tǒng)一判空 |
| 可變參數(shù)(varargs) | isOrEmpty(Object...) 任一為空返回 true,isAndEmpty(Object...) 全部為空返回 true |
| Null Safety | 替代手寫 != null && !"" 鏈?zhǔn)脚袛?/td> |
1.2 日期時(shí)間處理
| 知識(shí)點(diǎn) | 說明 |
|---|---|
java.util.Date(舊 API) | DateUtil 基于舊 API 兼容存量代碼 |
java.time.LocalDate/LocalDateTime(新 API) | DateTimeUtil 基于 Java 8 時(shí)間 API |
| 智能日期解析 | 根據(jù)字符串長(zhǎng)度自動(dòng)識(shí)別格式(10位時(shí)間戳/13位時(shí)間戳/19位標(biāo)準(zhǔn)格式/23位含毫秒) |
| 正則模式匹配 | 用正則判斷日期字符串格式(標(biāo)準(zhǔn) yyyy-MM-dd/斜杠 yyyy/MM/dd) |
| TemporalAdjusters | 獲取月初/月末/年初/年末/上月/下月等時(shí)間點(diǎn) |
| 時(shí)區(qū)枚舉 | ZoneEnums 定義時(shí)區(qū)常量 |
| 日期格式枚舉 | DateTimeFormat 枚舉統(tǒng)一管理 20+ 種日期格式 |
1.3 JSON 序列化/反序列化
| 知識(shí)點(diǎn) | 說明 |
|---|---|
| Jackson ObjectMapper | 配置好的全局單例,線程安全 |
| TypeReference 泛型反序列化 | 解決 Java 泛型擦除問題 |
| Snake Case 支持 | snakeMapper 自動(dòng)駝峰 ↔ 下劃線轉(zhuǎn)換 |
| 容錯(cuò)處理 | 序列化/反序列化異常不拋出,返回 null 并記錄日志 |
1.4 加密工具
| 知識(shí)點(diǎn) | 說明 |
|---|---|
| AES 對(duì)稱加密 | AesUtil 提供簡(jiǎn)單的 AES 加解密(ECB 模式) |
| MD5 摘要 | Md5Utils 提供 MD5 哈希(簽名校驗(yàn)場(chǎng)景) |
| ThreadLocal MessageDigest | MD5 計(jì)算使用 ThreadLocal 避免多線程競(jìng)爭(zhēng) |
| Hex 編碼 | 字節(jié)數(shù)組轉(zhuǎn)十六進(jìn)制字符串 |
1.5 線程上下文管理
| 知識(shí)點(diǎn) | 說明 |
|---|---|
| ThreadLocal | Auth2SessionIdUtil 使用 ThreadLocal 存儲(chǔ)請(qǐng)求級(jí)別的用戶信息 |
| Token 傳遞 | 在請(qǐng)求處理鏈中透?jìng)?OAuth2 Token |
| 請(qǐng)求級(jí)隔離 | 每個(gè)請(qǐng)求有獨(dú)立的 sessionId/token/loginName |
| 清理機(jī)制 | delete() 方法清理 ThreadLocal 防止內(nèi)存泄漏 |
1.6 國(guó)際化(i18n)
| 知識(shí)點(diǎn) | 說明 |
|---|---|
MessageSource | Spring 國(guó)際化消息源 |
| 資源文件 | messages.properties / messages_zh_CN.properties |
| 參數(shù)化消息 | getMsg(key, args...) 支持占位符 |
1.7 網(wǎng)絡(luò)工具
| 知識(shí)點(diǎn) | 說明 |
|---|---|
| 客戶端 IP 獲取 | 從 X-Forwarded-For / X-Real-IP 等 Header 解析 |
| 內(nèi)網(wǎng) IP 判斷 | isIntranetIp() 判斷是否為 10.x/172.16-31.x/192.168.x |
| 主機(jī)名獲取 | getHostName() 獲取當(dāng)前服務(wù)器主機(jī)名 |
| ThreadLocal IP | 請(qǐng)求級(jí)別緩存客戶端 IP |
二、包結(jié)構(gòu)
xxx.xxx.xxx.utils
├── CheckEmptyUtil.java // 空值判斷工具(使用頻率最高)
├── DateUtil.java // 日期工具(舊API,java.util.Date)
├── StringUtil.java // 字符串/JSON工具(序列化+特殊字符處理)
├── JsonUtil.java // JSON 序列化工具(支持 Snake Case)
├── Auth2SessionIdUtil.java // OAuth2 會(huì)話上下文(ThreadLocal)
├── IpUtil.java // IP 地址工具
├── AesUtil.java // AES 加解密工具
├── Md5Utils.java // MD5 摘要工具
├── xxxI18nUtil.java // 國(guó)際化消息工具
├── FileUtil.java // 文件操作工具
├── JasperUtil.java // 報(bào)表導(dǎo)出工具(Jasper)
├── PdfUtil.java // PDF 生成工具
├── ClassNameUtil.java // 類名處理工具
├── xxxSerializationUtils.java // Java 序列化工具
├── PackageUtil.java // 包掃描工具
├── UrlUtil.java // URL 處理工具
├── cloud/
│ ├── AdapterHeader.java // 適配器 Header 工具
│ └── LoginToken.java // 登錄 Token 封裝
└── time/
├── DateTimeUtil.java // 日期時(shí)間工具(新API,java.time)
├── DateTimeFormat.java // 日期格式枚舉(20+種預(yù)定義格式)
├── DateTimeConverter.java // 日期轉(zhuǎn)換器
└── ZoneEnums.java // 時(shí)區(qū)枚舉
無 spring.factories:純工具類庫(kù),不涉及自動(dòng)配置,直接引入靜態(tài)方法調(diào)用。
三、通用示例代碼
3.1 CheckEmptyUtil(空值判斷工具)
package com.example.utils;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
/**
* 通用空值判斷工具.
* 統(tǒng)一處理各種類型的空值判斷,替代業(yè)務(wù)代碼中的 != null && !isEmpty() 鏈?zhǔn)脚袛?
*
* 支持類型:
* - null
* - String(空字符串)
* - Collection(空集合)
* - Map(空Map)
* - Array(空數(shù)組)
* - Optional(空Optional)
* - 其他 Object(僅判null)
*/
public class CheckEmptyUtil {
/**
* 判斷對(duì)象是否為空.
* 根據(jù)實(shí)際類型自動(dòng)選擇判空策略.
*/
public static boolean isEmpty(Object obj) {
if (obj == null) {
return true;
}
if (obj instanceof String) {
return ((String) obj).trim().isEmpty();
}
if (obj instanceof Collection) {
return ((Collection<?>) obj).isEmpty();
}
if (obj instanceof Map) {
return ((Map<?, ?>) obj).isEmpty();
}
if (obj.getClass().isArray()) {
return Array.getLength(obj) == 0;
}
if (obj instanceof Optional) {
return !((Optional<?>) obj).isPresent();
}
return false;
}
/**
* 判斷對(duì)象是否非空.
*/
public static boolean isNotEmpty(Object obj) {
return !isEmpty(obj);
}
/**
* 任一參數(shù)為空返回true.
* 常用于參數(shù)校驗(yàn):if (isOrEmpty(a, b)) throw ...
*/
public static boolean isOrEmpty(Object... objs) {
if (objs == null) {
return true;
}
for (Object obj : objs) {
if (isEmpty(obj)) {
return true;
}
}
return false;
}
/**
* 全部參數(shù)都為空返回true.
* 常用于條件判斷:if (isAndEmpty(a, b)) 都沒傳
*/
public static boolean isAndEmpty(Object... objs) {
if (objs == null) {
return true;
}
for (Object obj : objs) {
if (isNotEmpty(obj)) {
return false;
}
}
return true;
}
}
3.2 DateUtil(舊版日期工具)
package com.example.utils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Pattern;
/**
* 日期工具類(基于 java.util.Date).
*
* 核心特性:
* 1. 智能日期解析 - 根據(jù)字符串格式自動(dòng)識(shí)別
* 2. 常用日期操作 - 月初/月末/日始/日終
* 3. 日期格式化 - 標(biāo)準(zhǔn)格式輸出
*/
public class DateUtil {
private static final Pattern UNIX_TIMESTAMP = Pattern.compile("^\\d{10}$");
private static final Pattern JAVA_TIMESTAMP = Pattern.compile("^\\d{13}$");
private static final String STANDARD_DATETIME = "yyyy-MM-dd HH:mm:ss";
private static final String STANDARD_DATE = "yyyy-MM-dd";
/**
* 智能日期解析.
* 支持:時(shí)間戳(10位/13位)、標(biāo)準(zhǔn)格式、斜杠格式.
*
* @param text 日期字符串
* @return Date 對(duì)象,解析失敗返回 null
*/
public static Date convertToDate(String text) {
if (text == null || text.trim().isEmpty()) {
return null;
}
text = text.trim();
try {
switch (text.length()) {
case 10:
// Unix 時(shí)間戳 或 yyyy-MM-dd
if (UNIX_TIMESTAMP.matcher(text).matches()) {
return new Date(Long.parseLong(text) * 1000);
}
return new SimpleDateFormat(STANDARD_DATE).parse(text);
case 13:
// Java 時(shí)間戳
if (JAVA_TIMESTAMP.matcher(text).matches()) {
return new Date(Long.parseLong(text));
}
return null;
case 19:
// yyyy-MM-dd HH:mm:ss
return new SimpleDateFormat(STANDARD_DATETIME).parse(text);
case 23:
// yyyy-MM-dd HH:mm:ss.SSS
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(text);
default:
return new SimpleDateFormat(STANDARD_DATETIME).parse(text);
}
} catch (Exception e) {
return null;
}
}
/** 獲取日期的起始時(shí)刻(00:00:00.000). */
public static Date getDayBegin(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
/** 獲取日期的結(jié)束時(shí)刻(23:59:59.999). */
public static Date getDayEnd(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 999);
return cal.getTime();
}
/** 格式化為標(biāo)準(zhǔn)日期時(shí)間字符串. */
public static String formatStandardDateTime(Date date) {
if (date == null) return null;
return new SimpleDateFormat(STANDARD_DATETIME).format(date);
}
/** 獲取 N 天后的日期. */
public static Date getDateAfter(Date date, int days) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DAY_OF_MONTH, days);
return cal.getTime();
}
}
3.3 StringUtil(字符串/JSON 工具)
package com.example.utils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 字符串和JSON工具類.
*
* 核心功能:
* 1. JSON 序列化/反序列化(容錯(cuò)模式)
* 2. 特殊字符檢測(cè)和替換(中文/Emoji/符號(hào))
* 3. 駝峰拆分
*/
public class StringUtil {
private static final Logger logger = LoggerFactory.getLogger(StringUtil.class);
private static final Pattern CHINESE_CHAR = Pattern.compile("[\\u4e00-\\u9fa5]");
private static final Pattern EMOJI_CHAR = Pattern.compile("[\\ud800-\\udfff]");
// 全局單例 ObjectMapper(線程安全)
private static final ObjectMapper READ_MAPPER = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
private static final ObjectMapper WRITE_MAPPER = new ObjectMapper()
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
/**
* 對(duì)象序列化為 JSON 字符串.
* 異常時(shí)返回空字符串,不拋出異常.
*/
public static String getJsonString(Object obj) {
if (obj == null) {
return "";
}
if (obj instanceof String) {
return (String) obj;
}
try {
return WRITE_MAPPER.writeValueAsString(obj);
} catch (Exception e) {
logger.error("JSON序列化失敗", e);
return "";
}
}
/**
* JSON 字符串反序列化為對(duì)象.
*/
public static <T> T parseJsonString(String json, Class<T> clazz) {
if (json == null || json.isEmpty()) {
return null;
}
try {
return READ_MAPPER.readValue(json, clazz);
} catch (Exception e) {
logger.error("JSON反序列化失敗", e);
return null;
}
}
/**
* JSON 字符串反序列化(泛型支持).
*/
public static <T> T parseJsonString(String json, TypeReference<T> typeRef) {
if (json == null || json.isEmpty()) {
return null;
}
try {
return READ_MAPPER.readValue(json, typeRef);
} catch (Exception e) {
logger.error("JSON反序列化失敗", e);
return null;
}
}
/** 是否包含中文字符. */
public static boolean containsChinese(String str) {
return str != null && CHINESE_CHAR.matcher(str).find();
}
/** 是否包含 Emoji 字符. */
public static boolean containsEmoji(String str) {
return str != null && EMOJI_CHAR.matcher(str).find();
}
/** 替換 Emoji 為指定字符. */
public static String replaceEmoji(String str, String replacement) {
if (str == null) return null;
return EMOJI_CHAR.matcher(str).replaceAll(replacement);
}
}
3.4 Auth2SessionIdUtil(請(qǐng)求上下文工具)
package com.example.utils;
/**
* OAuth2 請(qǐng)求級(jí)別上下文工具.
* 通過 ThreadLocal 存儲(chǔ)當(dāng)前請(qǐng)求的用戶信息,在同一請(qǐng)求內(nèi)全局可訪問.
*
* 使用場(chǎng)景:
* - Filter/Interceptor 中設(shè)置(請(qǐng)求進(jìn)入時(shí))
* - Service 層中讀?。I(yè)務(wù)處理時(shí))
* - Filter 中清理(請(qǐng)求結(jié)束時(shí))
*
* 注意:必須在請(qǐng)求結(jié)束時(shí)調(diào)用 delete() 清理,防止線程池復(fù)用導(dǎo)致數(shù)據(jù)串線.
*/
public final class Auth2SessionIdUtil {
private static final ThreadLocal<String> sessionIdLocal = new ThreadLocal<>();
private static final ThreadLocal<String> tokenLocal = new ThreadLocal<>();
private static final ThreadLocal<String> loginNameLocal = new ThreadLocal<>();
/** 獲取當(dāng)前請(qǐng)求的會(huì)話ID. */
public static String getSessionId() {
return sessionIdLocal.get();
}
public static void setSessionId(String sessionId) {
sessionIdLocal.set(sessionId);
}
/** 獲取當(dāng)前請(qǐng)求的 OAuth2 Token. */
public static String getToken() {
return tokenLocal.get();
}
public static void setToken(String token) {
tokenLocal.set(token);
}
/** 獲取當(dāng)前登錄用戶名. */
public static String getLoginName() {
return loginNameLocal.get();
}
public static void setLoginName(String loginName) {
loginNameLocal.set(loginName);
}
/**
* 清理所有 ThreadLocal 數(shù)據(jù).
* 必須在請(qǐng)求結(jié)束時(shí)調(diào)用!防止線程池復(fù)用導(dǎo)致數(shù)據(jù)泄露.
*/
public static void delete() {
sessionIdLocal.remove();
tokenLocal.remove();
loginNameLocal.remove();
}
}
3.5 pom.xml(工具庫(kù))
<project>
<groupId>com.example</groupId>
<artifactId>example-utils</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<dependencies>
<!-- JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<!-- Servlet(獲取IP等) -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Spring Context(國(guó)際化) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>provided</scope>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>四、引入方使用
4.1 添加依賴
<dependency>
<groupId>com.example</groupId>
<artifactId>example-utils</artifactId>
<version>1.0.0</version>
</dependency>4.2 使用示例
@Service
public class OrderService {
public void processOrder(OrderDto dto) {
// 參數(shù)校驗(yàn)
if (CheckEmptyUtil.isOrEmpty(dto.getOrderCode(), dto.getMemberId())) {
throw new IllegalArgumentException("參數(shù)不能為空");
}
// 日期處理
Date deliveryDate = DateUtil.convertToDate(dto.getDeliveryTime()); // 自動(dòng)識(shí)別格式
Date deadline = DateUtil.getDateAfter(deliveryDate, 3); // 3天后
// JSON 序列化(記日志)
log.info("處理訂單入?yún)? {}", StringUtil.getJsonString(dto));
// 空值安全操作
if (CheckEmptyUtil.isNotEmpty(dto.getItemList())) {
dto.getItemList().forEach(item -> {
// 業(yè)務(wù)邏輯...
});
}
}
}
五、關(guān)鍵設(shè)計(jì)總結(jié)
| 設(shè)計(jì)要點(diǎn) | 實(shí)現(xiàn)方式 | 收益 |
|---|---|---|
| 多類型統(tǒng)一判空 | instanceof 分發(fā) | 一個(gè)方法覆蓋所有類型,減少重復(fù)代碼 |
| 智能日期解析 | 按字符串長(zhǎng)度 + 正則分發(fā) | 無需調(diào)用方關(guān)心日期格式 |
| JSON 容錯(cuò) | 序列化/反序列化異常返回 null | 不會(huì)因?yàn)橐粋€(gè)字段異常導(dǎo)致整個(gè)請(qǐng)求失敗 |
| 全局 ObjectMapper | 靜態(tài)單例 + 線程安全配置 | 避免每次創(chuàng)建實(shí)例的開銷 |
| ThreadLocal 上下文 | 請(qǐng)求級(jí)隔離用戶信息 | Service 層無需傳參即可獲取當(dāng)前用戶 |
| ThreadLocal 清理 | delete() 方法 | 防止線程池復(fù)用導(dǎo)致數(shù)據(jù)串線 |
| 純靜態(tài)工具類 | 無 spring.factories,無 Bean 注冊(cè) | 任何項(xiàng)目引入即用,零配置 |
| provided scope | 核心依賴由引入方提供 | 不引入版本沖突 |
以上就是SpringBoot中通用工具類庫(kù)(Utils)封裝與使用實(shí)踐的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot通用工具類庫(kù)封裝與使用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java javax.annotation.Resource注解的詳解
這篇文章主要介紹了javax.annotation.Resource注解的詳解的相關(guān)資料,需要的朋友可以參考下2016-10-10
Springboot非分布式定時(shí)任務(wù)實(shí)現(xiàn)代碼
這篇文章主要介紹了Springboot非分布式定時(shí)任務(wù)實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
feign遠(yuǎn)程調(diào)用無法傳遞對(duì)象屬性405的問題
這篇文章主要介紹了feign遠(yuǎn)程調(diào)用無法傳遞對(duì)象屬性405的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java中字符串和byte數(shù)組之間的簡(jiǎn)單轉(zhuǎn)換方法
這篇文章主要給大家介紹了關(guān)于Java中字符串和byte數(shù)組之間的簡(jiǎn)單轉(zhuǎn)換方法,Java中將String類型轉(zhuǎn)換為byte[]類型,可以使用String的getBytes()方法,還有很多其他的辦法,需要的朋友可以參考下2023-08-08
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(59)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-08-08
Java并發(fā)編程volatile關(guān)鍵字的作用
這篇文章主要介紹了Java并發(fā)編程volatile關(guān)鍵字的作用,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07

