全局异常处理

全局异常处理

当定义了JSR303校验器后,校验不通过都会产生一个BindException,输出错误信息。若要对异常处理,我们可以定义一个全局异常处理的拦截器。

JSR303校验可以参考:
https://blog.csdn.net/wolfishness/article/details/101167488.

好处:

  • 1.可以实现对项目中所有产生的异常进行拦截,在同一个类中实现统一处理。避免异常漏处理的情况。

  • 2.项目中遇到异常,一般Try catch,这就不可避免在业务代码中加入这部分代码,显得复杂冗余。

一、统一异常处理

package com.bosssoft.bes.base.resolver;

import com.bosssoft.bes.base.coredata.vo.CommonResponse;
import com.bosssoft.bes.base.enums.SystemExceptionEnum;
import com.bosssoft.bes.base.exception.GlobalException;
import com.bosssoft.bes.base.utils.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
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.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * 统一异常全局异常处理类
 * @ClassName: GlobalExceptionResolver
 * @Description: TODO
 * @Author: lujinshan
 * @Date: 2019/8/10 17:47
 * @Version: 1.0.0
 */
@Slf4j
@ControllerAdvice(annotations = {RestController.class})
public class GlobalExceptionResolver {

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

    @ExceptionHandler({Exception.class})
    @ResponseBody
    public CommonResponse handleException(Exception e) {
        //判断是否是系统自定义异常
        if (e instanceof GlobalException){
            return ResultUtils.error(((GlobalException) e).getCode(), e.getMessage());
        }

        //判断是否是参数异常,并且没有使用@RequestBody
        if (e instanceof BindException) {
            //此处的BindException为Spring框架抛出的Validation异常
            BindException bindException = (BindException) e;
            //抛出的异常可能不止一个
            List<ObjectError> errors = bindException.getAllErrors();
            //获取第一个异常
            ObjectError error = errors.get(0);
            //获取异常信息
            String errorMsg = error.getDefaultMessage();
            return ResultUtils.error(20001, errorMsg);

        }
        //判断是否是参数异常,并且使用@RequestBody
        if (e instanceof MethodArgumentNotValidException) {
            LOGGER.info(((MethodArgumentNotValidException) e).getBindingResult().getAllErrors().toString());
            MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException) e;
            //抛出的异常可能不止一个
            List<ObjectError> errorList = methodArgumentNotValidException.getBindingResult().getAllErrors();
            List<String> result = new ArrayList<>();
            for (ObjectError objectError : errorList) {
                result.add(objectError.getDefaultMessage());
            }
            return ResultUtils.error(20000, result.toString());
        }
        LOGGER.error("【系统异常】{}", e);
        return ResultUtils.error(SystemExceptionEnum.SYSTEM_ERROR.getCode(), SystemExceptionEnum.SYSTEM_ERROR.getMessage());
    }

}

主要用到的是:@ControllerAdvice + @ExceptionHandler (Spring 框架)

@ControllerAdvice是一个@Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。

意思就是该注解会对所有@RequestMapping方法进行检查,拦截。并进行异常处理。

拦截的范围 可以选定拦截异常范围,最终输出,以Result(CodeMsg)的形式输出。将异常信息放入 msg中。

MethodArgumentNotValidException 和 BindException 都是为了处理JSR303异常

于是我们的业务异常和运行时的其他异常可以在这个类中一并处理。

目的:

  • 1.当参数校验不通过的时候,输出也是Result(CodeMsg),传给前端用于前端显示获取处理**

  • 2.当Service 出现业务逻辑错误的时候,这个时候我们可以直接抛出异常,让拦截器来捕捉,捕捉之后,就不需要冗余的代码来return 一个不符合业务逻辑的返回值来作为输出。如下:

二、自定义异常:

package com.bosssoft.bes.base.exception;

/**
 * 统一异常基类
 * @ClassName: GlobalException
 * @Description: TODO
 * @Author: lujinshan
 * @Date: 2019/8/10 23:28
 * @Version: 1.0.0
 */
public class GlobalException extends RuntimeException {


    private static final long serialVersionUID = -6025952423835016528L;
    /**
     * 异常码
     */
    private Integer code;

    /**
     * 统一异常
     * @param e
     */
    public GlobalException(GlobalException e){
        super(e.getMessage());
        this.code =  e.getCode();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

三、统一异常注解

package com.bosssoft.bes.base.annotation;

import java.lang.annotation.*;

/**
 * 统一异常注解
 * @author lujinshan
 * @date Created on 2019.08.12
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GlobalExceptionLog {
    /**
     * 设置默认值
     */
    String value() default "";
}

四、统一异常AOP切面

package com.bosssoft.bes.base.aop;

import com.bosssoft.bes.base.resolver.GlobalExceptionResolver;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;


/**
 * 全局异常切面
 * @ClassName: ParamCheckAop
 * @Description: TODO
 * @Author: lujinshan
 * @Date: 2019/8/12 13:40
 * @Version: 1.0.0
 */
@Component
@Aspect
public class GlobalExceptionAspect {
    private final static Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionAspect.class);

    @Autowired
    private GlobalExceptionResolver globalExceptionResolver;

    @Pointcut(value = "@annotation(com.bosssoft.bes.base.annotation.GlobalExceptionLog)")
    public void logPointCut() {
    }

    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) {

    }

    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, RuntimeException e) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        //创建时间
        LOGGER.error("dateTime={}" + dateFormat.format(new Date()));
        //URL
        LOGGER.error("url={}", request.getRequestURL());
        //method
        LOGGER.error("method={}", request.getMethod());
        //ip
        LOGGER.error("ip={}", request.getRemoteAddr());
        //class_method
        LOGGER.error("class_method={}", joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName());
        //args[]
        LOGGER.error("args={}", joinPoint.getArgs());
        //异常信息
        LOGGER.error("errMessage={}" + e);
        //输出异常堆栈信息
        e.printStackTrace();
    }

}

五、异常消息封装

package com.bosssoft.bes.base.utils;

import com.alibaba.fastjson.JSONObject;
import com.bosssoft.bes.base.coredata.vo.CommonResponse;
import com.bosssoft.bes.base.coredata.vo.ResponseHead;

/**
 * 异常信息封装
 * @ClassName: ResultUtils
 * @Description: TODO
 * @Author: lujinshan
 * @Date: 2019/8/12 14:57
 * @Version: 1.0.0
 */
public class ResultUtils {
    /**
     * 封装成功信息
     * @param object
     * @return
     */
    public static CommonResponse success(Object object){
        CommonResponse commonResponse = new CommonResponse();
        ResponseHead head = new ResponseHead();
        //成功
        head.setCode("0");
        head.setMessage("success");
        commonResponse.setResponseHead(head);
        commonResponse.setBody(JSONObject.parseObject(object.toString()));
        return commonResponse;
    }

    /**
     * 封装失败信息
     * @param code
     * @param message
     * @return
     */
    public static CommonResponse error(Integer code , String message){
        CommonResponse commonResponse = new CommonResponse();
        ResponseHead head = new ResponseHead();
        //填入异常码和异常信息
        head.setCode(code.toString());
        head.setMessage(message);
        commonResponse.setResponseHead(head);
        return commonResponse;
    }
}

注:CommonResponse是自己编写的输出数据格式

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值