全局异常处理
全局异常的作用:可以捕捉系统内部产生的错误,不管是参数错误,还是程序错误,还是突然代码发神经导致的错误。但是这些错误我们都可以拿到,并且输出对应我们想要前台看到的信息。让前台更优雅的看到这些错误,不至于看到都是系统提示的英文鸟语。
全局异常扫描注解
在application启动类中,需要配置全局异常扫描:@EnableAutoConfiguration
//启动类09
@SpringBootApplication //扫描所有包
@MapperScan(basePackages = "com.wt.mapper")
@ComponentScan(basePackages = {"com.wt"})
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
包的结构
处理代码
1、返回的处理结果类型JsonData,Commonreturntype类
package com.wt.response;
//通用返回格式
public class CommonReturnType {
//摆明对应的请求的返回处理结果 success或fail
private String status;
//status= success 则data返回json数据
//status= fail 返回的错误码格式
private Object data;
public static CommonReturnType create(Object result, String status) {
CommonReturnType type = new CommonReturnType();
type.setStatus(status);
type.setData(result);
return type;
}
//定义一个通用的创建方法
public static CommonReturnType create(Object result) {
return CommonReturnType.create(result, "success");
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
2、错误接口类的定义:CommonError
package com.wt.error;
public interface CommonError {
//获取错误信息码
public int getErrCode();
//获取错误信息
public String getErrMsg();
//获取共同的错误
public CommonError setErrMsg(String errMsg);
}
3、EmBusinessError定义枚举错误信息,这里存储了我们自己觉得需要抛出的异常文字定义,总之我们想要抛出啥错误信息,就需要在这里定义更多的枚举变量
package com.wt.error;
import javax.xml.stream.events.Comment;
public enum EmBusinessError implements CommonError {
//枚举变量要放在最开始
//通用错误类型 10001
PARAMETER_VALIDATION_ERROR(10001, "参数不合法"),
UNKNOWN_ERROR(10002, "未知错误");
EmBusinessError(int errCode, String errMsg) {
this.errCode = errCode;
this.errMsg = errMsg;
}
//成员变量可以靠后放置
private int errCode;
private String errMsg;
@Override
public int getErrCode() {
return this.errCode;
}
@Override
public String getErrMsg() {
return this.errMsg;
}
@Override
public CommonError setErrMsg(String errMsg) {
this.errMsg = errMsg;
return this;
}
public void setErrCode(int errCode) {
this.errCode = errCode;
}
}
4、BusinessException包装类业务异常类实现,这个又是另一种错误抛出的信息提示
package com.wt.error;
//业务错误处理
public class BusinessException extends RuntimeException implements CommonError{
private CommonError commonError;
//直接接收EmbusinessError 的传参用于构造业务异常
public BusinessException(CommonError commonError){
super();
this.commonError = commonError;
}
//接收自定义的errMsg的方式构造业务异常
public BusinessException(CommonError commonError, String errMsg) {
super();
this.commonError= commonError;
this.commonError.setErrMsg(errMsg);
}
@Override
public int getErrCode() {
return this.commonError.getErrCode();
}
@Override
public String getErrMsg() {
return this.commonError.getErrMsg();
}
@Override
public CommonError setErrMsg(String errMsg) {
this.commonError.setErrMsg(errMsg);
return this;
}
public CommonError getCommonError() {
return commonError;
}
}
5、GlobalExceptionHandler最后定义全局异常捕获类,这就相当于一个接口,一个监视器一样,我们程序出现错误就会有这样一个监管的人来逮住你
package com.wt.errorhandler;
import com.wt.error.BusinessException;
import com.wt.error.EmBusinessError;
import com.wt.response.CommonReturnType;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
//全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
//日志监测
public static final org.slf4j.Logger logger=LoggerFactory.getLogger(GlobalExceptionHandler.class);
//错误处理
@ExceptionHandler(Exception.class)
@ResponseBody
public CommonReturnType doError(HttpServletRequest httpServletReques, HttpServletResponse
httpServletResponse, Exception ex) {
ex.printStackTrace();
//map存储异常响应的数据
Map<String, Object> responseData = new HashMap<>();
//如果是业务异常类型
if (ex instanceof BusinessException) {
System.out.println("===这里错误了进来了么哟===");
BusinessException businessException = (BusinessException) ex;
//map中就放置 错误码和信息
responseData.put("errCode", businessException.getErrCode());
responseData.put("errMsg", businessException.getErrMsg());
//
} else if (ex instanceof ServletRequestBindingException) {
responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
//拦截到对应错误信息 输出对应的异常信息
responseData.put("errMsg", "url绑定路由问题");
} else if (ex instanceof NoHandlerFoundException) {
responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
responseData.put("errMsg", "没有找到对应的访问路径");
} else {
/*这里错误了没*/
responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
responseData.put("errMsg", EmBusinessError.UNKNOWN_ERROR.getErrMsg());
}
//错误日志输出
logger.error(responseData.toString());
System.out.println("================这个是错误集合"+responseData.toString());
//回写错误集合
return CommonReturnType.create(responseData,"fail");
}
}
测试
测试还是老规矩:在controller中进行测试,controller在api模块中,别搞忘了
//异常错误
@GetMapping("/exceptiontest")
public CommonReturnType exceptionTest() {
//这里传递一个参数 有个为空 代表参数错误
int i=1/0;
return CommonReturnType.create(null, "fail");
}
访问路径:http://localhost:8088/exceptiontest
全局异常检测下的验证器
项目结构图
引入依赖
<!--校验相关 -->
<!--apache工具包,方便常规操作 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<!--hibernate校验工具 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.4.Final</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<!--hutool工具 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.7</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-parent</artifactId>
<version>5.3.7</version>
<type>pom</type>
</dependency>
校验器
1、校验结果的判断ValidationResult
package com.wt.validator;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
//校验结果
//检验结果的正确与否
public class ValidationResult {
//检验结果是否有错
private boolean hasErrors = false ;
//存放错误信息 的map
private Map<String, String> errorMsgMap = new HashMap<>();
public boolean isHasErrors() {
return hasErrors;
}
public void setHasErrors(boolean hasErrors) {
this.hasErrors = hasErrors;
}
public Map<String, String> getErrorMsgMap() {
return errorMsgMap;
}
public void setErrorMsgMap(Map<String, String> errorMsgMap) {
this.errorMsgMap = errorMsgMap;
}
//实现通用的通过格式化字符串信息获取错误结果的msg方法
public String getErrMsg() {
return StringUtils.join(errorMsgMap.values().toArray(), ",");
}
}
2、校验器的实现类ValidatorImpl,这里会和全局异常配合使用,当校验的结果和规定的不符合,就会捕捉到错误的信息进行处理,并且返回规定的错误提示信息。vo类是页面向后台传递数据的对象,将页面数据封装与后台进行查询交互,这样一个对象可以很好的规范用户在页面的操作。免得搞一些小动作。
package com.wt.validator;
import cn.hutool.core.map.MapUtil;
import com.wt.error.BusinessException;
import com.wt.error.EmBusinessError;
import org.apache.ibatis.io.ResolverUtil;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
@Component
public class ValidatorImpl implements InitializingBean {
private Validator validator;
public void check(Object bean) throws BusinessException{
ValidationResult result = validate(bean);
if(MapUtil.isNotEmpty(result.getErrorMsgMap())) {
if(result.isHasErrors()) {
throw new BusinessException(EmBusinessError.UNKNOWN_ERROR, result.getErrorMsgMap().toString());
}
}
}
//实现校验方法并返回校验结果
private ValidationResult validate(Object bean) {
//验证器结果
final ValidationResult result = new ValidationResult();
//将验证器的结果放入集合
Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(bean);
//如果有错误
if(constraintViolationSet.size()>0){
//有错误
result.setHasErrors(true);
//打印错误集合 ,同时得到错误信息,和每个错误的属性名字 也就是错误code
constraintViolationSet.forEach(ConstraintViolation->{
String errMsg = ConstraintViolation.getMessage();
String propertyName = ConstraintViolation.getPropertyPath().toString();
result.getErrorMsgMap().put(propertyName, errMsg);
});
}
return result;
}
@Override
public void afterPropertiesSet() throws Exception {
//将hibernate validator 通过工厂的初始化方式使其实例化
this.validator = Validation.buildDefaultValidatorFactory().getValidator();
}
}
3、创建Vo对象
package com.wt.controller.viewobject;
import lombok.*;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
//这个对象的用处还是蛮大的 一般用于界面封装界面提交的信息
//你想想 每次页面提交 信息的个数和格式 估摸着都和数据库的不一样
//那么我们在输入信息的时候就将信息封装成一个对象 那这个对象到后台去校验 操作
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
public class TestVo {
@NotNull
private Integer id;
//这里给他规定字符的格式
@NotBlank
@Length(min=1, max=3, message = "名称长度1-3")
private String name;
private Integer age;
}
测试
老规矩还是在controller中进行添加测试模块,如果有不懂的需要看我前面的搭建。我是不会告诉你直接答案的。
@Resource
private ValidatorImpl validator;
@GetMapping("/validatorTest")
public CommonReturnType testException(TestVo testVo) {
validator.check(testVo);
return CommonReturnType.create("success");
}
这里由于是封装了一个对象,参数是一个参数,所以用那个swagger2并不是很友好,不知道怎么去封装一个对象那就别玩了,还是老老实实去用一下postman,提交id,name。
输入和他规定不符合的,那么就会出现他提示我的错出来