Java项目构建基础的三个统一,太厉害了!
作者:永動(dòng)的圖靈機(jī)
來(lái)源:juejin.im/post/5e073980f265da33f8653f2e
統(tǒng)一結(jié)果返回
目前的前后端開(kāi)發(fā)大部分?jǐn)?shù)據(jù)的傳輸格式都是json,因此定義一個(gè)統(tǒng)一規(guī)范的數(shù)據(jù)格式有利于前后端的交互與UI的展示。
統(tǒng)一結(jié)果的一般形式
-
是否響應(yīng)成功;
-
響應(yīng)狀態(tài)碼;
-
狀態(tài)碼描述;
-
響應(yīng)數(shù)據(jù)
-
其他標(biāo)識(shí)符
結(jié)果類(lèi)枚舉
前三者可定義結(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é)果類(lèi)
第5個(gè)屬于自定義返回,利用前4者可定義統(tǒng)一返回對(duì)象
注意:
-
外界只可以調(diào)用統(tǒng)一返回類(lèi)的方法,不可以直接創(chuàng)建,因此構(gòu)造器私有;
-
內(nèi)置靜態(tài)方法,返回對(duì)象;
-
為便于自定義統(tǒng)一結(jié)果的信息,建議使用鏈?zhǔn)骄幊?#xff0c;將返回對(duì)象設(shè)類(lèi)本身,即return this;
-
響應(yīng)數(shù)據(jù)由于為json格式,可定義為JsonObject或Map形式;
控制層返回
視圖層使用統(tǒng)一結(jié)果
@RestController?? @RequestMapping("/api/v1/users")?? public?class?TeacherAdminController?{??@Autowired??private?UserService?userService;??@GetMapping??public?R?list()?{??List<Teacher>?list?=?teacherService.list(null);??return?R.ok().data("itms",?list).message("用戶(hù)列表");??}?? }??????json結(jié)果
{??"success":?true,??"code":?20000,??"message":?"查詢(xún)用戶(hù)列表",??"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é)果類(lèi)的使用參考了mybatis-plus中R對(duì)象的設(shè)計(jì)
統(tǒng)一異常處理
使用統(tǒng)一返回結(jié)果時(shí),還有一種情況,就是程序的保存是由于運(yùn)行時(shí)異常導(dǎo)致的結(jié)果,有些異常我們可以無(wú)法提前預(yù)知,不能正常走到我們r(jià)eturn的R對(duì)象返回。
因此,我們需要定義一個(gè)統(tǒng)一的全局異常來(lái)捕獲這些信息,并作為一種結(jié)果返回控制層
@ControllerAdvice
該注解為統(tǒng)一異常處理的核心
是一種作用于控制層的切面通知(Advice),該注解能夠?qū)⑼ㄓ玫?#64;ExceptionHandler、@InitBinder和@ModelAttributes方法收集到一個(gè)類(lèi)型,并應(yīng)用到所有控制器上
該類(lèi)中的設(shè)計(jì)思路:
-
使用@ExceptionHandler注解捕獲指定或自定義的異常;
-
使用@ControllerAdvice集成@ExceptionHandler的方法到一個(gè)類(lèi)中;
-
必須定義一個(gè)通用的異常捕獲方法,便于捕獲未定義的異常信息;
-
自定一個(gè)異常類(lèi),捕獲針對(duì)項(xiàng)目或業(yè)務(wù)的異常;
-
異常的對(duì)象信息補(bǔ)充到統(tǒng)一結(jié)果枚舉中;
自定義全局異常類(lèi)
@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)一異常較為簡(jiǎn)略,詳細(xì)參考:
https://juejin.im/post/5cbc744a6fb9a0685a3f01a7
統(tǒng)一日志收集
日志是追蹤錯(cuò)誤定位問(wèn)題的關(guān)鍵,尤其在生產(chǎn)環(huán)境中,需要及時(shí)修復(fù)熱部署,不會(huì)提供開(kāi)發(fā)者debug的環(huán)境,此時(shí)日志將會(huì)是最快解決問(wèn)題的關(guān)鍵
日志的框架比較豐富,由于spring boot對(duì)logback的集成,因此推薦使用logback在項(xiàng)目中使用。
Logback
關(guān)于logback的配置和介紹,可以參考官網(wǎng)或推薦博客glmapper的logback博客,logback-spring.xml配置文件
https://blog.csdn.net/xu_san_duo/article/details/80364600
配置
以下直接貼出配置信息,介紹信息可以直接參考備注
<?xml?version="1.0"?encoding="UTF-8"?>?? <!--?日志級(jí)別從低到高分為T(mén)RACE?<?DEBUG?<?INFO?<?WARN?<?ERROR?<?FATAL,如果設(shè)置為WARN,則低于WARN的信息都不會(huì)輸出?-->?? <!--?scan:當(dāng)此屬性設(shè)置為true時(shí),配置文檔如果發(fā)生改變,將會(huì)被重新加載,默認(rèn)值為true?-->?? <!--?scanPeriod:設(shè)置監(jiān)測(cè)配置文檔是否有修改的時(shí)間間隔,如果沒(méi)有給出時(shí)間單位,默認(rèn)單位是毫秒。??當(dāng)scan為true時(shí),此屬性生效。默認(rèn)的時(shí)間間隔為1分鐘。?-->?? <!--?debug:當(dāng)此屬性設(shè)置為true時(shí),將打印出logback內(nèi)部日志信息,實(shí)時(shí)查看logback運(yùn)行狀態(tài)。默認(rèn)值為false。?-->?? <configuration??scan="true"?scanPeriod="10?seconds">??<contextName>logback</contextName>??<!--?name的值是變量的名稱(chēng),value的值時(shí)變量定義的值。通過(guò)定義的值會(huì)被插入到logger上下文中。定義后,可以使“${}”來(lái)使用變量。?-->??<property?name="log.path"?value="D:/Documents/logs/edu"?/>??<!--0.?日志格式和顏色渲染?-->??<!--?彩色日志依賴(lài)的渲染類(lèi)?-->??<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}}"/>??<!--1.?輸出到控制臺(tái)-->??<appender?name="CONSOLE"?class="ch.qos.logback.core.ConsoleAppender">??<!--此日志appender是為開(kāi)發(fā)使用,只配置最底級(jí)別,控制臺(tái)輸出的日志級(jí)別是大于或等于此級(jí)別的日志信息-->??<filter?class="ch.qos.logback.classic.filter.ThresholdFilter">??<level>debug</level>??</filter>??<encoder>??<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>??<!--?設(shè)置字符集?-->??<charset>UTF-8</charset>??</encoder>??</appender>??<!--2.?輸出到文檔-->??<!--?2.1?level為?DEBUG?日志,時(shí)間滾動(dòng)輸出??-->??<appender?name="DEBUG_FILE"?class="ch.qos.logback.core.rolling.RollingFileAppender">??<!--?正在記錄的日志文檔的路徑及文檔名?-->??<file>${log.path}/edu_debug.log</file>??<!--日志文檔輸出格式-->??<encoder>??<pattern>%d{yyyy-MM-dd?HH:mm:ss.SSS}?[%thread]?%-5level?%logger{50}?-?%msg%n</pattern>??<charset>UTF-8</charset>?<!--?設(shè)置字符集?-->??</encoder>??<!--?日志記錄器的滾動(dòng)策略,按日期,按大小記錄?-->??<rollingPolicy?class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">??<!--?日志歸檔?-->??<fileNamePattern>${log.path}/web-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>??<timeBasedFileNamingAndTriggeringPolicy?class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">??<maxFileSize>100MB</maxFileSize>??</timeBasedFileNamingAndTriggeringPolicy>??<!--日志文檔保留天數(shù)-->??<maxHistory>15</maxHistory>??</rollingPolicy>??<!--?此日志文檔只記錄debug級(jí)別的?-->??<filter?class="ch.qos.logback.classic.filter.LevelFilter">??<level>debug</level>??<onMatch>ACCEPT</onMatch>??<onMismatch>DENY</onMismatch>??</filter>??</appender>??<!--?2.2?level為?INFO?日志,時(shí)間滾動(dòng)輸出??-->??<appender?name="INFO_FILE"?class="ch.qos.logback.core.rolling.RollingFileAppender">??<!--?正在記錄的日志文檔的路徑及文檔名?-->??<file>${log.path}/edu_info.log</file>??<!--日志文檔輸出格式-->??<encoder>??<pattern>%d{yyyy-MM-dd?HH:mm:ss.SSS}?[%thread]?%-5level?%logger{50}?-?%msg%n</pattern>??<charset>UTF-8</charset>??</encoder>??<!--?日志記錄器的滾動(dòng)策略,按日期,按大小記錄?-->??<rollingPolicy?class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">??<!--?每天日志歸檔路徑以及格式?-->??<fileNamePattern>${log.path}/web-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>??<timeBasedFileNamingAndTriggeringPolicy?class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">??<maxFileSize>100MB</maxFileSize>??</timeBasedFileNamingAndTriggeringPolicy>??<!--日志文檔保留天數(shù)-->??<maxHistory>15</maxHistory>??</rollingPolicy>??<!--?此日志文檔只記錄info級(jí)別的?-->??<filter?class="ch.qos.logback.classic.filter.LevelFilter">??<level>info</level>??<onMatch>ACCEPT</onMatch>??<onMismatch>DENY</onMismatch>??</filter>??</appender>??<!--?2.3?level為?WARN?日志,時(shí)間滾動(dòng)輸出??-->??<appender?name="WARN_FILE"?class="ch.qos.logback.core.rolling.RollingFileAppender">??<!--?正在記錄的日志文檔的路徑及文檔名?-->??<file>${log.path}/edu_warn.log</file>??<!--日志文檔輸出格式-->??<encoder>??<pattern>%d{yyyy-MM-dd?HH:mm:ss.SSS}?[%thread]?%-5level?%logger{50}?-?%msg%n</pattern>??<charset>UTF-8</charset>?<!--?此處設(shè)置字符集?-->??</encoder>??<!--?日志記錄器的滾動(dòng)策略,按日期,按大小記錄?-->??<rollingPolicy?class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">??<fileNamePattern>${log.path}/web-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>??<timeBasedFileNamingAndTriggeringPolicy?class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">??<maxFileSize>100MB</maxFileSize>??</timeBasedFileNamingAndTriggeringPolicy>??<!--日志文檔保留天數(shù)-->??<maxHistory>15</maxHistory>??</rollingPolicy>??<!--?此日志文檔只記錄warn級(jí)別的?-->??<filter?class="ch.qos.logback.classic.filter.LevelFilter">??<level>warn</level>??<onMatch>ACCEPT</onMatch>??<onMismatch>DENY</onMismatch>??</filter>??</appender>??<!--?2.4?level為?ERROR?日志,時(shí)間滾動(dòng)輸出??-->??<appender?name="ERROR_FILE"?class="ch.qos.logback.core.rolling.RollingFileAppender">??<!--?正在記錄的日志文檔的路徑及文檔名?-->??<file>${log.path}/edu_error.log</file>??<!--日志文檔輸出格式-->??<encoder>??<pattern>%d{yyyy-MM-dd?HH:mm:ss.SSS}?[%thread]?%-5level?%logger{50}?-?%msg%n</pattern>??<charset>UTF-8</charset>?<!--?此處設(shè)置字符集?-->??</encoder>??<!--?日志記錄器的滾動(dòng)策略,按日期,按大小記錄?-->??<rollingPolicy?class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">??<fileNamePattern>${log.path}/web-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>??<timeBasedFileNamingAndTriggeringPolicy?class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">??<maxFileSize>100MB</maxFileSize>??</timeBasedFileNamingAndTriggeringPolicy>??<!--日志文檔保留天數(shù)-->??<maxHistory>15</maxHistory>??</rollingPolicy>??<!--?此日志文檔只記錄ERROR級(jí)別的?-->??<filter?class="ch.qos.logback.classic.filter.LevelFilter">??<level>ERROR</level>??<onMatch>ACCEPT</onMatch>??<onMismatch>DENY</onMismatch>??</filter>??</appender>??<!--??<logger>用來(lái)設(shè)置某一個(gè)包或者具體的某一個(gè)類(lèi)的日志打印級(jí)別、??以及指定<appender>。<logger>僅有一個(gè)name屬性,??一個(gè)可選的level和一個(gè)可選的addtivity屬性。??name:用來(lái)指定受此logger約束的某一個(gè)包或者具體的某一個(gè)類(lèi)。??level:用來(lái)設(shè)置打印級(jí)別,大小寫(xiě)無(wú)關(guān):TRACE,?DEBUG,?INFO,?WARN,?ERROR,?ALL?和?OFF,??還有一個(gè)特俗值INHERITED或者同義詞NULL,代表強(qiáng)制執(zhí)行上級(jí)的級(jí)別。??如果未設(shè)置此屬性,那么當(dāng)前l(fā)ogger將會(huì)繼承上級(jí)的級(jí)別。??addtivity:是否向上級(jí)logger傳遞打印信息。默認(rèn)是true。??<logger?name="org.springframework.web"?level="info"/>??<logger?name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor"?level="INFO"/>??-->??<!--??使用mybatis的時(shí)候,sql語(yǔ)句是debug下才會(huì)打印,而這里我們只配置了info,所以想要查看sql語(yǔ)句的話,有以下兩種操作:??第一種把<root?level="info">改成<root?level="DEBUG">這樣就會(huì)打印sql,不過(guò)這樣日志那邊會(huì)出現(xiàn)很多其他消息??第二種就是單獨(dú)給dao下目錄配置debug模式,代碼如下,這樣配置sql語(yǔ)句會(huì)打印,其他還是正常info級(jí)別:??【logging.level.org.mybatis=debug?logging.level.dao=debug】??-->??<!--??root節(jié)點(diǎn)是必選節(jié)點(diǎn),用來(lái)指定最基礎(chǔ)的日志輸出級(jí)別,只有一個(gè)level屬性??level:用來(lái)設(shè)置打印級(jí)別,大小寫(xiě)無(wú)關(guān):TRACE,?DEBUG,?INFO,?WARN,?ERROR,?ALL?和?OFF,??不能設(shè)置為INHERITED或者同義詞NULL。默認(rèn)是DEBUG??可以包含零個(gè)或多個(gè)元素,標(biāo)識(shí)這個(gè)appender將會(huì)添加到這個(gè)logger。??-->??<!--?4.?最終的策略?-->??<!--?4.1?開(kāi)發(fā)環(huán)境:打印控制臺(tái)-->??<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>??<!--?4.2?生產(chǎn)環(huán)境:輸出到文檔-->??<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)一異常的處理器,將異常信息以流的方式寫(xiě)到日志文件中
異常信息文件工具類(lèi)
@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();??}??//?...?? }?????注意
-
日志的環(huán)境即spring.profiles.acticve,跟隨項(xiàng)目啟動(dòng);
-
啟動(dòng)后,即可到自定目錄查找到生成的日志文件;
-
本地idea調(diào)試時(shí),推薦Grep Console插件可實(shí)現(xiàn)控制臺(tái)的自定義顏色輸出
詳細(xì)過(guò)程,可參考源代碼:
https://github.com/chetwhy/cloud-flow
總結(jié)
以上是生活随笔為你收集整理的Java项目构建基础的三个统一,太厉害了!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 红黑树的理解与 Java 实现
- 下一篇: Spring Boot 数据库连接池入门