<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java 項目構建基礎:統(tǒng)一結果,統(tǒng)一異常,統(tǒng)一日志

          共 8808字,需瀏覽 18分鐘

           ·

          2022-04-16 18:01

          點擊下方“IT牧場”,選擇“設為星標”

          來源:juejin.im/post/

          5e073980f265da33f8653f2e

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

          統(tǒng)一結果返回

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

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

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

          結果類枚舉

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

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

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

          統(tǒng)一結果類

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

          注意:

          1. 外接只可以調用統(tǒng)一返回類的方法,不可以直接創(chuàng)建,影刺構造器私有;
          2. 內置靜態(tài)方法,返回對象;
          3. 為便于自定義統(tǒng)一結果的信息,建議使用鏈式編程,將返回對象設類本身,即return this;
          4. 響應數(shù)據(jù)由于為json格式,可定義為JsonObject或Map形式;
          @Data
          public?class?R?{
          ????private?Boolean?success;

          ????private?Integer?code;

          ????private?String?message;

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

          ????//?構造器私有
          ????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;
          ????}

          ????//?通用返回失敗,未知錯誤
          ????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;
          ????}

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

          ????/**------------使用鏈式編程,返回類本身-----------**/

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

          ????//?通用設置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;
          ????}

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

          控制層返回

          • 視圖層使用統(tǒng)一結果
          @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結果
          {
          ??"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)一結果類的使用參考了mybatis-plus中R對象的設計

          統(tǒng)一異常處理

          使用統(tǒng)一返回結果時,還有一種情況,就是程序的保存是由于運行時異常導致的結果,有些異常我們可以無法提前預知,不能正常走到我們return的R對象返回。

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

          @ControllerAdvice

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

          是一種作用于控制層的切面通知(Advice),該注解能夠將通用的@ExceptionHandler、@InitBinder和@ModelAttributes方法收集到一個類型,并應用到所有控制器上

          該類中的設計思路:

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

          自定義全局異常類

          @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();?//?通用異常結果
          ????}

          ????/**--------?指定異常處理方法?--------**/
          ????@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());
          ????}
          }

          控制層展示

          以下為展示當遇到null指定異常時,返回的結果信息

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

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

          統(tǒng)一日志收集

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

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

          Logback

          關于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,跟隨項目啟動;
          2. 啟動后,即可到自定目錄查找到生成的日志文件;
          3. 本地idea調試時,推薦Grep Console插件可實現(xiàn)控制臺的自定義顏色輸出

          干貨分享

          最近將個人學習筆記整理成冊,使用PDF分享。關注我,回復如下代碼,即可獲得百度盤地址,無套路領取!

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

          加個關注不迷路

          喜歡就點個"在看"唄^_^

          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操网 | 影音先锋啪啪啪 | 三级电影中文字幕 | 69视频网站| 欧美X X X欧美91 |