今天吃饭时,被公司新来的同事问道:“项目controller层里好多都没写try,catch,难道异常不用处理吗?”。虽然正吃饭时被打扰,让我很讨厌,但是既然他诚心诚意的问了,本着爱护(收服)新员工的责任,我还是面带微笑给他讲解了一番:“是这样,异常@#*%*&~...”

  讲完,我爱吃的蒜香烤鱼已经凉了,但是我从同事的眼光中看到了对技术的崇拜和敬仰。哎,程序员就是这么朴实无华,且枯燥~

  

废话讲完,小二,上菜~~

@ControllerAdvice //表示所有被扫描到的controller都有效,相当于给controller中所有的方法写了try
public class ControllerExceptionHandler {
   //日志
    private final Logger log = LoggerFactory.getLogger(this.getClass().getName());

    /**
     * 处理运行时的异常,可优化为自定义异常类
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public ResponseEntity handlerException(RuntimeException ex) {
        log.error("发生业务异常!原因是:{}", ex.getMessage());
        return ResponseEntity.error(ex.getMessage());
    }


    /**
     * 对方法参数校验异常处理方法(仅对于表单提交有效,对于以json格式提交将会失效)
     * 如果是表单类型的提交,则spring会采用表单数据的处理类进行处理(进行参数校验错误时会抛出BindException异常)
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public ResponseEntity handlerBindException(BindException ex) {
        log.error("valid参数校验异常,提交参数类型form!原因是: {}", ex.getMessage());
        return handlerNotValidException(ex);
    }

    /**
     * 对方法参数校验异常处理方法(前端提交的方式为json格式出现异常时会被该异常类处理)
     * json格式提交时,spring会采用json数据的数据转换器进行处理(进行参数校验时错误是抛出MethodArgumentNotValidException异常)
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseEntity handlerArgumentNotValidException(MethodArgumentNotValidException ex) {
        log.error("valid参数校验异常,提交参数类型Json!原因是: {}", ex.getMessage());
        return handlerNotValidException(ex);
    }


    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseEntity handlerException2(Exception ex) {
        log.error("发生未知异常!原因是: {}", ex.getMessage());
        return ResponseEntity.error(ex.getMessage());
    }



    /**
     * valid校验,其参数错误异常的统一处理
     * @param e
     */
    public ResponseEntity handlerNotValidException(Exception e) {
        log.debug("begin resolve argument exception");
        BindingResult result;
        if (e instanceof BindException) {
            BindException exception = (BindException) e;
            result = exception.getBindingResult();
        } else {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            result = exception.getBindingResult();
        }

//        Map<String, Object> maps = getStringObjectMap(result);
        List<String> list = getStringObjectList(result);
        return ResponseEntity.error(list.toString());
    }



    /**
     * 这里只返回错误原因(建议)
     * 例子:[ID不能为空]
     */
    private List<String> getStringObjectList(BindingResult result) {
        List<String> list;
        if (result.hasErrors()) {
            List<FieldError> fieldErrors = result.getFieldErrors();
            list = new ArrayList<>(fieldErrors.size());
            fieldErrors.forEach(error -> {
                list.add(error.getDefaultMessage());
            });
        } else {
            list = Collections.EMPTY_LIST;
        }
        return list;
    }


    /**
     * 这里是返回:字段名=错误原因(不建议)
     */
    private Map<String, Object> getStringObjectMap(BindingResult result) {
        Map<String, Object> maps;
        if (result.hasErrors()) {
            List<FieldError> fieldErrors = result.getFieldErrors();
            maps = new HashMap<>(fieldErrors.size());
            fieldErrors.forEach(error -> {
                maps.put(error.getField(), error.getDefaultMessage());
            });
        } else {
            maps = Collections.EMPTY_MAP;
        }
        return maps;
    }
}

  

  我个人比较喜欢使用@Valid注解来校验数据,所以这里全局处理了其相关的异常。对于编写全局异常,非常考验程序员对于java异常的理解和运用。异常哪个地方抛,是谁抛,抛给谁,谁处理,这些都是难点,但是如果能吃透,基本上是个合格的程序员了。期望你能扶摇直上九万里~