spring拦截异常统一处理的几种方式以及优缺点(代码只是小部分,逻辑请根据实际情况编写)

前言:在做项目时,Dao和Service层的异常都会被抛到Controller层,如果我们在每个Controller的方法中都加上异常的try...catch处理就显的非常繁琐。所以springmvc为我们提供了统一的异常处理方案,可以把controller层的异常进行统一处理,这样既提高了代码的复用性也让异常处理代码和我们的业务代码解耦。

一丶通过自定义类配合@ControllerAdvice注解统一处理

1.新建自定义异常处理类,按照自己项目多种异常情况细分粒度。例如登陆异常,数据库异常等,其中@ExceptionHandler里面的value值是对应抛出时的异常类。然后封装异常信息给到前端,错误码和错误信息等。

@Slf4j
@Controller
@ControllerAdvice
public class ResultException {


    @Autowired
    private ConfigValueUtil configValueUtil;

    @ResponseBody
    @ExceptionHandler(value = LoginException.class)
    public ActionResult loginException(LoginException e) {
        ActionResult result = ActionResult.fail(ActionResultCode.Fail.getCode(), e.getMessage());
        if (Boolean.parseBoolean(configValueUtil.getMultiTenancy())) {
            printLoginLog(e, "登陆异常");
        }
        return result;
    }



}

2.业务层逻辑处理,抛出对应异常。通过throw new 生成对应的异常类,传入相应异常信息。

    @PostMapping("/Login")
    public ActionResult<LoginVO> login(Principal principal, @RequestParam Map<String, String> parameters, @RequestBody LoginForm loginForm) throws LoginException {
        TenantContextHolder.clear();
        UserInfo userInfo = new UserInfo();
		String phone = loginForm.getPhone();
        String phoneCode = loginForm.getPhoneCode();
        String timestampkey = loginForm.getTimestamp();
        if(StringUtil.isNotEmpty(phone)){
            List<UserEntity> userEntityList = userService.list(new QueryWrapper<UserEntity>().lambda().eq(UserEntity::getMobilePhone,phone));
            if(CollectionUtils.isNotEmpty(userEntityList)){
                String phoneCode1 = String.valueOf(redisUtil.getString(phone));
                if("null".equals(phoneCode1)){
                    throw new LoginException("验证码已过期!");
                }
                if(!(phoneCode1.equals(phoneCode))){
                    throw new LoginException("验证码输入错误!");
                }
 
    }

 二丶通过自定义类继承ResponseEntityExceptionHandler类,配合@ControllerAdvice注解统一处理

1.新建自定义类,继承ResponseEntityExceptionHandler类,重写handleExceptionInternal方法,构造对应的数据体返回给前端。其中handleExceptionOutputJson方法不可省略,如果要返回正确的数据体给到前端,必须构建对应的错误码,状态以及头部信息等,由于异常的底层类都是实现Throwable类,所以作为参数,方便赋值以及取值等操作。ErrorCodeAndMessageException是自定义错误信息相关的基类。

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class);

    @ExceptionHandler({Throwable.class})
    public ResponseEntity<Object> handleAll(Exception ex, WebRequest request) {
        return handleExceptionOutputJson(ex, request);
    }

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable Object body, HttpHeaders headers,
                                                             HttpStatus status, WebRequest request) {
        if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
        }
        return handleExceptionOutputJson(ex, request);
    }

    protected ResponseEntity<Object> handleExceptionOutputJson(Throwable e, WebRequest request) {
        CommonResult<Object> obj = new CommonResult();

        obj.setErrorCode(ErrorCode.FAILURE.getErrorCode());
        obj.setMessage(e.getMessage());
        if (e instanceof ErrorCodeAndMessageException) {
            obj.setErrorCode(((ErrorCodeAndMessageException) e).getErrorCode());
        } else if (e instanceof AccessDeniedException) {
            obj.setErrorCode(ErrorCode.UNAUTHORIZED.getErrorCode());
        } else if (e instanceof DuplicateKeyException) {
            obj.setErrorCode(ErrorCode.DUPLICATE_RECORD.getErrorCode());
            Throwable t = ((DuplicateKeyException) e).getCause();
            if (t instanceof SQLIntegrityConstraintViolationException) {
                obj.setMessage(((SQLIntegrityConstraintViolationException) t).getMessage());
            }
        } else {
            obj.setErrorCode(ErrorCode.FAILURE.getErrorCode());
        }

        LOGGER.error("", e);
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        return new ResponseEntity<>(SDJsonUtils.beanToString(obj), headers, HttpStatus.OK);
    }


}

3.新建ErrorCodeAndMessageException基类,继承RuntimeException。

public class ErrorCodeAndMessageException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    private int errorCode;

    public ErrorCodeAndMessageException(ErrorCode errorCode) {
        this(errorCode.getErrorCode(), errorCode.getMessage());
    }

    public ErrorCodeAndMessageException(int errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return errorCode;
    }

}

3.新建枚举类,罗列所有错误信息以及错误码,作为后续异常子类使用。

public enum ErrorCode {
    SUCCESS(0, "成功"),
    FAILURE(1, "失败"),

    /**
     * 缺少参数
     */
    MISSING_PARAMETERS(1000, "缺少参数"),
    /**
     * 参数非法
     */
    ILLEGAL_PARAMETER(1001, "参数非法"),
    BAD_HANYUPINYIN_OUTPUTFORMAT_COMBINATION(1002, "拼音码输出格式非法"),
    ILLEGAL_OPERATION(1003, "非法操作"),

    /**
     * 30000以上是数据库相关
     */
    INSERT_RECORD_FAILED(30000, "插入记录失败"),
    DUPLICATE_RECORD(30001, "重复的记录"),
    UPDATE_RECORD_FAILED(30002, "更新记录失败"),
    DELETE_RECORD_FAILED(30003, "删除记录失败"),
    RECORD_DOES_NOT_EXIST(30004, "记录不存在"),


    /**
     * 40000以上是安全相关
     */
    UNAUTHENTICATED(40000, "未认证"),
    UNAUTHORIZED(40001, "未授权"),
    ADMINISTRATOR_ACCOUNT_REQUIRED(40002, "需要管理员账号"),
    NORMAL_ACCOUNT_REQUIRED(40003, "需要普通账号"),
    USER_DOES_NOT_EXIST(40004, "用户不存在"),
    WRONG_PASSWORD(40005, "密码错误"),
    ;

    private int errorCode;
    private String message;

    ErrorCode(int errorCode, String message) {
        this.errorCode = errorCode;
        this.message = message;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public String getMessage() {
        return message;
    }
}

4.细分异常类,例如插入数据时的异常。其中就用到枚举类里面的3000异常错误。

public class InsertRecordFailedException extends ErrorCodeAndMessageException {
    public InsertRecordFailedException() {
        super(ErrorCode.INSERT_RECORD_FAILED);
    }

    public InsertRecordFailedException(String message) {
        super(ErrorCode.INSERT_RECORD_FAILED.getErrorCode(), message);
    }
}

5.业务层使用异常类。

    public DisinfectantUseRecord createDisinfectantUseRecord(CreateDisinfectantUseRecord.Params params) {
        DisinfectantUseRecord disinfectantUseRecord = new DisinfectantUseRecord();
        BeanUtils.copyProperties(params, disinfectantUseRecord);
        if (!StringUtils.isEmpty(disinfectantUseRecord.getLocationId())) {
            Location location = locationManager.getById(disinfectantUseRecord.getLocationId());
            disinfectantUseRecord.setLocation(location.getName());
        }
        if (!StringUtils.isEmpty(disinfectantUseRecord.getDisinfectantId())) {
            Disinfectant disinfectant = disinfectantManager.getById(disinfectantUseRecord.getDisinfectantId());
            disinfectantUseRecord.setDisinfectantName(disinfectant.getName());
            disinfectantUseRecord.setValidDays(disinfectant.getValidDays());
        }
        disinfectantUseRecord.setOperator(SecurityUtils.getCurrentUserName());
        int c = disinfectantUseRecordMapper.insert(disinfectantUseRecord);
        if (c != 1 || disinfectantUseRecord.getId() == null) {
            throw new InsertRecordFailedException();
        }
        return disinfectantUseRecord;
    }

三丶通过实现HandlerExceptionResolver接口

1.新建自定义类实现HandlerExceptionResolver 类,重写resolveException方法

@Component

public class MyHandlerExceptionResolver implements HandlerExceptionResolver {      

public ModelAndView resolveException(HttpServletRequest httpServletRequest,

HttpServletResponse httpServletResponse,

Object o, Exception e) {        

String msg = e.getMessage();        

ModelAndView modelAndView=new ModelAndView();        

modelAndView.addObject("msg",msg);        

modelAndView.setViewName("error.jsp");        

return modelAndView;  

  }

}

总结:从业务代码健壮还有异常粒度考虑,第二种会更适合,像工厂模式思想,用枚举的方式细分异常粒度,新加异常时改动比较小。其中实际开发中第一种会用得比较多,最后一种是以前比较老旧的处理方式,粒度不够细,如果不考虑异常业务细分情况下可以使用。

参考文献:spring实现统一异常处理_51CTO博客_springboot统一异常处理

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值