小学课程建设网站目标,移动网站优化,理卖做各视频网站的会员,国内专业建站平台搭建SpringBoot多模块微服务项目脚手架(三) 文章目录搭建SpringBoot多模块微服务项目脚手架(三)1.概述项目结构2.接口返回统一信息模板2.1.封装返回统一信息思路介绍2.2.封装json数据格式1.导入依赖2.封装code码3.封装json格式模板4.使用统一返回信息3.接口统一请求信息模板3.1…搭建SpringBoot多模块微服务项目脚手架(三) 文章目录搭建SpringBoot多模块微服务项目脚手架(三)1.概述项目结构2.接口返回统一信息模板2.1.封装返回统一信息思路介绍2.2.封装json数据格式1.导入依赖2.封装code码3.封装json格式模板4.使用统一返回信息3.接口统一请求信息模板3.1.封装统一请求信息1.导入依赖包2.封装请求信息类3.使用统一请求模板4.统一异常信息模板4.1.封装全局异常类1.导入依赖2.创建全局类封装异常3.使用统一异常处理信息4.2.封装特定异常4.3.封装自定义异常信息自定义异常信息分三个步骤1.定义自定义异常信息属性结构2.自定义异常处理返回的信息3.使用自定义异常类返回自定义信息5.封装统一日志模板5.1.统一日志信息设置1.封装日志格式2.设置错误日志输出到文件3.异常栈信息输出到错误日志文件4.禁用mybatis日志6.验收脚手架6.1.验收Swagger6.2.验收MybatisPlus1.实体类添加MybatisPlus注解2. 新增业务验证ID自增长、创建时间和修改时间自动填充功能3.分页查询4.逻辑删除6.3.验收统一请求和返回信息模块6.4.验收异常1.特定异常2.自定义异常6.5.验收日志7.总结福利1.概述
搭建SpringBoot多模块微服务项目脚手架 第三篇文章在搭建本篇文章内容前先查看 搭建SpringBoot多模块微服务项目脚手架一) 搭建SpringBoot多模块微服务项目脚手架二) 搭建基础环境。
第一篇文章 https://blog.csdn.net/m0_38039437/article/details/129408124 搭建SpringBoot多模块微服务框架
第二篇文章 https://blog.csdn.net/m0_38039437/article/details/129419467 封装《MybatisPlus》 封装《Swagger》 封装《自动生成代码工具》
第三篇文章 封装《接口返回统一信息模板》 封装《接口统一请求信息模板》 封装《统一异常信息模板》 封装《统一日志模板》
项目结构 2.接口返回统一信息模板
2.1.封装返回统一信息思路介绍
我们返回的数据使用Json格式返回的内容包含两个部分固定内容和可变内容根据这个需求介绍下设计一个返回数据的模板思路。 1.首先设计json数据格式结构例如下面的结构包含
success是否成功code状态码message提示信息data返回数据
2.设计json结构实现思路下面会用实例介绍
{success: true,code: 20000,message: 成功,data: {items: [{id: 1,name: 刘德华,intro: 毕业于师范大学数学系热爱教育事业执教数学思维6年有余}]}
}2.2.封装json数据格式
在common公共模块下common_utils 模块中封装返回数据json格式因为它是一个公共的功能并且不依赖业务封装好之后在每个项目中都能够使用。
1.导入依赖
封装返回统一信息类中需要使用lombok提供get和set方法因此我们在common模块中导入依赖。
!--lombok--
dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId
/dependency2.封装code码
状态码是一个常量值因此设计状态码使用枚举类和接口两种方式都可以这里使用接口设计状态码
在common_utils模块的java文件夹下新建包名 com.bruce.commonutils在com.bruce.commonutils包下新建ResultCode接口设计状态码
这里只列举了几个状态码在开发过程中会根据业务定义很多个失败的状态码通过不同的状态码确认是哪个业务模块抛出的问题。
package com.bruce.commonutils;/*** author bruce* create 2023/3/9 18:58*/
public interface ResultCode {//返回状态成功public static Integer SUCCESS 200;//返回用户模块没有登录权限状态码public static Integer ERROR 10000;//返回用户模块没有登录权限状态码public static Integer ERROR_USER_LOGIN 10000;//返回用户模块没有注册异常状态码public static Integer ERROR_USER_REGISTER 10001;//返回课程模块没有购买课程状态码public static Integer ERROR_COURSE_PAY 20000;
}3.封装json格式模板
在commonutils包下新建R类设计统一返回结果信息
json格式中每个字段都将他们定义为类的属性然后构造一个JavaBean类通过get和set方法赋值和取值。创建几个链式方法向调用者提供方法操作属性的值实现统一返回信息格式内容。
package com.bruce.commonutils;/*** author bruce* create 2023/3/9 19:11*/import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.HashMap;
import java.util.Map;//lombok为属性提供get和set方法
Data
public class R {//将json格式中的字段定义为类属性//Swagger注解如果没有集成Swagger可以不用建议使用ApiModelProperty(value 是否成功)private Boolean success;ApiModelProperty(value 返回码)private Integer code;ApiModelProperty(value 返回消息)private String message;ApiModelProperty(value 返回数据)private MapString, Object data new HashMapString, Object();//私有化构造器使其他类只能调用提供的方法private R() {}//结果返回成功静态方法public static R ok(){R r new R();r.setSuccess(true);r.setCode(ResultCode.SUCCESS);r.setMessage(成功);return r;}//结果返回失败静态方法public static R error(){R resultMessage new R();resultMessage.setSuccess(false);resultMessage.setCode(ResultCode.ERROR);resultMessage.setMessage(失败);return resultMessage;}public R success(Boolean success){this.setSuccess(success);return this;}// 提供自定义信息的方法由调用者传入public R message(String message){this.setMessage(message);return this;}public R code(Integer code){this.setCode(code);return this;}public R data(String key, Object value){this.data.put(key, value);return this;}public R data(MapString, Object map){this.setData(map);return this;}}
4.使用统一返回信息
统一返回结果格式放在了公共的包中业务模块使用该功能是跨模块调用因此需要在业务模块中加上common_utils模块依赖。 例如在service 模块的pom.xml 文件中导入common_utils依赖
!--统一返回结果格式--
dependencygroupIdcom.bruce/groupIdartifactIdcommon_utils/artifactIdversion0.0.1-SNAPSHOT/version
/dependency3.接口统一请求信息模板
前端请求接口时通常将数据封装到JSON对象中传递给接口在封装json数据时可以定义一个规范的数据模板所有请求这都使用同一个数据模板
3.1.封装统一请求信息
1.导入依赖包
封装请求信息类时会对每个请求字段信息做校验验证前端传来的信息是否符合规则如果不符合则直接返回。因此我们需要使用jakarta.validation依赖提供的参数校验功能。
这个校验功能只在请求信息中使用而封装统一请求信息只在common_utils模块中因此就将此依赖添加到该默认pom.xml文件中
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdcommon/artifactIdgroupIdcom.bruce/groupIdversion0.0.1-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdcommon_utils/artifactIddependenciesdependencygroupIdjakarta.validation/groupIdartifactIdjakarta.validation-api/artifactIdversion2.0.2/version/dependency/dependencies/project2.封装请求信息类
统一请求数据模板被各个业务模块调用它属于公共模块因此将它在common模块common_utils下和统一返回数据格式在同一个目录下。
在commonutils包下新建B类设计统一请求信息格式根据封装json结构设计如下
除了data属性所有的属性都是固定不变的请求每个接口都要携带这些属性。data属性定义为泛型用来接收前端请求接口传来的数据。
package com.bruce.commonutils;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.Serializable;/*** author bruce* create 2023/3/9 19:41*/
Data
ApiModel(基础请求)
public class BT implements Serializable {NotNull(message 渠道参数错误)ApiModelProperty(value 渠道, example AppStore)private String channelId;NotNull(message 设备ID参数错误)ApiModelProperty(value 设备ID, example C3C1A3EE-6B32-4C06-A83C-DFCC0A31A331)private String deviceNo;NotNull(message 请求端设备种类参数错误)ApiModelProperty(value 请求端设备种类, example Android iOS PC)private String deviceType;NotNull(message 请求端版本号参数错误)ApiModelProperty(value 请求端版本号, example 1.3.2)private String version;NotNull(message 请求端版本参数错误)ApiModelProperty(value 请求端版本, example 001003002)private Integer versionCode;ApiModelProperty(value 请求端产品, example wenyizhongguo)private String clientType;ApiModelProperty(value 分辨率, example 1280*720)private String resolution;ApiModelProperty(value 系统版本, example 8.0)private String osVersion;ApiModelProperty(value 厂商, example iPhone 8 Plus)private String deviceName;ApiModelProperty(value 定位信息)private String adCode;ApiModelProperty(value 网络类型, example 5G, wifi)private String networkAccess;ValidApiModelProperty(请求内容)private T data;
}
3.使用统一请求模板
1.统一请求放在了公共的包中业务模块使用该功能是跨模块调用因此需要在业务模块中加上common_utils模块依赖。 例如在service 模块的pom.xml 文件中导入common_utils依赖如果已存在则忽略。
!--统一返回结果格式--
dependencygroupIdcom.bruce/groupIdartifactIdcommon_utils/artifactIdversion0.0.1-SNAPSHOT/version
/dependency2.在业务模块的启动类上使用ComponentScan(basePackages {“com.bruce”})注解设置包扫描路径扫描到common包就可以。
4.统一异常信息模板
在请求接口遇到异常时会返回一个异常信息这个异常信息通常都是java异常类返回的信息。用户都是看了之后不知道是什么意思因此我们将这些返回的的异常信息统一进行处理返回用户能够看懂的信息。
封装统一异常模板包含三个类型之所以将他们分类就是为了在众多的异常中能够快速定位出问题出现在代码的位置和原因。 返回全局异常信息 全局异常类型是一个通用类型他没有特殊含义只要是异常信息都可以通过它返回给调用者。
特定异常处理 创建一些特别异常类型用来突出显示某些问题例如空指针异常类型、除数不能为0异常类型等等
自定义异常处理 自定义一些与业务相关的异常信息例如登录没有权限异常、下单失败异常等等
4.1.封装全局异常类
1.导入依赖
封装全局异常类时需要依赖统一返回信息对象所以需要在service_base模块中引入commonutils依赖
dependencygroupIdcom.bruce/groupIdartifactIdcommon_utils/artifactIdversion0.0.1-SNAPSHOT/version
/dependency2.创建全局类封装异常
1.在common公共模块的service_base公共模块下封装异常信息模板 2.在该模块的com.bruce.servicebase包下新建exceptionhandler包 3.在该包下新建GlobalExceptionHandler 类封装全局异常类
package com.bruce.servicebase.exceptionhandler;import com.bruce.commonutils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;/*** author bruce* create 2023/3/9 20:25*/
Slf4j
ControllerAdvice
public class GlobalExceptionHandler {//设置哪些异常调用这个统一异常方法ExceptionHandler(Exception.class)//返回数据ResponseBodypublic R error(Exception e) {e.printStackTrace();return R.error().message(执行了全局异常处理);}
}
3.使用统一异常处理信息
1.统一异常放在了公共的包中业务模块使用该功能是跨模块调用因此需要在业务模块中加上service_base公共模块依赖。 例如在service 模块的pom.xml 文件中导入service_base依赖如果已存在则忽略。
dependencygroupIdcom.bruce/groupIdartifactIdservice_base/artifactIdversion0.0.1-SNAPSHOT/version
/dependency2.在业务启动类中使用ComponentScan注解扫描common的包以便能够将统一异常处理类进行注册。
4.2.封装特定异常
特定异常处理和全局异常处理原理相同唯一的区别就是给某个异常类设置一个专用的返回信息。例如被除数为零时会调用ArithmeticException异常类我们可以专为该异常类添加一个异常信息。
在全局异常类GlobalExceptionHandler 封装特定异常类信息
package com.bruce.servicebase.exceptionhandler;import com.bruce.commonutils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;/*** author bruce* create 2023/3/9 20:25*/
Slf4j
ControllerAdvice
public class GlobalExceptionHandler {//设置哪些异常调用这个统一异常方法ExceptionHandler(Exception.class)//返回数据ResponseBodypublic R error(Exception e) {e.printStackTrace();return R.error().message(执行了全局异常处理);}//设置特定异常信息ExceptionHandler(ArithmeticException.class)ResponseBodypublic R error(ArithmeticException e) {e.printStackTrace();return R.error().message(特定异常被除数不能为零);}
}
4.3.封装自定义异常信息
当全局异常中没有我们想要的异常类型时我们可以自己创造一个异常类型用到我们的业务中。在不同的业务中返回不同的自定义信息。
自定义异常信息分三个步骤
定义自定义异常信息属性结构自定义异常处理返回的信息使用自定义异常返回信息
1.定义自定义异常信息属性结构
在exceptionhandler包新建一个自定义异常类BruceException类继承RuntimeException异常类。该类定义异常信息的属性。
package com.bruce.servicebase.exceptionhandler;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** author bruce* create 2023/3/9 20:48*/
Data
//生成有参构造器
AllArgsConstructor
//生成无餐构造器
NoArgsConstructor
public class BruceException extends RuntimeException {//定义状态码private Integer code;//定义异常信息private String msg;
}
2.自定义异常处理返回的信息
在全局异常类GlobalExceptionHandler 封装自定义异常类信息
package com.bruce.servicebase.exceptionhandler;import com.bruce.commonutils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;/*** author bruce* create 2023/3/9 20:25*/
Slf4j
ControllerAdvice
public class GlobalExceptionHandler {//设置哪些异常调用这个统一异常方法ExceptionHandler(Exception.class)//返回数据ResponseBodypublic R error(Exception e) {e.printStackTrace();return R.error().message(执行了全局异常处理);}//设置特定异常信息ExceptionHandler(ArithmeticException.class)ResponseBodypublic R error(ArithmeticException e) {//将日志输出到error文件中log.error(ExceptionUtil.getMessage(e));e.printStackTrace();return R.error().message(特定异常被除数不能为零);}//自定义异常类型ExceptionHandler(BruceException.class)ResponseBodypublic R error(BruceException e) {e.printStackTrace();//动态返回异常状态码和异常信息return R.error().code(e.getCode()).message(e.getMsg());}
}
3.使用自定义异常类返回自定义信息
在业务中需要抛出异常的位置手动调用自定义的异常类抛出自定义异常信息。
try {int a 10/0;
}catch(Exception e) {//给自定义异常类传入状态码和异常信息throw new BruceException(20001,出现自定义异常);
}5.封装统一日志模板
介绍SpringBoot项目如何对日志进行统一处理比如将info、error、warn级别的日志输出到文件设置日志格式等。
封装日志模板包含如下几个内容
创建日志配置文件设置日志输出格式错误日志输出到文件堆栈信息输出到文件
5.1.统一日志信息设置
1.封装日志格式
在业务模块中创建配置文件封装日志例如在 service_user 用户模块的resources文件夹下新建logback-spring.xml配置文件配置日志内容。
配置文件分为两个部分
springProfile标签以上的内容都是设置日志输出格式和日志输出到文件路径。springProfile标签设置日志应用到不同环境日志的级别。
日志级别设置介绍
日志的级别是在springProfile标签中设置root标签设置的是全局日志级别。logger标签是设置某个类或者某个包输出日志的级别例如只将sql输出设置为debug级别就可以指定mapper包输出的日志为debug。
?xml version1.0 encodingUTF-8?
configuration scantrue scanPeriod10 seconds!-- 日志级别从低到高分为TRACE DEBUG INFO WARN ERROR FATAL如果设置为WARN则低于WARN的信息都不会输出 --!-- scan:当此属性设置为true时配置文件如果发生改变将会被重新加载默认值为true --!-- scanPeriod:设置监测配置文件是否有修改的时间间隔如果没有给出时间单位默认单位是毫秒。当scan为true时此属性生效。默认的时间间隔为1分钟。 --!-- debug:当此属性设置为true时将打印出logback内部日志信息实时查看logback运行状态。默认值为false。 --contextNamelogback/contextName!-- name的值是变量的名称value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后可以使“${}”来使用变量。这里value的值将作为日志文件路径--property namelog.path value/Users/edy/Documents/javawork/parent_spring/!-- 彩色日志 --!-- 配置格式变量CONSOLE_LOG_PATTERN 彩色日志格式 --!-- magenta:洋红 --!-- boldMagenta:粗红--!-- cyan:青色 --!-- white:白色 --!-- magenta:洋红 --property nameCONSOLE_LOG_PATTERNvalue%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)/!--输出到控制台--appender nameCONSOLE classch.qos.logback.core.ConsoleAppenderfilter classch.qos.logback.classic.filter.ThresholdFilter!--这里设置的日志级别优先级高于配置文件底部springProfile标签的logger和root子标签设置的日志级别如果这里设置了日志级别会覆盖下面的设置的日志级别下面设置的日志级别不会生效。--!--levelWARN/level--/filterencoderPattern${CONSOLE_LOG_PATTERN}/Pattern!-- 设置字符集 --charsetUTF-8/charset/encoder/appender!--输出到文件--!-- 时间滚动输出 level为 INFO 日志 --appender nameINFO_FILE classch.qos.logback.core.rolling.RollingFileAppender!-- 正在记录的日志文件的路径及文件名 --file${log.path}/log_info.log/file!--日志文件输出格式--encoderpattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n/patterncharsetUTF-8/charset/encoder!-- 日志记录器的滚动策略按日期按大小记录 --rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy!-- 每天日志归档路径以及格式 --fileNamePattern${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log/fileNamePatterntimeBasedFileNamingAndTriggeringPolicy classch.qos.logback.core.rolling.SizeAndTimeBasedFNATPmaxFileSize100MB/maxFileSize/timeBasedFileNamingAndTriggeringPolicy!--日志文件保留天数--maxHistory15/maxHistory/rollingPolicy!-- 此日志文件只记录info级别的 --filter classch.qos.logback.classic.filter.LevelFilterlevelINFO/levelonMatchACCEPT/onMatchonMismatchDENY/onMismatch/filter/appender!-- 时间滚动输出 level为 WARN 日志 --appender nameWARN_FILE classch.qos.logback.core.rolling.RollingFileAppender!-- 正在记录的日志文件的路径及文件名 --file${log.path}/log_warn.log/file!--日志文件输出格式--encoderpattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n/patterncharsetUTF-8/charset !-- 此处设置字符集 --/encoder!-- 日志记录器的滚动策略按日期按大小记录 --rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicyfileNamePattern${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log/fileNamePatterntimeBasedFileNamingAndTriggeringPolicy classch.qos.logback.core.rolling.SizeAndTimeBasedFNATPmaxFileSize100MB/maxFileSize/timeBasedFileNamingAndTriggeringPolicy!--日志文件保留天数--maxHistory15/maxHistory/rollingPolicy!-- 此日志文件只记录warn级别的 --filter classch.qos.logback.classic.filter.LevelFilterlevelwarn/levelonMatchACCEPT/onMatchonMismatchDENY/onMismatch/filter/appender!-- 时间滚动输出 level为 ERROR 日志 --appender nameERROR_FILE classch.qos.logback.core.rolling.RollingFileAppender!-- 正在记录的日志文件的路径及文件名 --file${log.path}/log_error.log/file!--日志文件输出格式--encoderpattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n/patterncharsetUTF-8/charset !-- 此处设置字符集 --/encoder!-- 日志记录器的滚动策略按日期按大小记录 --rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicyfileNamePattern${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log/fileNamePatterntimeBasedFileNamingAndTriggeringPolicy classch.qos.logback.core.rolling.SizeAndTimeBasedFNATPmaxFileSize100MB/maxFileSize/timeBasedFileNamingAndTriggeringPolicy!--日志文件保留天数--maxHistory15/maxHistory/rollingPolicy!-- 此日志文件只记录ERROR级别的 --filter classch.qos.logback.classic.filter.LevelFilterlevelERROR/levelonMatchACCEPT/onMatchonMismatchDENY/onMismatch/filter/appender!--logger用来设置某一个包或者具体的某一个类的日志打印级别、以及指定appender。logger仅有一个name属性一个可选的level和一个可选的addtivity属性。name:用来指定受此logger约束的某一个包或者具体的某一个类。level:用来设置打印级别大小写无关TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF如果未设置此属性那么当前logger将会继承上级的级别。--!--使用mybatis的时候sql语句是debug下才会打印而这里我们只配置了info所以想要查看sql语句的话有以下两种操作第一种把root levelINFO改成root levelDEBUG这样就会打印sql不过这样日志那边会出现很多其他消息第二种就是单独给mapper下目录配置DEBUG模式代码如下这样配置sql语句会打印其他还是正常DEBUG级别--!--开发环境:打印控制台--springProfile namedev!--可以输出项目中的debug日志包括mybatis的sql日志--logger namecom.bruce.service_user.mapper levelDEBUG /!--root节点是必选节点用来指定最基础的日志输出级别只有一个level属性level:用来设置打印级别大小写无关TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF默认是DEBUG可以包含零个或多个appender元素,ref属性值是引用上面appender标签定义的名称,用来设置上面这些属性的日志级别。--root levelINFOappender-ref refCONSOLE /appender-ref refINFO_FILE /appender-ref refWARN_FILE /appender-ref refERROR_FILE //root/springProfile!--生产环境:输出到文件--springProfile nameproroot levelINFOappender-ref refCONSOLE /appender-ref refDEBUG_FILE /appender-ref refINFO_FILE /appender-ref refERROR_FILE /appender-ref refWARN_FILE //root/springProfile/configuration2.设置错误日志输出到文件
上面的配置文件已经配置好了日志输出的格式运行项目后就会自动在配置文件中指定的路径下创建info.log、error.log、warn.log三个日志文件。
通常代码报错都会自动调用异常类抛出异常信息。因此我们在异常类上添加注解就可以将异常信息都输出到error.log文件中。 此处会用到统一异常信息处理知识因此需要先配置好统一异常信息的内容。
在异常类上配置日志
在service_base公共模块中打开封装全局异常类GlobalExceptionHandler在该类上添加Slf4j注解。在该类处理异常的方法里使用log.error()方法将异常信息输出到error文件。输出日志的方法还有log.info、log.warn、log.debug不同的日志级别输出到不同级别的文件。
package com.bruce.servicebase.exceptionhandler;import com.bruce.commonutils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;/*** author bruce* create 2023/3/9 20:25*/
Slf4j
ControllerAdvice
public class GlobalExceptionHandler {//设置哪些异常调用这个统一异常方法ExceptionHandler(Exception.class)//返回数据ResponseBodypublic R error(Exception e) {e.printStackTrace();return R.error().message(执行了全局异常处理);}//设置特定异常信息ExceptionHandler(ArithmeticException.class)ResponseBodypublic R error(ArithmeticException e) {//将异常日志输出到error文件中只有错误信息没有上下文栈信息log.error(e.getMessage());e.printStackTrace();return R.error().message(特定异常被除数不能为零);}//自定义异常类型ExceptionHandler(BruceException.class)ResponseBodypublic R error(BruceException e) {e.printStackTrace();//动态返回异常状态码和异常信息return R.error().code(e.getCode()).message(e.getMsg());}
}
3.异常栈信息输出到错误日志文件
在service_base 公共模块的 exceptionhandler包新建ExceptionUtil类这个类的作用是将异常栈信息输出到错误日志文件中
package com.bruce.servicebase.exceptionhandler;import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;/*** author bruce* create 2023/3/9 20:43*/
public class ExceptionUtil {public static String getMessage(Exception e) {StringWriter sw null;PrintWriter pw null;try {sw new StringWriter();pw new PrintWriter(sw);// 将出错的栈信息输出到printWriter中e.printStackTrace(pw);pw.flush();sw.flush();} finally {if (sw ! null) {try {sw.close();} catch (IOException e1) {e1.printStackTrace();}}if (pw ! null) {pw.close();}}return sw.toString();}
}
在全局异常类GlobalExceptionHandler 通过log.err方法将异常输出到错误日志
//设置特定异常信息ExceptionHandler(ArithmeticException.class)ResponseBodypublic R error(ArithmeticException e) {//调用ExceptionUtil工具路将异常栈信息输出到错误日志error文件中log.error(ExceptionUtil.getMessage(e));// 控制台输出异常信息e.printStackTrace();return R.error().message(特定异常被除数不能为零);}4.禁用mybatis日志
在每个业务模块的application.properties文件中将mybatis日志删掉或禁用否则日志插件会有冲突。 例如 service_user 用户模块的application.properties文件中将下面的日志内容删除。
#mybatis日志
#logging.level.rootINFO
#mybatis-plus.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl6.验收脚手架
我们通过开发一个用户模块业务验收搭建的《搭建SpringBoot多模块微服务项目脚手架》各个功能是否可以正常使用。
6.1.验收Swagger
1.启动service_user 模块服务点击UserApplication 启动类启动服务。 2.在浏览器输入http://localhost:8001/swagger-ui/index.html 查看Swagger
6.2.验收MybatisPlus
MybatisPlus功能都在service_user模块上验证通过mybatisplus操作增删改查业务验收ID自增长、创建时间和修改时间自动填充、分页查询、逻辑删除功能。
1.实体类添加MybatisPlus注解
在service_user模块的entity 包中打开User实体类
在id属性上添加 TableId 注解自动生成ID。在 gmtCreate 属性 和 gmtModified 属性上添加TableField注解自动填充内容在 isDeleted 属性上添加 TableLogic 注解实现逻辑删除
package com.bruce.service_user.entity;import com.baomidou.mybatisplus.annotation.*;import java.util.Date;import java.io.Serializable;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;/*** p* * /p** author bruce* since 2023-03-09*/
Data
EqualsAndHashCode(callSuper false)
Accessors(chain true)
ApiModel(valueUser对象, description)
public class User implements Serializable {private static final long serialVersionUID 1L;ApiModelProperty(value 用户ID)TableId(value id, type IdType.ID_WORKER_STR)private String id;ApiModelProperty(value 姓名)private String name;ApiModelProperty(value 年龄)private Integer age;ApiModelProperty(value 邮箱)private String email;ApiModelProperty(value 逻辑删除 1true已删除 0false未删除)TableLogicprivate Boolean isDeleted;ApiModelProperty(value 创建时间)TableField(fill FieldFill.INSERT)private Date gmtCreate;ApiModelProperty(value 更新时间)TableField(fill FieldFill.INSERT_UPDATE)private Date gmtModified;}
2. 新增业务验证ID自增长、创建时间和修改时间自动填充功能
新增业务用来验证ID自增长、创建时间和修改时间自动填充功能。 在service_user模块,controller包中UserController类中开发新增用户接口 controller层开发新增用户接口
package com.bruce.service_user.controller;import com.bruce.commonutils.B;
import com.bruce.commonutils.R;
import com.bruce.service_user.entity.User;
import com.bruce.service_user.service.UserService;
import com.bruce.servicebase.exceptionhandler.BruceException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/*** p* 前端控制器* /p** author bruce* since 2023-03-09*/
Api(tags 用户管理)
RestController
RequestMapping(/service_user/user)
public class UserController {AutowiredUserService userService;ApiOperation(value 新增用户)PostMapping(addUser)public R addUser(RequestBody BUser b) {int userId userService.addUser(b);if (userId 1) {throw new BruceException(20003, 新增用户失败);}return R.ok();}
}
service层实现新增用户业务逻辑
package com.bruce.service_user.service.impl;import com.bruce.commonutils.B;
import com.bruce.service_user.entity.User;
import com.bruce.service_user.mapper.UserMapper;
import com.bruce.service_user.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;/*** p* 服务实现类* /p** author bruce* since 2023-03-09*/
Service
public class UserServiceImpl extends ServiceImplUserMapper, User implements UserService {Overridepublic int addUser(BUser b) {User user b.getData();int insert baseMapper.insert(user);return insert;}
}
调用新增用户接口
在Swagger上点击新增用户接口填写如下请求内容。并执行Execute。
{adCode: string,channelId: AppStore,clientType: wenyizhongguo,data: {age: 10,email: user163.com,name: 张三},deviceName: iPhone 8 Plus,deviceNo: C3C1A3EE-6B32-4C06-A83C-DFCC0A31A331,deviceType: Android iOS PC,networkAccess: 5G, wifi,osVersion: 8.0,resolution: 1280*720,version: 1.3.2,versionCode: 1003002
}查看控制台输出了新增成功的日志查看mysql数据库新增了一条数据从数据中查看id为自动生成创建时间和修改时间自动填充。删除状态填充了默认值。
-------------------------------------------------------------------------------------------------------
| id | name | age | email | is_deleted | gmt_create | gmt_modified |
-------------------------------------------------------------------------------------------------------
| 1634037440222609409 | 张三 | 10 | user163.com | 0 | 2023-03-10 11:44:19 | 2023-03-10 11:44:19 |
-------------------------------------------------------------------------------------------------------3.分页查询
通过分页查询用户列表验证封装的分页查询功能。
controller层开发分页查询接口 在service_user模块,controller包中UserController类中开发分页查询用户接口 ApiOperation(value 无条件分页查询用户)GetMapping(pageUser/{page}/{limit})public R queryUserList(ApiParam(name page, value 当前页码, required true)PathVariable long page,ApiParam(name limit, value 每页记录数, required true)PathVariable long limit) {//创建分页查询对象PageUser userPage new Page(page, limit);//调用分页查询对象传入分页对象和查询条件,结果封装到userPage对象userService.page(userPage, null);//获取查询结果ListUser records userPage.getRecords();//获取查询总条数long size userPage.getSize();//封装返回结果MapString, Object map new HashMap();map.put(total, size);map.put(data, records);return R.ok().message(成功).data(map);}调用接口查询用户 在Swagger上点击查询用户接口填写当前页和每页条数。并执行Execute。返回查询结果
{success: true,code: 200,message: 成功,data: {total: 10,data: [{id: 1634037440222609409,name: 张三,age: 10,email: user163.com,isDeleted: false,gmtCreate: 2023-03-10T03:44:19.00000:00,gmtModified: 2023-03-10T03:44:19.00000:00}]}
}4.逻辑删除
通过删除用户验证封装的逻辑删除功能。
controller层开发删除用户接口 在service_user模块,controller包中UserController类中开发删除用户接口
ApiOperation(value 根据ID删除用户, notes 逻辑删除用户)DeleteMapping({id})public R removeById(PathVariable String id) {boolean flag userService.removeById(id);if (flag) {return R.ok();} else {return R.error();}}调用接口删除用户 在Swagger上点击删除用户接口填写用户id。并执行Execute。通过mysql查询用户信息is_deleted字段变为1.
-------------------------------------------------------------------------------------------------------
| id | name | age | email | is_deleted | gmt_create | gmt_modified |
-------------------------------------------------------------------------------------------------------
| 1634037440222609409 | 张三 | 10 | user163.com | 1 | 2023-03-10 11:44:19 | 2023-03-10 11:44:19 |
-------------------------------------------------------------------------------------------------------
1 row in set (0.00 sec)
6.3.验收统一请求和返回信息模块
上面示例中开发的接口返回类型使用我们封装的R类对象请求参数类型使用我们封装的B类型对象。
ApiOperation(value 新增用户)PostMapping(addUser)public R addUser(RequestBody BUser b) {int userId userService.addUser(b);if (userId 1) {throw new BruceException(20003, 新增用户失败);}return R.ok();}6.4.验收异常
1.特定异常
通过一个被除数为0验证下发生异常后调用我们封装好的ArithmeticException 异常返回被除数不能为0信息。 controller层开发特定异常接口 在service_user模块,controller包中UserController类中开发特定异常接口
注意在接口中只定义了成功返回的信息没有判断失败时返回的信息是因为它发生异常后会走封装好的特定异常方法在该方法中定义好了返回信息格式。 ApiOperation(value 特定异常, notes 被除数为0异常 ArithmeticExceptio)GetMapping(exception/{first}/{secont})public R myException(ApiParam(name first, value 除数, required true)PathVariable long first,ApiParam(name secont, value 被除数, required true)PathVariable long secont) {long l first / secont;return R.ok().message(成功).data(data, l);}调用特定异常接口 在Swagger上点击特定异常接口除数填写1被除数填写0。并执行Execute。返回信息是特定异常封装的返回信息。
{success: false,code: 60000,message: 特定异常被除数不能为零,data: {}
}2.自定义异常
通过一个被除数为0验证下发生异常后调用我们封装好的BruceException 自定义异常类返回传入的code和message信息。 controller层开发特定异常接口 在service_user模块,controller包中UserController类中开发特定异常接口 ApiOperation(value 自定义异常, notes 被除数为0异常 自定义异常信息)GetMapping(exception/{first}/{secont})public R myException(ApiParam(name first, value 除数, required true)PathVariable long first,ApiParam(name secont, value 被除数, required true)PathVariable long secont) {try {long l first / secont;return R.ok().message(成功).data(data, l);} catch (Exception e) {//实例化自定义异常类对象传入code和messagethrow new BruceException(3001, 自定义异常);}}调用自定义异常接口 在Swagger上点击自定义异常接口除数填写1被除数填写0。并执行Execute。返回信息是自定义异常封装的返回信息。
{success: false,code: 3001,message: 自定义异常,data: {}
}6.5.验收日志
我们将日志封装后根据日志类型分别输出到 log_info.log 、 log_warn.log 、 log_error.log 三个文件中上面操作业务有成功和和异常的因此我们只要查看下日志文件就可以确认它是否可用。 7.总结
每次使用SpringBoot开发微服务项目时都要搭建一次完整的开发环境过程都很长配置也很复杂繁琐。但是我们将这个过程做成一个模板并命名为脚手架那么就不一样了。 将《搭建SpringBoot多模块微服务项目脚手架》上传到GitHub上每次需要的时候只要下载下来简单的修改下包名就可以开发业务这个体验就很舒服了。
福利
《搭建SpringBoot多模块微服务项目脚手架》 源代码已在Gitee仓库开源每个想学习的朋友都可以下载练手。 只做为学习勿做他用
Gitee仓库项目地址https://gitee.com/brucexiaogui/spring-boot-hand-ladder 文章转载自: http://www.morning.qfrmy.cn.gov.cn.qfrmy.cn http://www.morning.wbllx.cn.gov.cn.wbllx.cn http://www.morning.qhmhz.cn.gov.cn.qhmhz.cn http://www.morning.fwkq.cn.gov.cn.fwkq.cn http://www.morning.pyxtn.cn.gov.cn.pyxtn.cn http://www.morning.rmtmk.cn.gov.cn.rmtmk.cn http://www.morning.skcmt.cn.gov.cn.skcmt.cn http://www.morning.kbfzp.cn.gov.cn.kbfzp.cn http://www.morning.sblgt.cn.gov.cn.sblgt.cn http://www.morning.mljtx.cn.gov.cn.mljtx.cn http://www.morning.fhhry.cn.gov.cn.fhhry.cn http://www.morning.qbjgw.cn.gov.cn.qbjgw.cn http://www.morning.tlrxt.cn.gov.cn.tlrxt.cn http://www.morning.hfrbt.cn.gov.cn.hfrbt.cn http://www.morning.ynbyk.cn.gov.cn.ynbyk.cn http://www.morning.twdkt.cn.gov.cn.twdkt.cn http://www.morning.dhwyl.cn.gov.cn.dhwyl.cn http://www.morning.lkhgq.cn.gov.cn.lkhgq.cn http://www.morning.tfwg.cn.gov.cn.tfwg.cn http://www.morning.ldqzz.cn.gov.cn.ldqzz.cn http://www.morning.ddfp.cn.gov.cn.ddfp.cn http://www.morning.qfrsm.cn.gov.cn.qfrsm.cn http://www.morning.kmrgl.cn.gov.cn.kmrgl.cn http://www.morning.wgbsm.cn.gov.cn.wgbsm.cn http://www.morning.mnclk.cn.gov.cn.mnclk.cn http://www.morning.xdjsx.cn.gov.cn.xdjsx.cn http://www.morning.yrccw.cn.gov.cn.yrccw.cn http://www.morning.tgyzk.cn.gov.cn.tgyzk.cn http://www.morning.rzjfn.cn.gov.cn.rzjfn.cn http://www.morning.ykswq.cn.gov.cn.ykswq.cn http://www.morning.shnqh.cn.gov.cn.shnqh.cn http://www.morning.ytrbq.cn.gov.cn.ytrbq.cn http://www.morning.xqcgb.cn.gov.cn.xqcgb.cn http://www.morning.rwlns.cn.gov.cn.rwlns.cn http://www.morning.xirfr.cn.gov.cn.xirfr.cn http://www.morning.llthz.cn.gov.cn.llthz.cn http://www.morning.qxlyf.cn.gov.cn.qxlyf.cn http://www.morning.nafdmx.cn.gov.cn.nafdmx.cn http://www.morning.mcpdn.cn.gov.cn.mcpdn.cn http://www.morning.rfgc.cn.gov.cn.rfgc.cn http://www.morning.mltsc.cn.gov.cn.mltsc.cn http://www.morning.pskjm.cn.gov.cn.pskjm.cn http://www.morning.ysrtj.cn.gov.cn.ysrtj.cn http://www.morning.fbdtd.cn.gov.cn.fbdtd.cn http://www.morning.tdhxp.cn.gov.cn.tdhxp.cn http://www.morning.wskn.cn.gov.cn.wskn.cn http://www.morning.snzgg.cn.gov.cn.snzgg.cn http://www.morning.gkjyg.cn.gov.cn.gkjyg.cn http://www.morning.cmhkt.cn.gov.cn.cmhkt.cn http://www.morning.qbgdy.cn.gov.cn.qbgdy.cn http://www.morning.qnpyz.cn.gov.cn.qnpyz.cn http://www.morning.knzmb.cn.gov.cn.knzmb.cn http://www.morning.rscrj.cn.gov.cn.rscrj.cn http://www.morning.lflsq.cn.gov.cn.lflsq.cn http://www.morning.ns3nt8.cn.gov.cn.ns3nt8.cn http://www.morning.wjjxr.cn.gov.cn.wjjxr.cn http://www.morning.hlmkx.cn.gov.cn.hlmkx.cn http://www.morning.qhrdx.cn.gov.cn.qhrdx.cn http://www.morning.wiitw.com.gov.cn.wiitw.com http://www.morning.tldhq.cn.gov.cn.tldhq.cn http://www.morning.jpwkn.cn.gov.cn.jpwkn.cn http://www.morning.rszyf.cn.gov.cn.rszyf.cn http://www.morning.jqjnx.cn.gov.cn.jqjnx.cn http://www.morning.zgnng.cn.gov.cn.zgnng.cn http://www.morning.mhybs.cn.gov.cn.mhybs.cn http://www.morning.plwfx.cn.gov.cn.plwfx.cn http://www.morning.mkccd.cn.gov.cn.mkccd.cn http://www.morning.qzmnr.cn.gov.cn.qzmnr.cn http://www.morning.pkrb.cn.gov.cn.pkrb.cn http://www.morning.xkmrr.cn.gov.cn.xkmrr.cn http://www.morning.fdrwk.cn.gov.cn.fdrwk.cn http://www.morning.bnbtp.cn.gov.cn.bnbtp.cn http://www.morning.jpmcb.cn.gov.cn.jpmcb.cn http://www.morning.dmnqh.cn.gov.cn.dmnqh.cn http://www.morning.pqjpw.cn.gov.cn.pqjpw.cn http://www.morning.bmzxp.cn.gov.cn.bmzxp.cn http://www.morning.tynqy.cn.gov.cn.tynqy.cn http://www.morning.qnjcx.cn.gov.cn.qnjcx.cn http://www.morning.qhnmj.cn.gov.cn.qhnmj.cn http://www.morning.xqjz.cn.gov.cn.xqjz.cn