最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入聊一聊springboot項(xiàng)目全局異常處理那些事兒

 更新時(shí)間:2022年01月21日 09:07:18   作者:linyb極客之路  
最近在做項(xiàng)目時(shí)需要對異常進(jìn)行全局統(tǒng)一處理,所以下面這篇文章主要給大家介紹了關(guān)于springboot項(xiàng)目全局異常處理那些事兒,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

之前我們業(yè)務(wù)團(tuán)隊(duì)在處理全局異常時(shí),在每個(gè)業(yè)務(wù)微服務(wù)中都加入了@RestControllerAdvice+@ExceptionHandler來進(jìn)行全局異常捕獲。某次領(lǐng)導(dǎo)在走查代碼的時(shí)候,就提出了一個(gè)問題,為什么要每個(gè)微服務(wù)項(xiàng)目都要自己在寫一套全局異常代碼,為什么不把全局異常塊抽成一個(gè)公共的jar,然后每個(gè)微服務(wù)以jar的形式引入。后面業(yè)務(wù)團(tuán)隊(duì)就根據(jù)領(lǐng)導(dǎo)的要求,把全局異常塊單獨(dú)抽離出來封裝成jar。今天聊的話題就是關(guān)于把全局異常抽離出來,發(fā)生的一些問題

問題一:全局異常抽離出來后,業(yè)務(wù)錯(cuò)誤碼如何定義?

之前團(tuán)隊(duì)的業(yè)務(wù)錯(cuò)誤碼定義是:業(yè)務(wù)服務(wù)前綴 + 業(yè)務(wù)模塊 + 錯(cuò)誤碼,如果是識別不了的異常,則使用業(yè)務(wù)前綴 + 固定模塊碼 + 固定錯(cuò)誤碼。

之前的全局異常偽代碼如下

@RestControllerAdvice
@Slf4j
public class GlobalExceptionBaseHandler {

   
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public AjaxResult handleException(Exception e) {
        String servicePrifix = "U";
        String moudleCode = "001";
        String code = "0001";
        String errorCode = servicePrifix + moudleCode + code;
        String msg = e.getMessage();
        if(StringUtils.isEmpty(msg)){
            msg = "服務(wù)端異常";
        }
        log.error(msg, e);
        return AjaxResult.error(msg, errorCode);
    }
    }
現(xiàn)在全局異常抽離出來后,那個(gè)業(yè)務(wù)服務(wù)前綴如何識別?之前未抽離時(shí),業(yè)務(wù)服務(wù)前綴各個(gè)業(yè)務(wù)服務(wù)直接寫死在代碼里。

當(dāng)時(shí)我們臨時(shí)的解決方案是通過spring.application.name來解決。因?yàn)槿之惓4a塊抽離出來后,最終還是要被服務(wù)引入的。因此獲取業(yè)務(wù)服務(wù)前綴的偽代碼可以通過如下獲取

public enum  ServicePrefixEnum {

    USER_SERVICE("U","用戶中心");

    private final String servicePrefix;

    private final String serviceDesc;

    ServicePrefixEnum(String servicePrefix,String serviceDesc) {
        this.servicePrefix = servicePrefix;
        this.serviceDesc = serviceDesc;
    }

    public String getServicePrefix() {
        return servicePrefix;
    }

    public String getServiceDesc() {
        return serviceDesc;
    }
}
public String getServicePrefix(@Value("${spring.application.name}") String serviceName){
      return ServicePrefixEnum.valueOf(serviceName).getServicePrefix();
    }

但這種方案其實(shí)是存在弊端

弊端一: 通過枚舉硬編碼,預(yù)設(shè)了目前了微服務(wù)名稱,一旦項(xiàng)目改變了微服務(wù)名,就找不到服務(wù)前綴了。

弊端二: 如果新上線了業(yè)務(wù)服務(wù)模塊,這個(gè)枚舉類還得改動

后面我們在全局異常jar中增加了自定義業(yè)務(wù)碼的配置,業(yè)務(wù)人員僅需在springboot配置文件配置,形如下

lybgeek:
  bizcode:
    prefix: U

此時(shí)全局異常改造示例形如下

@RestControllerAdvice
@Slf4j
public class GlobalExceptionBaseHandler {
    
    
    @Autowired
    private ServiceCodeProperties serviceCodeProperties;

   
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public AjaxResult handleException(Exception e) {
        String servicePrifix = serviceCodeProperties.getPrifix();
        String moudleCode = "001";
        String code = "0001";
        String errorCode = servicePrifix + moudleCode + code;
        String msg = e.getMessage();
        if(StringUtils.isEmpty(msg)){
            msg = "服務(wù)端異常";
        }
        log.error(msg, e);
        return AjaxResult.error(msg, errorCode);
    }
}

問題二:全局異常因引入了和業(yè)務(wù)相同的依賴jar,但jar存在版本差異

如果全局異常直接如下寫,是不存在問題。示例如下

@RestControllerAdvice
@Slf4j
public class GlobalExceptionBaseHandler {


    @Autowired
    private ServiceCodeProperties serviceCodeProperties;

    
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public AjaxResult handleException(Exception e) {
        String servicePrifix = serviceCodeProperties.getPrifix();
        String moudleCode = "001";
        String code = "0001";
        String errorCode = servicePrifix + moudleCode + code;
        String msg = e.getMessage();
        if(StringUtils.isEmpty(msg)){
            msg = "服務(wù)端異常";
        }
        log.error(msg, e);
        return AjaxResult.error(msg, HttpStatus.INTERNAL_SERVER_ERROR.value());
    }


    @ExceptionHandler(BizException.class)
    public AjaxResult handleException(BizException e)
    {
        return AjaxResult.error(e.getMessage(), e.getErrorCode());
    }

}

即全局異常直接分為業(yè)務(wù)異常和Execption這兩種,這樣劃分的弊端在于沒辦法細(xì)分異常,而且也使項(xiàng)目組定義的模塊碼和業(yè)務(wù)碼沒法細(xì)分。因此我們也列出常用可以預(yù)知的系統(tǒng)異常,示例如下

/**
     *參數(shù)驗(yàn)證失敗
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public AjaxResult handleException(ConstraintViolationException e)
    {
        log.error("參數(shù)驗(yàn)證失敗", e);
        return AjaxResult.error("參數(shù)驗(yàn)證失敗", HttpStatus.BAD_REQUEST.value());
    }

   /**
     * 數(shù)據(jù)庫異常
     * @param e
     * @return
     */
    @ExceptionHandler({SQLException.class, MybatisPlusException.class,
            MyBatisSystemException.class, org.apache.ibatis.exceptions.PersistenceException.class,
            BadSqlGrammarException.class
    })
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public AjaxResult dbException(Exception e) {
        String msg = ExceptionUtil.getExceptionMessage(e);
        log.error(msg, e);
        return AjaxResult.error(msg,HttpStatus.BAD_REQUEST.value());
    }

    /**
     * 數(shù)據(jù)庫中已存在該記錄
     * @param e
     * @return
     */
    @ExceptionHandler(DuplicateKeyException.class)
    @ResponseStatus(HttpStatus.CONFLICT)
    public AjaxResult handleException(DuplicateKeyException e)
    {
        log.error("數(shù)據(jù)庫中已存在該記錄", e);
        return AjaxResult.error("數(shù)據(jù)庫中已存在該記錄", HttpStatus.CONFLICT.value());
    }

不過這樣導(dǎo)致了一個(gè)問題,就是全局異常和業(yè)務(wù)方使用相同的依賴jar,但存在版本差異時(shí),可能就會存在依賴沖突,導(dǎo)致業(yè)務(wù)項(xiàng)目啟動報(bào)錯(cuò)。因此解決方案就是在pom文件加入optional標(biāo)簽。示例如下

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <optional>true</optional>
        </dependency>

這標(biāo)簽的意思這jar坐標(biāo)是可選的,因此如果項(xiàng)目中已經(jīng)有引入該jar的坐標(biāo),就直接用該jar的坐標(biāo)

問題三:引入maven optional標(biāo)簽后,因業(yè)務(wù)沒引入全局異常需要的jar,導(dǎo)致項(xiàng)目啟動報(bào)錯(cuò)

這個(gè)問題的產(chǎn)生:舉個(gè)示例,我們的業(yè)務(wù)微服務(wù)項(xiàng)目有聚合層,某些聚合層是不需要依賴存儲介質(zhì),比如mysql。因此這些聚合層項(xiàng)目pom就不會引入類似mybatis相關(guān)的依賴。但我們的全局異常又需要類似mybatis相關(guān)的依賴,這樣導(dǎo)致如果要引用全局異常模塊,有得額外加入業(yè)務(wù)方不需要的jar。

因此springboot的條件注解就派上用場了,利用@ConditionalOnClass注解。示例如下

@RestControllerAdvice
@Slf4j
@ConditionalOnClass({SQLException.class, MybatisPlusException.class,
        MyBatisSystemException.class, org.apache.ibatis.exceptions.PersistenceException.class,
        BadSqlGrammarException.class, DuplicateKeyException.class})
public class GlobalExceptionDbHandler {

    /**
     * 數(shù)據(jù)庫異常
     * @param e
     * @return
     */
    @ExceptionHandler({SQLException.class, MybatisPlusException.class,
            MyBatisSystemException.class, org.apache.ibatis.exceptions.PersistenceException.class,
            BadSqlGrammarException.class
    })
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public AjaxResult dbException(Exception e) {
        String msg = ExceptionUtil.getExceptionMessage(e);
        log.error(msg, e);
        return AjaxResult.error(msg,HttpStatus.BAD_REQUEST.value());
    }

    /**
     * 數(shù)據(jù)庫中已存在該記錄
     * @param e
     * @return
     */
    @ExceptionHandler(DuplicateKeyException.class)
    @ResponseStatus(HttpStatus.CONFLICT)
    public AjaxResult handleException(DuplicateKeyException e)
    {
        log.error("數(shù)據(jù)庫中已存在該記錄", e);
        return AjaxResult.error("數(shù)據(jù)庫中已存在該記錄", HttpStatus.CONFLICT.value());
    }
}

@ConditionalOnClass這個(gè)注解的作用就是如果classpath存在指定的類,則該注解上的類會生效。

同時(shí)這邊有個(gè)細(xì)節(jié)點(diǎn),就是全局異常可能就得細(xì)分,即把原來的大一統(tǒng)的全局異常,按業(yè)務(wù)場景分開,比如存儲介質(zhì)相關(guān)的存儲異常,web相關(guān)異常

總結(jié)

本文主要講當(dāng)將全局異常抽離成jar,可能會發(fā)生的問題。這邊有涉及到一些細(xì)節(jié)點(diǎn)沒講,比如為啥要定義服務(wù)前綴+業(yè)務(wù)模塊碼+錯(cuò)誤碼,其實(shí)主要還是為了好排查問題。

也許有朋友會問,你們都搞了微服務(wù),難道不上分布式鏈路追蹤?根據(jù)分布式鏈路追蹤可以很方便定位到整個(gè)鏈路了。但真的開發(fā)微服務(wù)的時(shí)候,如果公司原來就就沒運(yùn)維平臺,有時(shí)候?yàn)榱顺杀究剂?,測試、開發(fā)環(huán)境都不會上的分布式鏈路追蹤的,甚至線上項(xiàng)目初期也不會上分布式鏈路追蹤。因此定義好相關(guān)的業(yè)務(wù)碼就變得格外重要

到此這篇關(guān)于springboot項(xiàng)目全局異常處理那些事兒的文章就介紹到這了,更多相關(guān)springboot項(xiàng)目全局異常處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

demo鏈接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-exception

相關(guān)文章

  • IDEA如何加載resources文件夾下文件相對路徑

    IDEA如何加載resources文件夾下文件相對路徑

    這篇文章主要介紹了IDEA如何加載resources文件夾下文件相對路徑問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java RabbitMQ高級特性詳細(xì)分析

    Java RabbitMQ高級特性詳細(xì)分析

    為了保證消息的可靠性傳輸,包括投遞消息的生產(chǎn)方能投遞成功,和消息消費(fèi)的消費(fèi)方正確消費(fèi),RabbitMQ 提供了兩個(gè)確認(rèn)機(jī)制,由于消息按照流通的順序從左到右,因此為保證可靠性,MQ必須對 Producer進(jìn)行確認(rèn),Consumer 必須對 MQ 進(jìn)行確認(rèn)
    2022-08-08
  • java實(shí)現(xiàn)隨機(jī)生成UUID

    java實(shí)現(xiàn)隨機(jī)生成UUID

    這篇文章主要介紹了java實(shí)現(xiàn)隨機(jī)生成UUID的函數(shù)代碼,有需要的小伙伴可以參考下。
    2015-07-07
  • Spring中@Value注解的使用方法詳解

    Spring中@Value注解的使用方法詳解

    這篇文章主要介紹了Spring中@Value注解的使用方法詳解,在spring項(xiàng)目中必不可少的就是讀取配置文件,那么讀取配置文件就有兩種方式,一種就是使用Spring中@Value注解,還有一種是使用SpringBoot中的@ConfigurationProperties注解,需要的朋友可以參考下
    2024-01-01
  • Java中的System.getenv()和System.getProperty()使用詳解

    Java中的System.getenv()和System.getProperty()使用詳解

    文章介紹了Java中用于讀取環(huán)境配置信息的兩種方法:System.getenv()和System.getProperty(),前者讀取系統(tǒng)環(huán)境變量,返回一個(gè)不可修改的Map;后者獲取JVM環(huán)境變量值,可以通過-D參數(shù)設(shè)置,文章還提到,通過這兩種方法可以簡化配置,不需要修改代碼
    2024-11-11
  • quartz定時(shí)執(zhí)行任務(wù),并配置web.xml的操作方法

    quartz定時(shí)執(zhí)行任務(wù),并配置web.xml的操作方法

    下面小編就為大家?guī)硪黄猶uartz定時(shí)執(zhí)行任務(wù),并配置web.xml的操作方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-07-07
  • Java數(shù)據(jù)導(dǎo)出功能之導(dǎo)出Excel文件實(shí)例

    Java數(shù)據(jù)導(dǎo)出功能之導(dǎo)出Excel文件實(shí)例

    這篇文章主要介紹了Java數(shù)據(jù)導(dǎo)出功能之導(dǎo)出Excel文件實(shí)例,本文給出了jar包的下載地址,并給出了導(dǎo)出Excel文件代碼實(shí)例,需要的朋友可以參考下
    2015-06-06
  • Java基礎(chǔ)類之ArrayUtils工具類詳解

    Java基礎(chǔ)類之ArrayUtils工具類詳解

    這篇文章主要介紹了java.ArrayDeque類使用方法,java.ArrayDeque類提供了可調(diào)整大小的陣列,并實(shí)現(xiàn)了Deque接口,感興趣的小伙伴們可以參考一下
    2021-09-09
  • java實(shí)現(xiàn)酷狗音樂臨時(shí)緩存文件轉(zhuǎn)換為MP3文件的方法

    java實(shí)現(xiàn)酷狗音樂臨時(shí)緩存文件轉(zhuǎn)換為MP3文件的方法

    這篇文章主要介紹了java實(shí)現(xiàn)酷狗音樂臨時(shí)緩存文件轉(zhuǎn)換為MP3文件的方法,涉及java針對文件操作的相關(guān)技巧,需要的朋友可以參考下
    2016-08-08
  • 如何使用Java爬蟲批量爬取圖片

    如何使用Java爬蟲批量爬取圖片

    這篇文章主要介紹了如何使用Java爬蟲批量爬取圖片,對于爬蟲的入門來說,圖片相對來說是比較容易獲取的,因?yàn)榇蟛糠謭D片都不是敏感數(shù)據(jù),所以不會遇到什么反爬措施,對于入門爬蟲來說是比較合適的,需要的朋友可以參考下
    2023-04-04

最新評論

东乌| 岐山县| 五莲县| 池州市| 濮阳县| 西安市| 五家渠市| 阿拉尔市| 临泉县| 自贡市| 普陀区| 公安县| 新龙县| 耒阳市| 长沙县| 昔阳县| 阿巴嘎旗| 铜山县| 英山县| 保靖县| 天长市| 观塘区| 新泰市| 界首市| 昂仁县| 莱阳市| 和田县| 阳东县| 阳西县| 龙山县| 福海县| 鸡西市| 岳阳市| 九台市| 常宁市| 格尔木市| 西畴县| 兰坪| 苗栗县| 祥云县| 革吉县|