在业务代码的开发中,往往会有这种情况,代码逻辑走到某一步,通过if的判断发现程序的逻辑无法继续往下面走了,否则会有一定的问题,这时候我们需要抛出异常组织程序往下进行,快速响应这个请求,并且回滚事物,这就是我们的自定义异常体系.
如何设计自定义异常体系,需要我们对自身系统的业务有一定的了解,例如一个商城,可能会有以下的自定义异常.
异常 | 场景 |
---|---|
OrderException.class | 订单业务发生异常 |
PayException.class | 支付业务发生异常 |
GoodsException.class | 商品业务发生异常 |
我们这里不做如何详细的异常体系设计,暂时只区分客户端异常,服务端异常.
(1)代码
CcmMallException.java
package com.ccm.common.exception;
/**
* @Description 自定义异常超类
* @Author ccm
* @CreateTime 2020/07/10 14:35
*/
public class CcmMallException extends RuntimeException {
}
CustomerException .java
package com.ccm.common.exception;
import lombok.Getter;
/**
* @Description 客户端异常
* @Author ccm
* @CreateTime 2020/7/10 14:40
*/
@Getter
public class CustomerException extends CcmMallException {
private CodeEnum codeEnum; //状态码
private String errorMessage; //错误详细信息
public CustomerException(String errorMessage) {
this.codeEnum = CodeEnum.ILLEGAL_REQUEST;
this.errorMessage = errorMessage;
}
public CustomerException(CodeEnum codeEnum, String errorMessage) {
this.codeEnum = codeEnum;
this.errorMessage = errorMessage;
}
}
ServerException.java
package com.ccm.common.exception;
import lombok.Getter;
/**
* @Description 服务端异常
* @Author ccm
* @CreateTime 2020/07/10 14:43
*/
@Getter
public class ServerException extends CcmMallException {
private CodeEnum codeEnum; //状态码
private String errorMessage; //错误详细信息
public ServerException(String errorMessage) {
this.codeEnum = CodeEnum.SYSTEM_INNER_ERROR;
this.errorMessage = errorMessage;
}
public ServerException(CodeEnum codeEnum, String errorMessage) {
this.codeEnum = codeEnum;
this.errorMessage = errorMessage;
}
}
package com.ccm.common.exception.result;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
/**
* @Description 统一结果集
* @Author ccm
* @CreateTime 2020/7/14 17:36
*/
@Slf4j
@Getter
@ToString
public class ResultSet<T> {
private Integer code; //状态码
private String codeMessage; //状态码信息
@Setter
private String errorMessage; //前后端对接错误信息
@Setter
private String errorMessageToUser; //给用户看的错误信息
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss:SSS",timezone = "GMT+8") //jackson指定时间转换格式
@JSONField(format="yyyy-MM-dd HH:mm:ss:SSS") //fastjson指定时间转换格式
private Date timestamp = new Date();
@Setter
private T data; //响应数据
//私有构造器
private ResultSet() {}
//响应成功,无响应数据
public static ResultSet success() {
ResultSet resultSet = new ResultSet();
resultSet.code = CodeEnum.SUCCESS.getCode();
resultSet.codeMessage = CodeEnum.SUCCESS.getCodeMessage();
return resultSet;
}
//响应成功,有响应数据
public static <T> ResultSet<T> success(T data) {
ResultSet resultSet = new ResultSet();
resultSet.code = CodeEnum.SUCCESS.getCode();
resultSet.codeMessage = CodeEnum.SUCCESS.getCodeMessage();
resultSet.data = data;
return resultSet;
}
//响应失败(仅供异常处理器使用,其他地方建议均以抛出异常的方式响应)
public static ResultSet error(CodeEnum codeEnum, String errorMessage,String errorMessageToUser) {
ResultSet resultSet = new ResultSet();
resultSet.code = codeEnum.getCode();
resultSet.codeMessage = codeEnum.getCodeMessage();
resultSet.errorMessage = errorMessage;
resultSet.setErrorMessageToUser(errorMessageToUser);
return resultSet;
}
}
(1)server.pom中加入公共包的依赖
<dependency>
<groupId>com.ccmgroupId>
<artifactId>commonartifactId>
<version>1.0.0version>
dependency>
(2)server-user服务中编写测试代码
package com.ccm.server.user.controller;
import com.alibaba.fastjson.JSONObject;
import com.ccm.common.exception.result.ResultSet;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description 结果集测试
* @Author ccm
* @CreateTime 2020/07/14 17:42
*/
@RestController
@RequestMapping(value = "resultTest")
@Api(tags = "结果集,全局异常处理器,自定义异常测试")
public class ResultTestController {
@ApiOperation(value = "结果集测试")
@GetMapping
public ResultSet<JSONObject> test01() {
JSONObject vo = new JSONObject();
vo.put("name","zhouzhiwu");
vo.put("age","10");
return ResultSet.success(vo);
}
}
(1)全局异常处理器的作用是捕获接口调用是产生的异常,在全局异常处理器中我们可以集中处理.
(2)我们定义了我们自己的结果集,所有的接口调用无论成功与否,http状态码都为200,具体的调用情况依据结果集中的code确定.
(1)为了让所有的微服务能够都使用全局异常处理器,我们将其定义在common包中.
(2)common.pom中加入依赖,主要作用是能够使用springbootweb相关的注解,编译不报错,所以依赖不传递.
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<optional>trueoptional>
dependency>
(3)common中编写全局异常处理器代码,代码中注释完善,所以不做过多的解释.
package com.ccm.common.handler;
import com.ccm.common.exception.CcmMallException;
import com.ccm.common.exception.CustomerException;
import com.ccm.common.exception.ServerException;
import com.ccm.common.exception.result.CodeEnum;
import com.ccm.common.exception.result.ResultSet;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.converter.HttpMessageNotReadableException;
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.annotation.ExceptionHandler;
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.validation.ConstraintViolationException;
/**
* @Description 全局异常处理器
* @Author ccm
* @CreateTime 2020/7/15 10:13
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 服务名
*/
@Value("${spring.application.name}")
private String serverName;
/**
* 错误信息前缀
*/
private String errorMessagePrefix;
@PostConstruct
public void init() {
this.errorMessagePrefix = new StringBuffer().append("(")
.append(this.serverName)
.append("服务")
.append("->")
.append(") ").toString();
}
/**
* @Description 处理系统内部异常(未知异常,入空指针,索引越界)
* @Author ccm
* @CreateTime 2020/7/15 10:13
* @Params [e, request]
* @Return com.ccm.common.exception.result.ResultSet
*/
@ExceptionHandler(value = {Exception.class})
public ResultSet handlerException(Exception e, HttpServletRequest request) {
log.error("请求路径uri={},系统内部出现异常:{}", request.getRequestURI(),e);
ResultSet resultSet = ResultSet.error(CodeEnum.SYSTEM_INNER_ERROR,errorMessagePrefix+e.toString(),"服务器繁忙,请稍后再试");
return resultSet;
}
/**
* @Description 非法请求异常(SpringAOP)
* @Author ccm
* @CreateTime 2020/7/15 10:14
* @Params [exception]
* @Return com.ccm.common.exception.result.ResultSet
*/
@ExceptionHandler(value = {
HttpMediaTypeNotAcceptableException.class,
HttpMediaTypeNotSupportedException.class,
HttpRequestMethodNotSupportedException.class,
MissingServletRequestParameterException.class,
NoHandlerFoundException.class,
MissingPathVariableException.class,
HttpMessageNotReadableException.class
})
public ResultSet handlerSpringAOPException(Exception exception) {
ResultSet resultSet = ResultSet.error(CodeEnum.ILLEGAL_REQUEST, errorMessagePrefix+exception.getMessage(),exception.getMessage());
return resultSet;
}
/**
* @Description 非法请求异常(@DateTimeFormat注解抛出异常)
* @Author ccm
* @CreateTime 2020/7/15 10:14
* @Params [e]
* @Return com.ccm.common.exception.result.ResultSet
*/
@ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
public ResultSet handlerSpringAOPException(MethodArgumentTypeMismatchException e) {
ResultSet resultSet = ResultSet.error(CodeEnum.ILLEGAL_REQUEST, errorMessagePrefix+e.getMessage(),e.getMessage());
return resultSet;
}
/**
* @Description 非法请求(处理spring validation参数校验抛出异常1)
* @Author ccm
* @CreateTime 2020/7/15 10:14
* @Params [methodArgumentNotValidException]
* @Return com.ccm.common.exception.result.ResultSet
*/
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResultSet 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();
ResultSet resultSet = ResultSet.error(CodeEnum.ILLEGAL_REQUEST, errorMessagePrefix+errorMessage,errorMessage);
return resultSet;
}
/**
* @Description 非法请求异常(处理spring validation参数校验抛出异常2)
* @Author ccm
* @CreateTime 2020/7/15 10:14
* @Params [constraintViolationException]
* @Return com.ccm.common.exception.result.ResultSet
*/
@ExceptionHandler(value = {ConstraintViolationException.class})
public ResultSet handlerConstraintViolationException(ConstraintViolationException constraintViolationException) {
String errorMessage = constraintViolationException.getLocalizedMessage();
ResultSet resultSet = ResultSet.error(CodeEnum.ILLEGAL_REQUEST,errorMessagePrefix+errorMessage,errorMessage);
return resultSet;
}
/**
* @Description 处理自定义异常-CustomException
* @Author ccm
* @CreateTime 2020/7/15 10:14
* @Params [e]
* @Return com.ccm.common.exception.result.ResultSet
*/
@ExceptionHandler(value = {CustomerException.class})
public ResultSet handlerCustomException(CustomerException e) {
String errorMessage = e.getErrorMessage();
ResultSet resultSet = ResultSet.error(e.getCodeEnum(), errorMessagePrefix+errorMessage,errorMessage);
return resultSet;
}
/**
* @Description 处理自定义异常-ServerException
* @Author ccm
* @CreateTime 2020/7/15 10:14
* @Params [e]
* @Return com.ccm.common.exception.result.ResultSet
*/
@ExceptionHandler(value = {ServerException.class})
public ResultSet handlerServerException(ServerException e) {
String errorMessage = e.getErrorMessage();
ResultSet resultSet = ResultSet.error(e.getCodeEnum(), errorMessagePrefix+errorMessage,errorMessage);
return resultSet;
}
}
(4)微服务中加入common包的依赖,server.pom中加入.
<dependency>
<groupId>com.ccmgroupId>
<artifactId>commonartifactId>
<version>1.0.0version>
<optional>trueoptional>
dependency>
(5)微服务启动类上增加注解扫描的范围,保证能够扫描到全局异常处理器,启动类上加上@ComponentScan(basePackages = “com.ccm”).
package com.ccm.server.user;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @Description server-user服务启动类
* @Author ccm
* @CreateTime 2020/07/03 14:04
*/
@ComponentScan(basePackages = "com.ccm")
@EnableSwagger2
@MapperScan(basePackages = "com.ccm.server.user.dao.mysql.mapper")
@SpringBootApplication //声明为一个启动类
@Import(value = PaginationInterceptor.class)
public class ServerUserApplication {
public static void main(String[] args) {
SpringApplication.run(ServerUserApplication.class,args);
}
}
package com.ccm.server.user.controller;
import com.alibaba.fastjson.JSONObject;
import com.ccm.common.exception.CustomerException;
import com.ccm.common.exception.result.ResultSet;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.hibernate.validator.constraints.Length;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description 结果集测试
* @Author ccm
* @CreateTime 2020/07/14 17:42
*/
@Validated
@RestController
@RequestMapping(value = "resultTest")
@Api(tags = "结果集,全局异常处理器,自定义异常测试")
public class ResultTestController {
@ApiOperation(value = "结果集测试")
@GetMapping
public ResultSet<JSONObject> test01() {
JSONObject vo = new JSONObject();
vo.put("name","zhouzhiwu");
vo.put("age","10");
return ResultSet.success(vo);
}
@ApiOperation(value = "全局处理器测试-未知异常")
@GetMapping(value = "test02")
public ResultSet<JSONObject> test02() {
JSONObject vo = new JSONObject();
vo.put("name","zhouzhiwu");
vo.put("age","10");
int i = 1/0;
return ResultSet.success(vo);
}
@ApiOperation(value = "全局处理器测试-自定义异常")
@GetMapping(value = "test03")
public ResultSet<JSONObject> test03() {
throw new CustomerException("自定义异常");
}
@ApiOperation(value = "全局处理器测试-参数校验框架抛出")
@GetMapping(value = "test04")
public ResultSet test04(@Length(min = 1,max = 2) @RequestParam String name) {
return ResultSet.success(name);
}
}
源码地址:https://gitee.com/chouchimoo/ccm-mall.git(本章节代码分支:zj-10)
您的点赞、收藏、转发和关注是我持续创作的动力!