幹掉複雜的工具類,國產Java工具類庫 Hutool 很香!

JavaGuide發表於2023-04-06

Hutool 大家已經比較熟悉了,這是一個超全的 Java 工具庫,深受國內開發者的喜愛。

我之前其實是不太喜歡使用這種功能太多的工具類的,也比較擔心穩定性和安全性,後面慢慢接受了就感覺其實也還好。而且,我們還可以按需只引入自己需要的功能模組,相對也比較靈活。

Hutool 的官方文件介紹的已經比較清晰了,奈何其提供的功能實在太多,我這裡列舉一些我個人覺得比較實用的功能,供大家學習參考。

Hutool 介紹

Hutool 真心是一個不錯的國產 Java 工具類庫,功能全面,對檔案、流、加密解密、轉碼、正則、執行緒、XML 等 JDK 方法進行了封裝,開箱即用!

官方是這樣介紹 Hutool 的:

Hutool 介紹

Hutool 包含的元件以及元件提供的功能如下表所示:

Hutool 包含的元件

你可以根據專案需求對每個模組單獨引入,也可以透過引入hutool-all方式引入所有模組。不過,還是不建議引入所有模組,因為絕大部分功能專案可能都用不上,建議只引入你需要的模組。

另外,Hutool 也有一個比較明顯的缺點,很多功能實現的比較簡單比如圖片驗證碼、Excel 工具類,很可能無法滿足專案的實際需求。像這樣情況,還是建議你選擇在某一方面更優秀的工具庫比如 Excel 工具庫MyExcel、EasyExcel、圖片處理庫Imglib。

Hutool 實戰

引入依賴

Maven 倉庫地址:https://mvnrepository.com/artifact/cn.hutool

這裡為了方便,我們直接引入所有模組,實際專案中還是建議只引入自己需要的模組。

Maven:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>

Gradle:

implementation 'cn.hutool:hutool-all:5.8.16'

功能演示

Hutool 提供的功能實在太多,這裡只列舉一些我個人覺得比較實用的功能,供大家學習參考。

型別轉換

Convert 類封裝了針對Java常見型別的轉換。

long[] b = {1,2,3,4,5};
String bStr = Convert.toStr(b);//"[1, 2, 3, 4, 5]"

double a = 67556.32;
String digitUppercase = Convert.digitToChinese(a);//"陸萬柒仟伍佰伍拾陸元叄角貳分"

郵件

在 Java 中傳送郵件主要品依靠 javax.mail 包,但是由於使用比較繁瑣,因此 Hutool 針對其做了封裝。

在classpath(在標準Maven專案中為src/main/resources)的config目錄下新建mail.setting檔案,完整配置如下(郵件伺服器必須支援並開啟SMTP協議):

# 郵件伺服器的SMTP地址,可選,預設為smtp.<發件人郵箱字尾>
host = smtp.yeah.net
# 郵件伺服器的SMTP埠,可選,預設25
port = 25
# 發件人(必須正確,否則傳送失敗)
from = hutool@yeah.net
# 使用者名稱,預設為發件人郵箱字首
user = hutool
# 密碼(注意,某些郵箱需要為SMTP服務單獨設定授權碼,詳情檢視相關幫助)
pass = q1w2e3

傳送郵件非常簡單:

MailUtil.send("hutool@foxmail.com", "測試", "郵件來自Hutool測試", false);

支援群發:

ArrayList<String> tos = CollUtil.newArrayList(
    "person1@bbb.com", 
    "person2@bbb.com", 
    "person3@bbb.com", 
    "person4@bbb.com");

MailUtil.send(tos, "測試", "郵件來自Hutool群發測試", false);

支援新增一個或者多個附件:

MailUtil.send("hutool@foxmail.com", "測試", "<h1>郵件來自Hutool測試</h1>", true, FileUtil.file("d:/aaa.xml"));

除了使用配置檔案定義全域性賬號以外,MailUtil.send方法同時提供過載方法可以傳入一個MailAccount物件,這個物件為一個普通Bean,記錄了郵件伺服器資訊。

MailAccount account = new MailAccount();
account.setHost("smtp.yeah.net");
account.setPort("25");
account.setAuth(true);
account.setFrom("hutool@yeah.net");
account.setUser("hutool");
account.setPass("q1w2e3");

MailUtil.send(account, CollUtil.newArrayList("hutool@foxmail.com"), "測試", "郵件來自Hutool測試", false);

唯一 ID

在分散式環境中,唯一 ID 生成應用十分廣泛,生成方法也多種多樣,Hutool 針對一些常用生成策略做了簡單封裝。

Hutool 提供的唯一 ID 生成器的工具類,涵蓋了:

  • UUID
  • ObjectId(MongoDB)
  • Snowflake(Twitter)

拿 UUID 舉例!

Hutool 重寫java.util.UUID的邏輯,對應類為cn.hutool.core.lang.UUID,使生成不帶-的 UUID 字串不再需要做字元替換,效能提升一倍左右

//生成的UUID是帶-的字串,類似於:a5c8a5e8-df2b-4706-bea4-08d0939410e3
String uuid = IdUtil.randomUUID();

//生成的是不帶-的字串,類似於:b17f24ff026d40949c85a24f4f375d42
String simpleUUID = IdUtil.simpleUUID();

HTTP 請求工具類

針對最為常用的 GET 和 POST 請求,HttpUtil 封裝了兩個方法,

  • HttpUtil.get
  • HttpUtil.post

GET請求:

// 最簡單的HTTP請求,可以自動透過header等資訊判斷編碼,不區分HTTP和HTTPS
String result1= HttpUtil.get("https://www.baidu.com");

// 當無法識別頁面編碼的時候,可以自定義請求頁面的編碼
String result2= HttpUtil.get("https://www.baidu.com", CharsetUtil.CHARSET_UTF_8);

//可以單獨傳入http引數,這樣引數會自動做URL編碼,拼接在URL中
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("city", "北京");

String result3= HttpUtil.get("https://www.baidu.com", paramMap);

POST請求:

HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("city", "北京");

String result= HttpUtil.post("https://www.baidu.com", paramMap);

檔案上傳:

HashMap<String, Object> paramMap = new HashMap<>();
//檔案上傳只需將引數中的鍵指定(預設file),值設為檔案物件即可,對於使用者來說,檔案上傳與普通表單提交併無區別
paramMap.put("file", FileUtil.file("D:\\face.jpg"));

String result= HttpUtil.post("https://www.baidu.com", paramMap);

快取

Hutool 提供了常見的幾種快取策略的實現:

  1. FIFO(first in first out) :先進先出策略。
  2. LFU(least frequently used) :最少使用率策略。
  3. LRU(least recently used) :最近最久未使用策略。
  4. Timed :定時策略。
  5. Weak :弱引用策略。

並且,Hutool 還支援將小檔案以 byte[] 的形式快取到內容中,減少檔案的訪問,以解決頻繁讀取檔案引起的效能問題。

FIFO(first in first out) 策略快取使用:

Cache<String,String> fifoCache = CacheUtil.newFIFOCache(3);

//加入元素,每個元素可以設定其過期時長,DateUnit.SECOND.getMillis()代表每秒對應的毫秒數,在此為3秒
fifoCache.put("key1", "value1", DateUnit.SECOND.getMillis() * 3);
fifoCache.put("key2", "value2", DateUnit.SECOND.getMillis() * 3);
fifoCache.put("key3", "value3", DateUnit.SECOND.getMillis() * 3);

//由於快取容量只有3,當加入第四個元素的時候,根據FIFO規則,最先放入的物件將被移除
fifoCache.put("key4", "value4", DateUnit.SECOND.getMillis() * 3);

//value1為null
String value1 = fifoCache.get("key1");

控制檯列印封裝

一般情況下,我們列印資訊到控制檯小夥伴們應該再熟悉不過了!

System.out.println("Hello World");

但是,這種方式不滿足很多場景的需要:

  1. 不支援引數,物件列印需要拼接字串
  2. 不能直接列印陣列,需要手動呼叫Arrays.toString

為此,Hutool 封裝了Console物件。

Console物件的使用更加類似於 Javascript 的console.log()方法,這也是借鑑了 JS 的一個語法糖。

String[] a = {"java", "c++", "c"};
Console.log(a);//控制檯輸出:[java, c++, c]

Console.log("This is Console log for {}.", "test");//控制檯輸出:This is Console log for test.

加密解密

Hutool 支援對稱加密、非對稱加密、摘要加密、訊息認證碼演算法、國密。

這裡以國密為例,Hutool針對Bouncy Castle做了簡化包裝,用於實現國密演算法中的SM2、SM3、SM4。

國密演算法需要引入Bouncy Castle庫的依賴:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15to18</artifactId>
    <version>1.69</version>
</dependency>

SM2 使用自定義金鑰對加密或解密

String text = "JavaGuide:一份涵蓋大部分 Java 程式設計師所需要掌握的核心知識。準備 Java 面試,首選 JavaGuide!";
System.out.println("原文:" + text);

KeyPair pair = SecureUtil.generateKeyPair("SM2");
// 公鑰
byte[] privateKey = pair.getPrivate().getEncoded();
// 私鑰
byte[] publicKey = pair.getPublic().getEncoded();

SM2 sm2 = SmUtil.sm2(privateKey, publicKey);
// 公鑰加密,私鑰解密
String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
System.out.println("加密後:" + encryptStr);

String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));
System.out.println("解密後:" + decryptStr);

SM2 簽名和驗籤

//加簽
String sign = sm2.signHex(HexUtil.encodeHexStr(text));
System.out.println("簽名:" + sign);
//驗籤
boolean verify = sm2.verifyHex(HexUtil.encodeHexStr(text), sign);
System.out.println("驗籤:" + verify);

輸出結果:

執行緒池

Hutool 支援使用建造者的模式建立自定義執行緒池,這樣看著更加清晰。

private static ExecutorService pool = ExecutorBuilder.create()
              .setCorePoolSize(10)//初始池大小
              .setMaxPoolSize(20) //最大池大小
              .setWorkQueue(new LinkedBlockingQueue<>(100))//最大等待數為100
              .setThreadFactory(ThreadFactoryBuilder.create().setNamePrefix("IM-Pool-").build())// 執行緒池命名
              .build();

實際專案中,如果一個物件的屬性比較多,有限考慮使用建造者模式建立物件。

並且,Hutool 還提供一個全域性的執行緒池,預設所有非同步方法在這個執行緒池中執行。

  • ThreadUtil.execute : 直接在公共執行緒池中執行執行緒
  • ThreadUtil.execAsync: 執行非同步方法
  • ......

Hutool 自身就大量用到了 ThreadUtil,比如敏感詞工具類 SensitiveUtil

public static void init(final Collection<String> sensitiveWords, boolean isAsync){
  if(isAsync){
    // 非同步初始化敏感詞樹
    ThreadUtil.execAsync(new Callable<Boolean>(){
      @Override
      public Boolean call() throws Exception {
        init(sensitiveWords);
        return true;
      }
      
    });
  }else{
    // 同步初始化敏感詞樹
    init(sensitiveWords);
  }
}

相關地址

相關文章