平时开发过程中,无可避免我们需要处理各类异常,所以这里我们在公共模块中自定义统一异常,Spring Boot 提供 @RestControllerAdvice 注解统一异常处理,我们在GitEgg_Platform中新建gitegg-platform-boot子工程,此工程主要用于Spring Boot相关功能的自定义及扩展。
1、修改gitegg-platform-boot的pom.xml,添加spring-boot-starter-web和swagger依赖,设置optional为true,让这个包在项目之间依赖不传递。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.gitegg.platform</groupId> <artifactId>gitegg-platform-swagger</artifactId> <optional>true</optional> </dependency>
2、自定义通用响应消息类,Result和PageResult,一个是普通响应消息,一个是分页响应消息。
Result类:
package com.gitegg.platform.boot.common.base; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.*; /** * @ClassName: Result * @Description: 自定义通用响应类 * @author GitEgg * @date 2020年09月19日 下午9:24:50 */ @ApiModel(description = "通用响应类") @Getter @ToString public class Result<T> { @ApiModelProperty(value = "是否成功", required = true) private boolean success; @ApiModelProperty(value ="响应代码", required = true) private int code; @ApiModelProperty(value ="提示信息", required = true) private String msg; @ApiModelProperty(value ="响应数据") private T data; /** * * @param code * @param data * @param msg */ private Result(int code, T data, String msg) { this.success = ResultCodeEnum.SUCCESS.code == code; this.code = code; this.msg = msg; this.data = data; } /** * * @param resultCodeEnum */ private Result(ResultCodeEnum resultCodeEnum ) { this(resultCodeEnum.code, null, resultCodeEnum.msg); } /** * * @param resultCodeEnum * @param msg */ private Result(ResultCodeEnum resultCodeEnum , String msg) { this(resultCodeEnum, null, msg); } /** * * @param resultCodeEnum * @param data */ private Result(ResultCodeEnum resultCodeEnum , T data) { this(resultCodeEnum, data, resultCodeEnum.msg); } /** * * @param resultCodeEnum * @param data * @param msg */ private Result(ResultCodeEnum resultCodeEnum , T data, String msg) { this(resultCodeEnum.code, data, msg); } /** * * * @param data 数据 * @param <T> T 响应数据 * @ */ public static <T> Result<T> data(T data) { return data(data, ResultCodeEnum.SUCCESS.msg); } /** * * * @param data 数据 * @param msg 消息 * @param <T> T 响应数据 * @ */ public static <T> Result<T> data(T data, String msg) { return data(ResultCodeEnum.SUCCESS.code, data, msg); } /** * * * @param code 状态码 * @param data 数据 * @param msg 消息 * @param <T> T 响应数据 * @ */ public static <T> Result<T> data(int code, T data, String msg) { return new Result<>(code, data, msg); } /** * 返回Result * * @param * @param <T> T 响应数据 * @返回Result */ public static <T> Result<T> success() { return new Result<>(ResultCodeEnum.SUCCESS); } /** * 返回Result * * @param msg 消息 * @param <T> T 响应数据 * @返回Result */ public static <T> Result<T> success(String msg) { return new Result<>(ResultCodeEnum.SUCCESS, msg); } /** * 返回Result * * @param * @param <T> T 响应数据 * @返回Result */ public static <T> Result<T> success(ResultCodeEnum resultCodeEnum ) { return new Result<>(resultCodeEnum); } /** * 返回Result * * @param * @param msg 提示信息 * @param <T> T 响应数据 * @返回Result */ public static <T> Result<T> success(ResultCodeEnum resultCodeEnum , String msg) { return new Result<>(resultCodeEnum, msg); } /** * 返回Result * * @param <T> T 响应数据 * @返回Result */ public static <T> Result<T> error() { return new Result<>(ResultCodeEnum.ERROR, ResultCodeEnum.ERROR.msg); } /** * 返回Result * * @param msg 消息 * @param <T> T 响应数据 * @返回Result */ public static <T> Result<T> error(String msg) { return new Result<>(ResultCodeEnum.ERROR, msg); } /** * 返回Result * * @param code 状态码 * @param msg 消息 * @param <T> T 响应数据 * @返回Result */ public static <T> Result<T> error(int code, String msg) { return new Result<>(code, null, msg); } /** * 返回Result * * @param * @param <T> T 响应数据 * @返回Result */ public static <T> Result<T> error(ResultCodeEnum resultCodeEnum ) { return new Result<>(resultCodeEnum); } /** * 返回Result * * @param * @param msg 提示信息 * @param <T> T 响应数据 * @返回Result */ public static <T> Result<T> error(ResultCodeEnum resultCodeEnum , String msg) { return new Result<>(resultCodeEnum, msg); } /** * * @param <T> * @param flag * @return */ public static <T> Result<T> result(boolean flag) { return flag ? Result.success("操作成功") : Result.error("操作失败"); } }
PageResult类:
package com.gitegg.platform.boot.common.base; import java.util.List; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; /** * @ClassName: PageResult * @Description: 通用分页返回 * @author GitEgg * @date * @param <T> */ @Data @ApiModel("通用分页响应类") public class PageResult<T> { @ApiModelProperty(value = "是否成功", required = true) private boolean success; @ApiModelProperty(value ="响应代码", required = true) private int code; @ApiModelProperty(value ="提示信息", required = true) private String msg; @ApiModelProperty(value ="总数量", required = true) private long count; @ApiModelProperty(value ="分页数据") private List<T> data; public PageResult(long total, List<T> rows) { this.count = total; this.data = rows; this.code = ResultCodeEnum.SUCCESS.code; this.msg = ResultCodeEnum.SUCCESS.msg; } }
3、自定义通用响应消息枚举类ResultCodeEnum。
package com.gitegg.platform.boot.common.enums; /** * @ClassName: ResultCodeEnum * @Description: 自定义返回码枚举 * @author GitEgg * @date 2020年09月19日 下午11:49:45 */ public enum ResultCodeEnum { /** * 成功 */ SUCCESS(200, "操作成功"), /** * 系统错误 */ ERROR(500, "系统错误"), /** * 操作失败 */ FAILED(101, "操作失败"), /** * 未登录/登录超时 */ UNAUTHORIZED(102, "登录超时"), /** * 参数错误 */ PARAM_ERROR(103, "参数错误"), /** * 参数错误-已存在 */ INVALID_PARAM_EXIST(104, "请求参数已存在"), /** * 参数错误 */ INVALID_PARAM_EMPTY(105, "请求参数为空"), /** * 参数错误 */ PARAM_TYPE_MISMATCH(106, "参数类型不匹配"), /** * 参数错误 */ PARAM_VALID_ERROR(107, "参数校验失败"), /** * 参数错误 */ ILLEGAL_REQUEST(108, "非法请求"), /** * 验证码错误 */ INVALID_VCODE(204, "验证码错误"), /** * 用户名或密码错误 */ INVALID_USERNAME_PASSWORD(205, "账号或密码错误"), /** * */ INVALID_RE_PASSWORD(206, "两次输入密码不一致"), /** * 用户名或密码错误 */ INVALID_OLD_PASSWORD(207, "旧密码错误"), /** * 用户名重复 */ USERNAME_ALREADY_IN(208, "用户名已存在"), /** * 用户不存在 */ INVALID_USERNAME(209, "用户名不存在"), /** * 角色不存在 */ INVALID_ROLE(210, "角色不存在"), /** * 角色不存在 */ ROLE_USED(211, "角色使用中,不可删除"), /** * 没有权限 */ NO_PERMISSION(403, "当前用户无该接口权限"); public int code; public String msg; ResultCodeEnum(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
4、自定义异常类BusinessException和SystemException
package com.gitegg.platform.boot.common.exception; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.Setter; /** * @ClassName: BusinessException * @Description: 业务处理异常 * @author GitEgg * @date */ @Getter @Setter public class BusinessException extends RuntimeException { private int code; private String msg; public BusinessException() { this.code = ResultCodeEnum.FAILED.code; this.msg = ResultCodeEnum.FAILED.msg; } public BusinessException(String message) { this.code = ResultCodeEnum.FAILED.code; this.msg = message; } public BusinessException(int code, String msg) { this.code = code; this.msg = msg; } public BusinessException(Throwable cause) { super(cause); } public BusinessException(String message, Throwable cause) { super(message, cause); } }
package com.gitegg.platform.boot.common.exception; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import lombok.Getter; /** * @ClassName: SystemException * @Description: 系统处理异常 * @author GitEgg * @date */ @Getter public class SystemException extends RuntimeException { private int code; private String msg; public SystemException() { this.code = ResultCodeEnum.ERROR.code; this.msg = ResultCodeEnum.ERROR.msg; } public SystemException(String message) { this.code = ResultCodeEnum.ERROR.code; this.msg = message; } public SystemException(int code, String msg) { this.code = code; this.msg = msg; } public SystemException(Throwable cause) { super(cause); } public SystemException(String message, Throwable cause) { super(message, cause); } }
5、自定义统一异常处理类GitEggControllerAdvice.java
package com.gitegg.platform.boot.common.advice; import com.gitegg.platform.boot.common.base.Result; import com.gitegg.platform.boot.common.enums.ResultCodeEnum; import com.gitegg.platform.boot.common.exception.BusinessException; import com.gitegg.platform.boot.common.exception.SystemException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.ui.Model; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingPathVariableException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.NoHandlerFoundException; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import javax.validation.ConstraintViolationException; @Slf4j @RestControllerAdvice public class GitEggControllerAdvice { /** * 服务名 */ @Value("${spring.application.name}") private String serverName; /** * 微服务系统标识 */ private String errorSystem; @PostConstruct public void init() { this.errorSystem = new StringBuffer() .append(this.serverName) .append(": ").toString(); } /** * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器 */ @InitBinder public void initBinder(WebDataBinder binder) { } /** * 把值绑定到Model中,使全局@RequestMapping可以获取到该值 */ @ModelAttribute public void addAttributes(Model model) { } /** * 全局异常捕捉处理 */ @ExceptionHandler(value = {Exception.class}) public Result handlerException(Exception exception, HttpServletRequest request) { log.error("请求路径uri={},系统内部出现异常:{}", request.getRequestURI(), exception); Result result = Result.error(ResultCodeEnum.ERROR, errorSystem + exception.toString()); return result; } /** * 非法请求异常 */ @ExceptionHandler(value = { HttpMediaTypeNotAcceptableException.class, HttpMediaTypeNotSupportedException.class, HttpRequestMethodNotSupportedException.class, MissingServletRequestParameterException.class, NoHandlerFoundException.class, MissingPathVariableException.class, HttpMessageNotReadableException.class }) public Result handlerSpringAOPException(Exception exception) { Result result = Result.error(ResultCodeEnum.ILLEGAL_REQUEST, errorSystem + exception.getMessage()); return result; } /** * 非法请求异常-参数类型不匹配 */ @ExceptionHandler(value = MethodArgumentTypeMismatchException.class) public Result handlerSpringAOPException(MethodArgumentTypeMismatchException exception) { Result result = Result.error(ResultCodeEnum.PARAM_TYPE_MISMATCH, errorSystem + exception.getMessage()); return result; } /** * 非法请求-参数校验 */ @ExceptionHandler(value = {MethodArgumentNotValidException.class}) public Result handlerMethodArgumentNotValidException(MethodArgumentNotValidException methodArgumentNotValidException) { //获取异常字段及对应的异常信息 StringBuffer stringBuffer = new StringBuffer(); methodArgumentNotValidException.getBindingResult().getFieldErrors().stream() .map(t -> t.getField()+"=>"+t.getDefaultMessage()+" ") .forEach(e -> stringBuffer.append(e)); String errorMessage = stringBuffer.toString(); Result result = Result.error(ResultCodeEnum.PARAM_VALID_ERROR, errorSystem + errorMessage); return result; } /** * 非法请求异常-参数校验 */ @ExceptionHandler(value = {ConstraintViolationException.class}) public Result handlerConstraintViolationException(ConstraintViolationException constraintViolationException) { String errorMessage = constraintViolationException.getLocalizedMessage(); Result result = Result.error(ResultCodeEnum.PARAM_VALID_ERROR, errorSystem + errorMessage); return result; } /** * 自定义业务异常-BusinessException */ @ExceptionHandler(value = {BusinessException.class}) public Result handlerCustomException(BusinessException exception) { String errorMessage = exception.getMsg(); Result result = Result.error(exception.getCode(), errorSystem + errorMessage); return result; } /** * 自定义系统异常-SystemException */ @ExceptionHandler(value = {SystemException.class}) public Result handlerCustomException(SystemException exception) { String errorMessage = exception.getMsg(); Result result = Result.error(exception.getCode(), errorSystem + errorMessage); return result; } }
6、重新将GitEgg-Platform进行install,在GitEgg-Cloud中的gitegg-service引入gitegg-platform-boot
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>GitEgg-Cloud</artifactId> <groupId>com.gitegg.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>gitegg-service</artifactId> <packaging>pom</packaging> <modules> <module>gitegg-service-base</module> <module>gitegg-service-bigdata</module> <module>gitegg-service-system</module> </modules> <dependencies> <!-- gitegg Spring Boot自定义及扩展 --> <dependency> <groupId>com.gitegg.platform</groupId> <artifactId>gitegg-platform-boot</artifactId> </dependency> <!-- gitegg数据库驱动及连接池 --> <dependency> <groupId>com.gitegg.platform</groupId> <artifactId>gitegg-platform-db</artifactId> </dependency> <!-- gitegg mybatis-plus --> <dependency> <groupId>com.gitegg.platform</groupId> <artifactId>gitegg-platform-mybatis</artifactId> </dependency> <!-- gitegg swagger2-knife4j --> <dependency> <groupId>com.gitegg.platform</groupId> <artifactId>gitegg-platform-swagger</artifactId> </dependency> <!-- spring boot web核心包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- spring boot 健康监控 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies> </project>
7、修改SystemController.java、ISystemService.java和SystemServiceImpl.java增加异常处理的测试代码
SystemController.java:
package com.gitegg.service.system.controller; import com.gitegg.platform.boot.common.base.Result; import com.gitegg.platform.boot.common.exception.BusinessException; import com.gitegg.service.system.service.ISystemService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping(value = "system") @AllArgsConstructor @Api(tags = "gitegg-system") public class SystemController { private final ISystemService systemService; @GetMapping(value = "list") @ApiOperation(value = "system list接口") public Object list() { return systemService.list(); } @GetMapping(value = "page") @ApiOperation(value = "system page接口") public Object page() { return systemService.page(); } @GetMapping(value = "exception") @ApiOperation(value = "自定义异常及返回测试接口") public Result<String> exception() { return Result.data(systemService.exception()); } }
ISystemService.java:
package com.gitegg.service.system.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.gitegg.service.system.entity.SystemTable; import java.util.List; public interface ISystemService { List<SystemTable> list(); Page<SystemTable> page(); String exception(); }
SystemServiceImpl.java:
package com.gitegg.service.system.service.impl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.gitegg.platform.boot.common.exception.BusinessException; import com.gitegg.service.system.entity.SystemTable; import com.gitegg.service.system.mapper.SystemTableMapper; import com.gitegg.service.system.service.ISystemService; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; /** * */ @Service @AllArgsConstructor public class SystemServiceImpl implements ISystemService { private final SystemTableMapper systemTableMapper; @Override public List<SystemTable> list() { return systemTableMapper.list(); } @Override public Page<SystemTable> page() { Page<SystemTable> page = new Page<>(1, 10); List<SystemTable> records = systemTableMapper.page(page); page.setRecords(records); return page; } @Override public String exception() { throw new BusinessException("自定义异常"); // return "成功获得数据"; } }
8、运行GitEggSystemApplication,打开浏览器访问:http://127.0.0.1:8001/doc.html,然后点击左侧的异常处理接口,使用Swagger2进行测试,即可看到结果
image.png