1. Java 項(xiàng)目構(gòu)建基礎(chǔ):統(tǒng)一結(jié)果,統(tǒng)一異常,統(tǒng)一日志

        共 8808字,需瀏覽 18分鐘

         ·

        2022-04-16 18:01

        點(diǎn)擊下方“IT牧場”,選擇“設(shè)為星標(biāo)”

        來源:juejin.im/post/

        5e073980f265da33f8653f2e

        • 統(tǒng)一結(jié)果返回
          • 統(tǒng)一結(jié)果的一般形式
          • 結(jié)果類枚舉
          • 統(tǒng)一結(jié)果類
          • 控制層返回
        • 統(tǒng)一異常處理
          • @ControllerAdvice
          • 自定義全局異常類
          • 統(tǒng)一異常處理器
          • 控制層展示
        • 統(tǒng)一日志收集
          • Logback
          • 配置
          • 日志收集異常信息
        • GitHub 源碼

        統(tǒng)一結(jié)果返回

        目前的前后端開發(fā)大部分?jǐn)?shù)據(jù)的傳輸格式都是json,因此定義一個(gè)統(tǒng)一規(guī)范的數(shù)據(jù)格式有利于前后端的交互與UI的展示。

        統(tǒng)一結(jié)果的一般形式

        1. 是否響應(yīng)成功;
        2. 響應(yīng)狀態(tài)碼;
        3. 狀態(tài)碼描述;
        4. 響應(yīng)數(shù)據(jù)
        5. 其他標(biāo)識符

        結(jié)果類枚舉

        • 前三者可定義結(jié)果枚舉,如:success,code,message
        @Getter
        public?enum?ResultCodeEnum?{
        ????SUCCESS(true,20000,"成功"),
        ????UNKNOWN_ERROR(false,20001,"未知錯(cuò)誤"),,
        ????PARAM_ERROR(false,20002,"參數(shù)錯(cuò)誤"),
        ????;

        ????//?響應(yīng)是否成功
        ????private?Boolean?success;
        ????//?響應(yīng)狀態(tài)碼
        ????private?Integer?code;
        ????//?響應(yīng)信息
        ????private?String?message;

        ????ResultCodeEnum(boolean?success,?Integer?code,?String?message)?{
        ????????this.success?=?success;
        ????????this.code?=?code;
        ????????this.message?=?message;
        ????}
        }

        統(tǒng)一結(jié)果類

        • 第5個(gè)屬于自定義返回,利用前4者可定義統(tǒng)一返回對象

        注意:

        1. 外接只可以調(diào)用統(tǒng)一返回類的方法,不可以直接創(chuàng)建,影刺構(gòu)造器私有;
        2. 內(nèi)置靜態(tài)方法,返回對象;
        3. 為便于自定義統(tǒng)一結(jié)果的信息,建議使用鏈?zhǔn)骄幊蹋瑢⒎祷貙ο笤O(shè)類本身,即return this;
        4. 響應(yīng)數(shù)據(jù)由于為json格式,可定義為JsonObject或Map形式;
        @Data
        public?class?R?{
        ????private?Boolean?success;

        ????private?Integer?code;

        ????private?String?message;

        ????private?Map?data?=?new?HashMap<>();

        ????//?構(gòu)造器私有
        ????private?R(){}

        ????//?通用返回成功
        ????public?static?R?ok()?{
        ????????R?r?=?new?R();
        ????????r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
        ????????r.setCode(ResultCodeEnum.SUCCESS.getCode());
        ????????r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
        ????????return?r;
        ????}

        ????//?通用返回失敗,未知錯(cuò)誤
        ????public?static?R?error()?{
        ????????R?r?=?new?R();
        ????????r.setSuccess(ResultCodeEnum.UNKNOWN_ERROR.getSuccess());
        ????????r.setCode(ResultCodeEnum.UNKNOWN_ERROR.getCode());
        ????????r.setMessage(ResultCodeEnum.UNKNOWN_ERROR.getMessage());
        ????????return?r;
        ????}

        ????//?設(shè)置結(jié)果,形參為結(jié)果枚舉
        ????public?static?R?setResult(ResultCodeEnum?result)?{
        ????????R?r?=?new?R();
        ????????r.setSuccess(result.getSuccess());
        ????????r.setCode(result.getCode());
        ????????r.setMessage(result.getMessage());
        ????????return?r;
        ????}

        ????/**------------使用鏈?zhǔn)骄幊蹋祷仡惐旧?----------**/

        ????//?自定義返回?cái)?shù)據(jù)
        ????public?R?data(Map?map)?{
        ????????this.setData(map);
        ????????return?this;
        ????}

        ????//?通用設(shè)置data
        ????public?R?data(String?key,Object?value)?{
        ????????this.data.put(key,?value);
        ????????return?this;
        ????}

        ????//?自定義狀態(tài)信息
        ????public?R?message(String?message)?{
        ????????this.setMessage(message);
        ????????return?this;
        ????}

        ????//?自定義狀態(tài)碼
        ????public?R?code(Integer?code)?{
        ????????this.setCode(code);
        ????????return?this;
        ????}

        ????//?自定義返回結(jié)果
        ????public?R?success(Boolean?success)?{
        ????????this.setSuccess(success);
        ????????return?this;
        ????}
        }

        控制層返回

        • 視圖層使用統(tǒng)一結(jié)果
        @RestController
        @RequestMapping("/api/v1/users")
        public?class?TeacherAdminController?{

        ????@Autowired
        ????private?UserService?userService;

        ????@GetMapping
        ????public?R?list()?{
        ????????List?list?=?teacherService.list(null);
        ????????return?R.ok().data("itms",?list).message("用戶列表");
        ????}
        }

        • json結(jié)果
        {
        ??"success":?true,
        ??"code":?20000,
        ??"message":?"查詢用戶列表",
        ??"data":?{
        ????"itms":?[
        ??????{
        ????????"id":?"1",
        ????????"username":?"admin",
        ????????"role":?"ADMIN",
        ????????"deleted":?false,
        ????????"gmtCreate":?"2019-12-26T15:32:29",
        ????????"gmtModified":?"2019-12-26T15:41:40"
        ??????},{
        ????????"id":?"2",
        ????????"username":?"zhangsan",
        ????????"role":?"USER",
        ????????"deleted":?false,
        ????????"gmtCreate":?"2019-12-26T15:32:29",
        ????????"gmtModified":?"2019-12-26T15:41:40"
        ??????}
        ????]
        ??}
        }

        統(tǒng)一結(jié)果類的使用參考了mybatis-plus中R對象的設(shè)計(jì)

        統(tǒng)一異常處理

        使用統(tǒng)一返回結(jié)果時(shí),還有一種情況,就是程序的保存是由于運(yùn)行時(shí)異常導(dǎo)致的結(jié)果,有些異常我們可以無法提前預(yù)知,不能正常走到我們r(jià)eturn的R對象返回。

        因此,我們需要定義一個(gè)統(tǒng)一的全局異常來捕獲這些信息,并作為一種結(jié)果返回控制層

        @ControllerAdvice

        該注解為統(tǒng)一異常處理的核心

        是一種作用于控制層的切面通知(Advice),該注解能夠?qū)⑼ㄓ玫腀ExceptionHandler、@InitBinder和@ModelAttributes方法收集到一個(gè)類型,并應(yīng)用到所有控制器上

        該類中的設(shè)計(jì)思路:

        1. 使用@ExceptionHandler注解捕獲指定或自定義的異常;
        2. 使用@ControllerAdvice集成@ExceptionHandler的方法到一個(gè)類中;
        3. 必須定義一個(gè)通用的異常捕獲方法,便于捕獲未定義的異常信息;
        4. 自定一個(gè)異常類,捕獲針對項(xiàng)目或業(yè)務(wù)的異常;
        5. 異常的對象信息補(bǔ)充到統(tǒng)一結(jié)果枚舉中;

        自定義全局異常類

        @Data
        public?class?CMSException?extends?RuntimeException?{
        ????private?Integer?code;

        ????public?CMSException(Integer?code,?String?message)?{
        ????????super(message);
        ????????this.code?=?code;
        ????}

        ????public?CMSException(ResultCodeEnum?resultCodeEnum)?{
        ????????super(resultCodeEnum.getMessage());
        ????????this.code?=?resultCodeEnum.getCode();
        ????}

        ????@Override
        ????public?String?toString()?{
        ????????return?"CMSException{"?+?"code="?+?code?+?",?message="?+?this.getMessage()?+?'}';
        ????}
        }

        統(tǒng)一異常處理器

        //?...
        import?org.springframework.web.bind.annotation.ControllerAdvice;
        import?org.springframework.web.bind.annotation.ExceptionHandler;
        import?org.springframework.web.bind.annotation.ResponseBody;

        @ControllerAdvice
        public?class?GlobalExceptionHandler?{

        ????/**--------?通用異常處理方法?--------**/
        ????@ExceptionHandler(Exception.class)
        ????@ResponseBody
        ????public?R?error(Exception?e)?
        {
        ????????e.printStackTrace();
        ????????return?R.error();?//?通用異常結(jié)果
        ????}

        ????/**--------?指定異常處理方法?--------**/
        ????@ExceptionHandler(NullPointerException.class)
        ????@ResponseBody
        ????public?R?error(NullPointerException?e)?
        {
        ????????e.printStackTrace();
        ????????return?R.setResult(ResultCodeEnum.NULL_POINT);
        ????}

        ????@ExceptionHandler(HttpClientErrorException.class)
        ????@ResponseBody
        ????public?R?error(IndexOutOfBoundsException?e)?
        {
        ????????e.printStackTrace();
        ????????return?R.setResult(ResultCodeEnum.HTTP_CLIENT_ERROR);
        ????}

        ????/**--------?自定義定異常處理方法?--------**/
        ????@ExceptionHandler(CMSException.class)
        ????@ResponseBody
        ????public?R?error(CMSException?e)?
        {
        ????????e.printStackTrace();
        ????????return?R.error().message(e.getMessage()).code(e.getCode());
        ????}
        }

        控制層展示

        以下為展示當(dāng)遇到null指定異常時(shí),返回的結(jié)果信息

        {
        ??"success":?false,
        ??"code":?20007,
        ??"message":?"空指針異常",
        ??"data":?{}
        }

        本節(jié)介紹統(tǒng)一異常較為簡略,推薦博客SpringBoot之全局異常處理

        統(tǒng)一日志收集

        日志是追蹤錯(cuò)誤定位問題的關(guān)鍵,尤其在生產(chǎn)環(huán)境中,需要及時(shí)修復(fù)熱部署,不會提供開發(fā)者debug的環(huán)境,此時(shí)日志將會是最快解決問題的關(guān)鍵

        日志的框架比較豐富,由于spring boot對logback的集成,因此推薦使用logback在項(xiàng)目中使用。

        Logback

        關(guān)于logback的配置和介紹,可以參考官網(wǎng)或推薦博客glmapper的logback博客,logback-spring.xml配置文件

        配置

        以下直接貼出配置信息,介紹信息科直接參考備注






        <configuration??scan="true"?scanPeriod="10?seconds">
        ????<contextName>logbackcontextName>

        ????
        ????<property?name="log.path"?value="D:/Documents/logs/edu"?/>

        ????
        ????
        ????<conversionRule?conversionWord="clr"?converterClass="org.springframework.boot.logging.logback.ColorConverter"?/>
        ????<conversionRule?conversionWord="wex"?converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"?/>
        ????<conversionRule?conversionWord="wEx"?converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"?/>
        ????
        ????<property?name="CONSOLE_LOG_PATTERN"?value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd?HH:mm:ss.SSS}){faint}?%clr(${LOG_LEVEL_PATTERN:-%5p})?%clr(${PID:-?}){magenta}?%clr(---){faint}?%clr([%15.15t]){faint}?%clr(%-40.40logger{39}){cyan}?%clr(:){faint}?%m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

        ????
        ????<appender?name="CONSOLE"?class="ch.qos.logback.core.ConsoleAppender">
        ????????
        ????????<filter?class="ch.qos.logback.classic.filter.ThresholdFilter">
        ????????????<level>debuglevel>
        ????????filter>
        ????????<encoder>
        ????????????<Pattern>${CONSOLE_LOG_PATTERN}Pattern>
        ????????????
        ????????????<charset>UTF-8charset>
        ????????encoder>
        ????appender>

        ????
        ????
        ????<appender?name="DEBUG_FILE"?class="ch.qos.logback.core.rolling.RollingFileAppender">
        ????????
        ????????<file>${log.path}/edu_debug.logfile>
        ????????
        ????????<encoder>
        ????????????<pattern>%d{yyyy-MM-dd?HH:mm:ss.SSS}?[%thread]?%-5level?%logger{50}?-?%msg%npattern>
        ????????????<charset>UTF-8charset>?
        ????????encoder>
        ????????
        ????????<rollingPolicy?class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        ????????????
        ????????????<fileNamePattern>${log.path}/web-debug-%d{yyyy-MM-dd}.%i.logfileNamePattern>
        ????????????<timeBasedFileNamingAndTriggeringPolicy?class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        ????????????????<maxFileSize>100MBmaxFileSize>
        ????????????timeBasedFileNamingAndTriggeringPolicy>
        ????????????
        ????????????<maxHistory>15maxHistory>
        ????????rollingPolicy>
        ????????
        ????????<filter?class="ch.qos.logback.classic.filter.LevelFilter">
        ????????????<level>debuglevel>
        ????????????<onMatch>ACCEPTonMatch>
        ????????????<onMismatch>DENYonMismatch>
        ????????filter>
        ????appender>

        ????
        ????<appender?name="INFO_FILE"?class="ch.qos.logback.core.rolling.RollingFileAppender">
        ????????
        ????????<file>${log.path}/edu_info.logfile>
        ????????
        ????????<encoder>
        ????????????<pattern>%d{yyyy-MM-dd?HH:mm:ss.SSS}?[%thread]?%-5level?%logger{50}?-?%msg%npattern>
        ????????????<charset>UTF-8charset>
        ????????encoder>
        ????????
        ????????<rollingPolicy?class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        ????????????
        ????????????<fileNamePattern>${log.path}/web-info-%d{yyyy-MM-dd}.%i.logfileNamePattern>
        ????????????<timeBasedFileNamingAndTriggeringPolicy?class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        ????????????????<maxFileSize>100MBmaxFileSize>
        ????????????timeBasedFileNamingAndTriggeringPolicy>
        ????????????
        ????????????<maxHistory>15maxHistory>
        ????????rollingPolicy>
        ????????
        ????????<filter?class="ch.qos.logback.classic.filter.LevelFilter">
        ????????????<level>infolevel>
        ????????????<onMatch>ACCEPTonMatch>
        ????????????<onMismatch>DENYonMismatch>
        ????????filter>
        ????appender>

        ????
        ????<appender?name="WARN_FILE"?class="ch.qos.logback.core.rolling.RollingFileAppender">
        ????????
        ????????<file>${log.path}/edu_warn.logfile>
        ????????
        ????????<encoder>
        ????????????<pattern>%d{yyyy-MM-dd?HH:mm:ss.SSS}?[%thread]?%-5level?%logger{50}?-?%msg%npattern>
        ????????????<charset>UTF-8charset>?
        ????????encoder>
        ????????
        ????????<rollingPolicy?class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        ????????????<fileNamePattern>${log.path}/web-warn-%d{yyyy-MM-dd}.%i.logfileNamePattern>
        ????????????<timeBasedFileNamingAndTriggeringPolicy?class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        ????????????????<maxFileSize>100MBmaxFileSize>
        ????????????timeBasedFileNamingAndTriggeringPolicy>
        ????????????
        ????????????<maxHistory>15maxHistory>
        ????????rollingPolicy>
        ????????
        ????????<filter?class="ch.qos.logback.classic.filter.LevelFilter">
        ????????????<level>warnlevel>
        ????????????<onMatch>ACCEPTonMatch>
        ????????????<onMismatch>DENYonMismatch>
        ????????filter>
        ????appender>

        ????
        ????<appender?name="ERROR_FILE"?class="ch.qos.logback.core.rolling.RollingFileAppender">
        ????????
        ????????<file>${log.path}/edu_error.logfile>
        ????????
        ????????<encoder>
        ????????????<pattern>%d{yyyy-MM-dd?HH:mm:ss.SSS}?[%thread]?%-5level?%logger{50}?-?%msg%npattern>
        ????????????<charset>UTF-8charset>?
        ????????encoder>
        ????????
        ????????<rollingPolicy?class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        ????????????<fileNamePattern>${log.path}/web-error-%d{yyyy-MM-dd}.%i.logfileNamePattern>
        ????????????<timeBasedFileNamingAndTriggeringPolicy?class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        ????????????????<maxFileSize>100MBmaxFileSize>
        ????????????timeBasedFileNamingAndTriggeringPolicy>
        ????????????
        ????????????<maxHistory>15maxHistory>
        ????????rollingPolicy>
        ????????
        ????????<filter?class="ch.qos.logback.classic.filter.LevelFilter">
        ????????????<level>ERRORlevel>
        ????????????<onMatch>ACCEPTonMatch>
        ????????????<onMismatch>DENYonMismatch>
        ????????filter>
        ????appender>

        ????

        ????

        ????

        ????
        ????
        ????<springProfile?name="dev">
        ????????<logger?name="com.cms"?level="info"/>
        ????????<root?level="info">
        ????????????<appender-ref?ref="CONSOLE"?/>
        ????????????<appender-ref?ref="DEBUG_FILE"?/>
        ????????????<appender-ref?ref="INFO_FILE"?/>
        ????????????<appender-ref?ref="WARN_FILE"?/>
        ????????????<appender-ref?ref="ERROR_FILE"?/>
        ????????root>
        ????springProfile>


        ????
        ????<springProfile?name="pro">
        ????????<logger?name="com.cms"?level="warn"/>
        ????????<root?level="info">
        ????????????<appender-ref?ref="ERROR_FILE"?/>
        ????????????<appender-ref?ref="WARN_FILE"?/>
        ????????root>
        ????springProfile>

        configuration>

        日志收集異常信息

        日志信息往往伴隨著異常信息的輸出,因此,我們需要修改統(tǒng)一異常的處理器,將異常信息以流的方式寫到日志文件中

        • 異常信息文件工具類
        @Slf4j
        public?class?ExceptionUtil?{

        ????/**
        ?????*?打印異常信息
        ?????*/

        ????public?static?String?getMessage(Exception?e)?{
        ????????String?swStr?=?null;
        ????????try?(StringWriter?sw?=?new?StringWriter();?PrintWriter?pw?=?new?PrintWriter(sw))?{
        ????????????e.printStackTrace(pw);
        ????????????pw.flush();
        ????????????sw.flush();
        ????????????swStr?=?sw.toString();
        ????????}?catch?(IOException?ex)?{
        ????????????ex.printStackTrace();
        ????????????log.error(ex.getMessage());
        ????????}
        ????????return?swStr;
        ????}
        }

        • 修改統(tǒng)一異常處理器,將異常方法中的直接打印改為日志輸入并打印
        //?...
        import?lombok.extern.slf4j.Slf4j;

        @ControllerAdvice
        @Slf4j
        public?class?GlobalExceptionHandler?{

        ????/**--------?通用異常處理方法?--------**/
        ????@ExceptionHandler(Exception.class)
        ????@ResponseBody
        ????public?R?error(Exception?e)?
        {
        ????????//?e.printStackTrace();
        ????????log.error(ExceptionUtil.getMessage(e));
        ????????return?R.error();
        ????}

        ???//?...
        }

        注意

        1. 日志的環(huán)境即spring.profiles.acticve,跟隨項(xiàng)目啟動;
        2. 啟動后,即可到自定目錄查找到生成的日志文件;
        3. 本地idea調(diào)試時(shí),推薦Grep Console插件可實(shí)現(xiàn)控制臺的自定義顏色輸出

        干貨分享

        最近將個(gè)人學(xué)習(xí)筆記整理成冊,使用PDF分享。關(guān)注我,回復(fù)如下代碼,即可獲得百度盤地址,無套路領(lǐng)??!

        ?001:《Java并發(fā)與高并發(fā)解決方案》學(xué)習(xí)筆記;?002:《深入JVM內(nèi)核——原理、診斷與優(yōu)化》學(xué)習(xí)筆記;?003:《Java面試寶典》?004:《Docker開源書》?005:《Kubernetes開源書》?006:《DDD速成(領(lǐng)域驅(qū)動設(shè)計(jì)速成)》?007:全部?008:加技術(shù)群討論

        加個(gè)關(guān)注不迷路

        喜歡就點(diǎn)個(gè)"在看"唄^_^

        瀏覽 66
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 成品人国产剧情久久 | 吻胸摸全身视频 | 成人无码mv色情在线观看视频an 日韩福利视频 | 2018天天操 | 韩国婬乱一级毛片视频无 |