高效能團隊的Java研發規範(進階版)

目前大部分團隊是使用的阿里巴巴Java開發規範,不過在日常開發中難免遇到覆蓋不到的場景,本文在阿里巴巴Java開發規範基礎上,補充一些常用的規範,用於提升代碼質量及增強代碼可讀性。

編程規約

1、基礎類型及操作

(1)轉換

基本類型轉換

String類型轉數字:使用apache common-lang3包中的工具類NumberUtils,優勢:可設置默認值,轉換出錯時返回默認值

NumberUtils.toInt("1");

拆箱:包裝類轉化為基本類型的時候,需要判定null,比如:

Integer numObject = param.get(0);
int num = numObject != null ? numObject : 0;
對象類型轉換

使用MapStruct工具,轉換類後綴Convertor,所有轉換操作都在轉換類中操作,禁止在業務代碼中編寫大量set代碼。

(2)判斷

枚舉判定

使用枚舉判等,而非枚舉對應的數字。因為枚舉更直觀,方便查看代碼及調試,數字容易出錯。

判空

各種對象的判空:

//對象判空&非空
Objects.isNull()
Objects.nonNull()

//String判空&非空
StringUtils.isEmpty()   //可匹配null和空字符串
StringUtils.isNotEmpty()
StringUtils.isBlank()   //可匹配null、空字符串、多個空白字符
StringUtils.isNotBlank()

//集合判空&非空
CollectionUtils.isEmpty()
CollectionUtils.isNotEmpty()

//Map判空&非空
MapUtils.isEmpty()
MapUtils.isNotEmpty()
斷言

使用Guava里的Preconditions工具類,比如:

//如果是空則拋異常
Preconditions.checkNotNull()
//通用判斷
Preconditions.checkArgument()

2、集合處理

(1)Map快捷操作

推薦:

//如果值不存在則計算
map.computeIfAbsent("key",k-> execValue(k));
//默認值
map.getOrDefault("key", DEFAULT_VALUE)

反例:

//如果值不存在則計算
String v = map.get("key");
if(v == null){
    v = execValue("key");
    map.put("key", v);
}
//默認值
map.containsKey("key") ? map.get("key") : DEFAULT_VALUE

(2)創建對象

構造方法或Builder模式,超過3個參數對象創建使用Builder模式

//Java11+:
List.of(1, 2, 3)  
Set.of(1, 2, 3)
Map.of("a", 1)

//Java8中不可變集合(需引入Guava)
ImmutableList.of(1,2,3)
ImmutableSet.of(1,2,3)
ImmutableMap.of("key","value")
//多值情況
ImmutableMap.builder()
    .put("key", "value")
    .put("key2", "value2")
    .build()

//Java8中可變集合(需引入Guava)
Lists.newArrayList(1, 2, 3)
Sets.newHashSet(1, 2, 3)
Maps.newHashMap("key", "value")

反例:

new ArrayList<>(){{
   add(1);
   add(2);
}};

(3)集合嵌套

集合里的值如果是基礎類型必須加上注釋,說明集合里存的是什麼,比如:

//返回值: Map(key: 姓名, value: List(商品))
Map<String, List<String>> res;

超過2層集合對象封裝必須封裝成自定義類:

//推薦
Map<String, List<Node>> res;

@Value
public static class Node {
    /**
    * 備註說明字段
    */
    String name;
    /**
    * 備註說明字段2
    */
    List<Integer> subjectIds;
}

//反例
Map<String, List<Pair<String, List<Integer>>>> res;

異常及日誌

1、異常

關於異常及錯誤碼的思考,請參考筆者的另一篇文章:錯誤碼設計思考

異常除了拋異常還有一種場景,即:上層發起多個必要調用,某些可能失敗,需要上層自行決定處理策略,推薦使用vavr中的Either類,Either使用建議:通常我們使用左值表示異常,而右值表示正常調用後的返回結果,即: Either<Throwable, Data>

2、日誌

(1)日誌文件

根據日誌等級一般分為4個日誌文件即可:debug.log、info.log、warn.log、error.log;

如有特殊需求可根據場景單獨建文件,比如請求日誌:request.log、gc日誌:gc.log等。

(2)所有用戶日誌都要有追蹤字段

追蹤字段包括:traceId、userId等,推薦使用MDC,常用的日誌框架:Log4j、Logback都支持。

(3)日誌清理及持久化

本地日誌根據磁盤大小,必須設置日誌保存天數,否則有硬盤滿風險;

分佈式環境為了方便查詢,需要將日誌採集到ES中查詢;

重要日誌:比如審計日誌、B端操作日誌需要持久保存,一般是保存到Hive中;

工具篇

1、JSON

推薦:使用Gson或Jackson;

不推薦:Fastjson。Fastjson爆出的漏洞多。

2、對象轉換

推薦:MapStruct,根據註解編譯成Java代碼,沒有反射,速度快;行為可預測,可查看編譯後的Java代碼查看轉換邏輯;

不推薦:BeanUtils、Dozer等。需要反射,行為不可預測,需要測試;

不推薦:超過3個字段手動轉換;

3、模板代碼

推薦:Lombok,減少代碼行數,提升開發效率,自動生成Java代碼,沒有性能損耗;

不推薦:手動生成大量set、get方法;

4、參數校驗

推薦:hibernate Validation、spring-boot-starter-validation,可通過註解自動實現參數攔截;

不推薦:每個入口(比如Controller)都copy大量重複的校驗邏輯;

5、緩存

推薦:Spring Cache,通過註解控制緩存邏輯,適合常用的加緩存場景。

設計篇

1、正向語義

正向語義的好處在於使代碼容易理解。 比如:if(judge()){….},很容易理解,即:判定成功則執行代碼塊。

相反,如果是負向語義,思維還要轉換一下,一般用於方法前置的參數校驗。

正向語義的應用場景有:

  • 方法定義:方法名推薦:canPass、checkParam,返回true代表成功。 不推薦:比如isInvalidParam返回true代表失敗,增加理解成本;
  • Lambda表達式:filter 操作符中返回true是可以通過的元素;
  • if和三目運算符:condition ? doSomething() : doSomething2() , 條件判定後緊跟的是判定成功後執行的操作。

反例:

if (!judge()) {
   doSomething2()
} else {
   doSomething()
}

2、防禦式編程

(1)外部數據校驗

外部傳過來數據都需要校驗,一般分為兩類:

  • 數據流入:用戶Http請求、RPC請求、MQ消費者等
  • 數據依賴:依賴的第三方RPC、數據庫等

如果是數據流入,一定要首先校驗數據合法性再往下執行,推薦hibernate Validation這類工具,可以很方便的做數據校驗

數據是數據依賴,一定要考慮各種網絡、限流、背壓等場景,做好熔斷、降級保障。推薦建立防腐層,將第三方的限界上下文語義轉換為當前上下文語義,避免理解上的歧義;

(2)Null處理

  • 對於強依賴,沒有返回值不行(比如查詢數據庫):直接拋異常;

  • 需要反饋給上層處理:

    (1)可能返回null的場景:使用Optional;

    (2)上層需要感知信息異常信息:使用vavr中的Either;

  • 可降級:

    (1)返回值是默認值:集合類返回,數字返回0或-1,字符串返回空字符串,其他場景自定義

    集合默認值:

Collections.emptyList()  //空List
Collections.emptySet()   //空Set
Collections.emptyMap()   //空Map

總結

本文總結了Java開發常用的高級規範,暫時想到這麼多,對文章中觀點感興趣,歡迎留言或加微信交流。

作者博客鏈接:Java研發規範(進階版)

作者簡介:木小豐,美團Java技術專家,專註分享軟件研發實踐、架構思考。歡迎關注公共號:Java研發

更多精彩文章:

錯誤碼設計思考

Java線程池進階

從MVC到DDD的架構演進

平台化建設思路淺談

構建可回滾的應用及上線checklist實踐

Tags: