日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

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

前言

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

實(shí)用技巧

rpc服務(wù)出參統(tǒng)一化

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

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

1public class TradeResultDTO implements Serializable {

2 /**

3 * 默認(rèn)失敗編碼

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}

統(tǒng)一對象返回的結(jié)構(gòu)就是上面這樣

接著這個(gè)我想說的是,作為服務(wù)提供方,如果這個(gè)接口提供了返回值,我拿創(chuàng)建訂單接口舉例

1/**

2 * 創(chuàng)建交易單,業(yè)務(wù)系統(tǒng)發(fā)起

3 *

4 * @param req 創(chuàng)建入?yún)?/p>

5 * @return 返回創(chuàng)建信息6 */

7TradeResultDTO createOrder(TradeCreateOrderRequestDTO req)

8

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

異常統(tǒng)一化


統(tǒng)一化使用,杜絕項(xiàng)目出現(xiàn)各種各樣的自定義異常

對外統(tǒng)一拋出異常

我使用的統(tǒng)一化有兩個(gè)方面:

  • 拋出的自定義異常不要五花八門,一個(gè)就夠了;很多人喜歡寫各種各樣的異常,初衷其實(shí)沒錯(cuò),但是人多手雜,自定義異常可能越寫越亂;
  • 異常信息最好盡可能的具體化,描述出業(yè)務(wù)產(chǎn)生異常原因就可以了,比如入?yún)⑿r?yàn)的用戶信息不存在之類的;或者在調(diào)用用戶中心的時(shí)候,捕獲了該異常,此時(shí)你只需定義調(diào)用用戶中心異常就可以了

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

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

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}

其實(shí)上面代碼里也體現(xiàn)出來IError這個(gè)接口了,我們的錯(cuò)誤枚舉都需要實(shí)現(xiàn)這個(gè)異常接口,方便統(tǒng)一獲取對應(yīng)的錯(cuò)誤碼和錯(cuò)誤信息,這里列舉一下通用異常的定義

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", "系統(tǒng)異常"),
12 REQUEST_OBJECT_IS_NULL_ERROR("00000001", "入?yún)ο鬄榭?#34;),
13 PARAMS_CANNOT_BE_NULL_ERROR("00000002", "參數(shù)不能為空"),
14 BUILD_LOCK_KEY_ERROR("00000003", "系統(tǒng)異常: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的方式我們可以按照具體業(yè)務(wù)定義相應(yīng)的枚舉,比如OrderErrorEnum、PayErrorEnum之類,不僅具有區(qū)分度而且,也能瞬速定位問題;

所以對外拋出異常統(tǒng)一化就一把梭:統(tǒng)一util和業(yè)務(wù)錯(cuò)誤枚舉分類

對內(nèi)統(tǒng)一捕獲外部異常

很多時(shí)候我們需要調(diào)用別人的服務(wù)然后寫了一些adapter適配類,然后在里面trycatch一把;其實(shí)這時(shí)候可以利用aop好好搞一把就完事了,并且統(tǒng)一輸出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 }

上面這段統(tǒng)一捕獲了外部服務(wù),記錄異常日志,避免了每個(gè)adapter類重復(fù)捕獲的問題

參數(shù)校驗(yàn)

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

ParamCheckUtil.java

1@Slf4j

2public class ParamCheckUtil {

3

4 /**

5 * 校驗(yàn)請求參數(shù)是否為空

6 *

7 * @param requestParams 請求入?yún)?/p>

8 * @param keys 屬性值數(shù)組 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("獲取屬性值出錯(cuò),requestParams={}, fieldName={}", requestParams, fieldName);

26 } finally {

27 // 判空標(biāo)志 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}

基于了上面統(tǒng)一異常的形式,只要參數(shù)校驗(yàn)出空我就拋出異常中斷程序,并且告知其缺什么參數(shù)

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

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相關(guān)

對象的構(gòu)造

關(guān)于對象的構(gòu)造,我想提兩點(diǎn),構(gòu)造變的對象和不變的對象

  • 構(gòu)造不變對象,使用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}

構(gòu)造可變對象,推薦提供鏈?zhǔn)秸{(diào)用形式 使用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}

對象轉(zhuǎn)換

就一把梭:lambda工具類+mapstruct進(jìn)行轉(zhuǎn)換

BeanConvertUtil.java 通用的對象、list、Page轉(zhuǎn)換

1public class BeanConvertUtil {
2 /**
3 * 對象轉(zhuǎn)換
4 *
5 * @param source 源對象
6 * @param convertFun T -> R lambda轉(zhuǎn)換表達(dá)式
7 * @param 輸入類型
8 * @param 輸出類型
9 * @return 返回轉(zhuǎn)化后輸出類型的對象
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轉(zhuǎn)換
20 *
21 * @param page 源對象
22 * @param convertFun T -> R lambda轉(zhuǎn)換表達(dá)式
23 * @param 輸入類型
24 * @param 輸出類型
25 * @return 返回轉(zhuǎn)化后輸出類型的對象
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轉(zhuǎn)換
37 *
38 * @param inputList 數(shù)據(jù)源
39 * @param convertFun T -> R lambda轉(zhuǎn)換表達(dá)式
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}

實(shí)戰(zhàn)使用,在lambda方法進(jìn)行轉(zhuǎn)換: 先轉(zhuǎn)換相同屬性,再進(jìn)行剩余屬性賦值

1?public?interface?OrderConverter?{ 2????OrderConverter?INSTANCE?=?Mappers.getMapper(OrderConverter.class); 3????????//?入?yún)⑦M(jìn)行相同屬性轉(zhuǎn)換 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});

其實(shí)對象轉(zhuǎn)換也可以完全通過mapstruct提供的一些表達(dá)式進(jìn)行轉(zhuǎn)換,但是有時(shí)候?qū)懩莻€(gè)感覺不是很直觀,其實(shí)都可以,我比較喜歡我這種形式,大家有建議也可以提出

NPE解決指南

1.null值手動(dòng)判斷[強(qiáng)制]

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

學(xué)會這點(diǎn) 基本意識有了

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

2.Optional

2.1 Optional嵌套取值[強(qiáng)制]

參數(shù)>=3的取值操作學(xué)會這點(diǎn) 基本告別NPE

這里以O(shè)rderInfo對象為例 獲取purchaseType

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

如果對取出的值如需再次進(jìn)行判斷操作 參考第1點(diǎn)

2.2 中斷拋出異常[按需]

還是以上面的例子

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

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

3.對象判空[強(qiáng)制]

1Objects.equals(obj1,obj2);

4.Boolean值判斷[強(qiáng)制]

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

1null!=a&&a;

正確食用

1Boolean.TRUE.equals(a);

5.list正確判空姿勢[強(qiáng)制]

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正確判空姿勢[強(qiáng)制]

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

7.包裝類型轉(zhuǎn)換判空[強(qiáng)制]

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

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

小結(jié)

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

END

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

喜歡的可以點(diǎn)贊+關(guān)注,感謝支持

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。