日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

敲代码就是一把梭_2020必看!开发五年的大佬日常工作中所使用的java代码技巧...

發布時間:2025/4/17 134 豆豆
生活随笔 收集整理的這篇文章主要介紹了 敲代码就是一把梭_2020必看!开发五年的大佬日常工作中所使用的java代码技巧... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

羅列工作中實際使用的一些代碼技巧或者叫工具類;知識無大小,希望大家都有收獲

實用技巧

rpc服務出參統一化

什么,出參統一化有什么好說的????? 我不知道你們有沒有遇到過多少五花八門的外部服務提供的返回對象,可能別人沒有規范約束,我們管不了,但是從我們這里出去的,我們可以強制約束一下,不然發生新老交替,這代碼還能看嗎

首先出參都叫xxDTO的,阿里java開發手冊提到過;再者我們是提供服務的一方,錯誤碼code,錯誤信息msg,以及返回結果data都是要明確體現出來的,像下面這樣

1public class TradeResultDTO implements Serializable {

2 /**

3 * 默認失敗編碼

4 */

5 private static final String DEFAULT_FAIL_CODE = "500";

6 private boolean success;

7 private String code;

8 private String msg; 9 private T data;

10 public static TradeResultDTO success(T data) {

11 return base("200", null, true, data);

12 }

13

14 public static TradeResultDTO fail(String code, String msg) {

15 return base(code, msg, false, null);

16 }

17

18 public static TradeResultDTO fail(String msg) {

19 return base(DEFAULT_FAIL_CODE, msg, false, null);

20 }

21

22 public static TradeResultDTO success() {

23 return base("200", null, true, null);

24 }

25

26 public static TradeResultDTO fail(IError iError) {

27 return base(iError.getErrorCode(), iError.getErrorMsg(), false, null);

28 }

29}

統一對象返回的結構就是上面這樣

接著這個我想說的是,作為服務提供方,如果這個接口提供了返回值,我拿創建訂單接口舉例

1/**

2 * 創建交易單,業務系統發起

3 *

4 * @param req 創建入參

5 * @return 返回創建信息6 */

7TradeResultDTO createOrder(TradeCreateOrderRequestDTO req)

8

比如這個TradeCreateOrderResponseDTO 返回了訂單號之類的基本信息,這個接口對于具體業務場景只能產生一筆訂單號,我之前遇到過對方只是提示什么的錯誤信息(訂單已存在),是的沒錯,他做了冪等,但是他沒有返回原值,那對應的調用方進入了死循環,可能對應的業務系統,需要返回的訂單號落到自己的數據庫,一直異常一直回滾重試,沒有任何意義;所以作為一個負責人的服務提供方,類似這種情況,如果你的方法有冪等,那么請一定返回存在的那個對象;

異常統一化


統一化使用,杜絕項目出現各種各樣的自定義異常

對外統一拋出異常

我使用的統一化有兩個方面:

  • 拋出的自定義異常不要五花八門,一個就夠了;很多人喜歡寫各種各樣的異常,初衷其實沒錯,但是人多手雜,自定義異??赡茉綄懺絹y;
  • 異常信息最好盡可能的具體化,描述出業務產生異常原因就可以了,比如入參校驗的用戶信息不存在之類的;或者在調用用戶中心的時候,捕獲了該異常,此時你只需定義調用用戶中心異常就可以了

然后看下工作中比較推薦的:

首先,需要搞一個統一拋出異常的工具 ExceptionUtil(這里Exceptions是公司內部統一前后端交互的,基于這個包裝一個基礎util,統一整個組拋異常的入口)

1public class ExceptionUtil {
2 public static OptimusExceptionBase fail(IError error) throws OptimusExceptionBase {
3 return Exceptions.fail(errorMessage(error));
4 }
5
6 public static OptimusExceptionBase fail(IError error, String... msg) throws OptimusExceptionBase {
7 return Exceptions.fail(errorMessage(error, msg));
8 }
9
10 public static OptimusExceptionBase fault(IError error) throws OptimusExceptionBase {
11 return Exceptions.fault(errorMessage(error));
12 }
13
14 public static OptimusExceptionBase fault(IError error, String... msg) throws OptimusExceptionBase {
15 return Exceptions.fault(errorMessage(error, msg));
16 }
17
18
19 private static ErrorMessage errorMessage(IError error) {
20 if (error == null) {
21 error = CommonErrorEnum.DEFAULT_ERROR;
22 }
23 return ErrorMessage.errorMessage("500", "[" + error.getErrorCode() + "]" + error.getErrorMsg());
24 }
25
26 private static ErrorMessage errorMessage(IError error, String... msg) {
27 if (error == null) {
28 error = CommonErrorEnum.DEFAULT_ERROR;
29 }
30 return ErrorMessage.errorMessage("500", "[" + error.getErrorCode() + "]" + MessageFormat.format(error.getErrorMsg(), msg));
31 }
32}

其實上面代碼里也體現出來IError這個接口了,我們的錯誤枚舉都需要實現這個異常接口,方便統一獲取對應的錯誤碼和錯誤信息,這里列舉一下通用異常的定義

1public interface IError {
2 String getErrorCode();
3
4 String getErrorMsg();
5}
6@AllArgsConstructor
7public enum CommonErrorEnum implements IError {
8 /**
9 *
10 */
11 DEFAULT_ERROR("00000000", "系統異常"),
12 REQUEST_OBJECT_IS_NULL_ERROR("00000001", "入參對象為空"),
13 PARAMS_CANNOT_BE_NULL_ERROR("00000002", "參數不能為空"),
14 BUILD_LOCK_KEY_ERROR("00000003", "系統異常:lock key異常"),
15 REPEAT_COMMIT_ERROR("00000004", "正在提交中,請稍候");
16
17 private String code;
18 private String msg;
19
20 @Override
21 public String getErrorCode() {
22 return code;
23 }
24
25 @Override
26 public String getErrorMsg() {
27 return msg;
28 }
29}

類似上面CommonErrorEnum的方式我們可以按照具體業務定義相應的枚舉,比如OrderErrorEnum、PayErrorEnum之類,不僅具有區分度而且,也能瞬速定位問題;

所以對外拋出異常統一化就一把梭:統一util和業務錯誤枚舉分類

對內統一捕獲外部異常

很多時候我們需要調用別人的服務然后寫了一些adapter適配類,然后在里面trycatch一把;其實這時候可以利用aop好好搞一把就完事了,并且統一輸出adapter層里面的日志

1 public Object transferException(ProceedingJoinPoint joinPoint) {
2 try {
3 Object result = joinPoint.proceed();
4 log.info("adapter result:{}", JSON.toJSONString(result));
5 return result;
6 } catch (Throwable exception) {
7 MethodSignature signature = (MethodSignature) joinPoint.getSignature();
8 Method method = signature.getMethod();
9 log.error("{}.{} throw exception", method.getDeclaringClass().getName(), method.getName(), exception);
10 throw ExceptionUtils.fault(CommonErrorEnum.ADAPTER_SERVICE_ERROR);
11 return null;
12 }
13 }

上面這段統一捕獲了外部服務,記錄異常日志,避免了每個adapter類重復捕獲的問題

參數校驗

用過swagger的應該了解api方法里有對應的注解屬性約束是否必填項,但是如果判斷不是在api入口又或者沒有類似的注解,你們一般怎么做的,下面給出我自己的一種簡單工具;有更好大佬的可以推薦一下

ParamCheckUtil.java

1@Slf4j

2public class ParamCheckUtil {

3

4 /**

5 * 校驗請求參數是否為空

6 *

7 * @param requestParams 請求入參

8 * @param keys 屬性值數組 9 */

10 public static void checkParams(Object requestParams, String... keys) {

11 if (null == requestParams) {

12 throw ExceptionUtil.fault(CommonErrorEnum.REQUEST_OBJECT_IS_NULL_ERROR);

13 }

14 StringBuilder sb = new StringBuilder();

15 for (String fieldName : keys) {

16 Object value = null;

17 Type type = null;

18 try {

19 String firstLetter = fieldName.substring(0, 1).toUpperCase();

20 String getter = "get" + firstLetter + fieldName.substring(1);

21 Method method = requestParams.getClass().getMethod(getter);

22 value = method.invoke(requestParams);

23 type = method.getReturnType();

24 } catch (Exception e) {

25 log.error("獲取屬性值出錯,requestParams={}, fieldName={}", requestParams, fieldName);

26 } finally {

27 // 判空標志 String/Collection/Map特殊處理

28 boolean isEmpty =

29 (String.class == type && StringUtil.isEmpty((String) value))

30 || (Collection.class == type && CollectionUtils.isEmpty((Collection extends Object>) value))

31 || (Map.class == type && CollectionUtils.isEmpty((Collection extends Object>) value))

32 || (null == value);

33 if (isEmpty) {

34 if (sb.length() != 0) {

35 sb.append(",");

36 }

37 sb.append(fieldName);

38 }

39 }

40 }

41

42 if (sb.length() > 0) {

43 log.error(sb.toString() + CommonErrorEnum.PARAMS_CANNOT_BE_NULL_ERROR.getErrorMsg());

44 throw ExceptionUtil.fault(CommonErrorEnum.PARAMS_CANNOT_BE_NULL_ERROR, sb.toString() + CommonErrorEnum.PARAMS_CANNOT_BE_NULL_ERROR.getErrorMsg());

45 }

46 }

47

48 // test

49 public static void main(String[] args) {

50 TradeCreateOrderRequestDTO tradeCreateOrderRequestDTO = new TradeCreateOrderRequestDTO();

51 tradeCreateOrderRequestDTO.setBusinessNo("");

52 ParamCheckUtil.checkParams(tradeCreateOrderRequestDTO, "businessNo", "tradeType", "tradeItemDTOS");

53 }

54

55}

基于了上面統一異常的形式,只要參數校驗出空我就拋出異常中斷程序,并且告知其缺什么參數

我在業務代碼需要判斷字段非空的地方只需要一行就夠了,就行下面這樣

1ParamCheckUtil.checkParams(tradeCreateOrderRequestDTO, "businessNo", "tradeType", "tradeItemDTOS");

而不是我們常用的一堆判斷,像下面這樣;看到這些我人都暈了,一次兩次就算了,一大段全是這種

1if (null == tradeCreateOrderRequestDTO) {

2// 提示tradeCreateOrderRequestDTO為空

3}

4if (StringUtil.isEmpty(tradeCreateOrderRequestDTO.getBusinessNo())) {

5// 提示businessNo為空

6}

7if (StringUtil.isEmpty(tradeCreateOrderRequestDTO.getTradeType())) {

8// 提示tradeType為空

9}

10if (CollectionUtils.isEmpty(tradeCreateOrderRequestDTO.getTradeItemDTOS())) {

11// 提示tradeItemDTOS列表為空

12}

如果你是上面說的這種形式,不妨試試我提供的這種

bean相關

對象的構造

關于對象的構造,我想提兩點,構造變的對象和不變的對象

  • 構造不變對象,使用builder,不提供set方法,推薦使用lombok @Builder

1@Builder

2public class UserInfo {

3 private String id;

4 private String name;

5

6 public static void main(String[] args) {

7 UserInfo userInfo = UserInfo.builder().id("a").name("name").build();

8 }

9}

構造可變對象,推薦提供鏈式調用形式 使用lombok @Accessors(chain = true)注解

1@Data
2@Accessors(chain = true)
3public class CardInfo {
4 private String id;
5 private String name;
6 public static void main(String[] args) {
7 CardInfo cardInfo = new CardInfo().setId("c").setName("name");
8 }
9}

對象轉換

就一把梭:lambda工具類+mapstruct進行轉換

BeanConvertUtil.java 通用的對象、list、Page轉換

1public class BeanConvertUtil {
2 /**
3 * 對象轉換
4 *
5 * @param source 源對象
6 * @param convertFun T -> R lambda轉換表達式
7 * @param 輸入類型
8 * @param 輸出類型
9 * @return 返回轉化后輸出類型的對象
10 */
11 public static R convertObject(T source, Function convertFun) {
12 if (null == source) {
13 return null;
14 }
15 return convertFun.apply(source);
16 }
17
18 /**
19 * Page轉換
20 *
21 * @param page 源對象
22 * @param convertFun T -> R lambda轉換表達式
23 * @param 輸入類型
24 * @param 輸出類型
25 * @return 返回轉化后輸出類型的對象
26 */
27 public static Page convertPage(Page page, Function convertFun) {
28 if (Objects.isNull(page)) {
29 return new Page<>(0, 1, 10, Collections.emptyList());
30 }
31 List pageList = convertList(page.getItems(), convertFun);
32 return new Page<>(page.getTotalNumber(), page.getCurrentIndex(), page.getPageSize(), pageList);
33 }
34
35 /**
36 * ListData轉換
37 *
38 * @param inputList 數據源
39 * @param convertFun T -> R lambda轉換表達式
40 * @param 輸入類型
41 * @param 輸出類型
42 * @return 輸出
43 */
44 public static List convertList(List inputList, Function convertFun) {
45 if (org.springframework.util.CollectionUtils.isEmpty(inputList)) {
46 return Lists.newArrayList();
47 }
48 return inputList
49 .stream()
50 .map(convertFun)
51 .collect(Collectors.toList());
52 }
53}

實戰使用,在lambda方法進行轉換: 先轉換相同屬性,再進行剩余屬性賦值

1?public?interface?OrderConverter?{ 2????OrderConverter?INSTANCE?=?Mappers.getMapper(OrderConverter.class); 3????????//?入參進行相同屬性轉換 4????TradeOrderDO?createOrder2TradeOrderDO(TradeCreateOrderRequestDTO?req); 5} 6?TradeOrderDO?mainTradeOrder?=?BeanConvertUtil.convertObject(req,?x?->?{ 7?????TradeOrderDO?tod?=?OrderConverter.INSTANCE.createOrder2TradeOrderDO(req); 8?????tod.setOrderType(mainOrderType); 9?????tod.setOrderCode(snowflakeIdAdapterService.getId());10?????tod.setOrderStatus(TradeStateEnum.ORDER_CREATED.getValue());11?????tod.setDateCreate(new?Date());12?????tod.setDateUpdate(new?Date());13?????return?tod;14});

其實對象轉換也可以完全通過mapstruct提供的一些表達式進行轉換,但是有時候寫那個感覺不是很直觀,其實都可以,我比較喜歡我這種形式,大家有建議也可以提出

NPE解決指南

1.null值手動判斷[強制]

嵌套取值<3 推薦 null值判斷(PS:強制null寫在前面,別問為什么,問就是這樣寫你會意識到這里要NPE)

學會這點 基本意識有了

1null!=obj&&null!=obj.getXX()

2.Optional

2.1 Optional嵌套取值[強制]

參數>=3的取值操作學會這點 基本告別NPE

這里以OrderInfo對象為例 獲取purchaseType

1Optional?optional?=?Optional.ofNullable(dto);2Integer?purchaseType?=?optional.map(OrderInfo::getOrderCarDTO)3?????????????????????????????????.map(OrderCarDTO::getPurchaseType)4?????????????????????????????????.orElse(null);

如果對取出的值如需再次進行判斷操作 參考第1點

2.2 中斷拋出異常[按需]

還是以上面的例子

1{
2 // ...
3 optional.map(OrderInfo::getOrderDTO).map(OrderDTO::getOrderBusinessType)
4 .orElseThrow(() -> new Exception("獲取cityCode失敗"));
5}

如果依賴某些值,可盡早fail-fast

3.對象判空[強制]

1Objects.equals(obj1,obj2);

4.Boolean值判斷[強制]

棄用以下方式謝謝(PS:很多時候null判斷你會丟的)

1null!=a&&a;

正確食用

1Boolean.TRUE.equals(a);

5.list正確判空姿勢[強制]

1if?(CollectionUtils.isEmpty(list))?{2????//?fail?fast3????//?return?xxObj?or?return;4}5List?safeList?=?list.stream().filter(Objects::nonNull).collect(Collectors.toList());6if?(CollectionUtils.isEmpty(safeList))?{7????//?fail?fast8????//?return?xxObj?or?return;9}

6.String正確判空姿勢[強制]

1//?不為空2if?(StringUtil.isNotEmpty(s))?{3????//?...4}

7.包裝類型轉換判空[強制]

特別是遍歷過程中使用,需要判斷是否為空。

1int?i?=?0;2list.forEach(item?->?{3????if(Objects.nonNull(item.getType)){4?????i?+=?item.getType;?//item.getType?返回?Integer???5????}6});

小結

融會貫通以上幾招絕對告別NPE

END

未完待續,大家如果有好的建議,希望在留言中提出

喜歡的可以點贊+關注,感謝支持

總結

以上是生活随笔為你收集整理的敲代码就是一把梭_2020必看!开发五年的大佬日常工作中所使用的java代码技巧...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。