美文网首页
Spring统一处理异常

Spring统一处理异常

作者: 超天大圣JR | 来源:发表于2019-12-17 12:29 被阅读0次

    一、前言

    之前敲代码的时候,避免不了各种try…catch, 如果业务复杂一点, 就会发现全都是try…catch

    try{
        ..........
    }catch(Exception1 e){
        ..........
    }catch(Exception2 e){
        ...........
    }catch(Exception3 e){
        ...........
    }
    

    这样其实代码既不简洁好看 ,我们敲着也烦, 一般我们可能想到用拦截器去处理, 但是既然现在Spring这么火,AOP大家也不陌生, 那么Spring一定为我们想好了这个解决办法.果然:

    @ExceptionHandler:源码

    //该注解作用对象为方法
    @Target({ElementType.METHOD})
    //在运行时有效
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ExceptionHandler {
        //value()可以指定异常类
        Class<? extends Throwable>[] value() default {};
    }
    
    

    @ControllerAdvice:源码

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    //bean对象交给spring管理生成
    @Component
    public @interface ControllerAdvice {
        @AliasFor("basePackages")
        String[] value() default {};
    
        @AliasFor("value")
        String[] basePackages() default {};
    
        Class<?>[] basePackageClasses() default {};
    
        Class<?>[] assignableTypes() default {};
    
        Class<? extends Annotation>[] annotations() default {};
    }
    

    该注解使用@Component注解,这样的话当我们使用<context:component-scan>扫描时也能扫描到。
    官方解释:

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

    • Spring4之前,@ControllerAdvice在同一调度的Servlet中协助所有控制器。Spring4已经改变:@ControllerAdvice支持配置控制器的子集,而默认的行为仍然可以利用。

    • 在Spring4中, @ControllerAdvice通过annotations(), basePackageClasses(), basePackages()方法定制用于选择控制器子集。

    不过据经验之谈,只有配合@ExceptionHandler最有用,其它两个不常用。

    二、应用代码

    这里需要声明的是,这个统一异常处理类,也是基于ControllerAdvice,也就是控制层切面的,如果是过滤器抛出的异常,不会被捕获!!!

    在@ControllerAdvice注解下的类,里面的方法用@ExceptionHandler注解修饰的方法,会将对应的异常交给对应的方法处理。
    示例:

    /**
     * 处理controller层服务器异常
     * 按照restful
     */
    @RestControllerAdvice
    public final class ServerExceptionHandler {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(ServerExceptionHandler.class);
    
        /**
         * 处理自定义异常
         *
         * @param e
         * @return
         */
        @ExceptionHandler(ServerException.class)
        private Result serverExceptionHandler(ServerException e) {
            return Result.fail(e.getMsgEnum());
        }
    
        /**
         * IllegalArgumentException 非法参数
         *
         * @param e
         * @return
         */
        @ExceptionHandler(IllegalArgumentException.class)
        private Result illegalArgumentExceptionHandler(IllegalArgumentException e) {
            return Result.illegalArgument(e);
        }
    
        /**
         * illegalStateException 非法状态
         *
         * @param e
         * @return
         */
        @ExceptionHandler(IllegalStateException.class)
        private Result illegalStateExceptionHandler(IllegalStateException e) {
            return Result.illegalState(e);
        }
    
        /**
         * 处理其他异常
         *
         * @param request
         * @param e
         * @return
         */
        @ExceptionHandler(Exception.class)
        private Result otherExceptionHandler(HttpServletRequest request, Exception e) {
            /*
             * 系统出现了一些不明确的异常
             * 这些问题都是必须要处理的
             */
            LOGGER.error("系统出现错误");
            LOGGER.error("出错的请求路径为:{}", request.getRequestURI());
            LOGGER.error("请求参数为:{}", request.getParameterMap());
            LOGGER.error("错误详细信息为:{}", e);
            return Result.error(e.getLocalizedMessage());
        }
    }
    

    自定义异常类

    /**
     * 自定义服务器异常
     * runtimeException
     * 所有异常应该包含 错误code,错误message.
     */
    public class ServerException extends RuntimeException {
    
        /**
         * 错误提示信息,包含code和message
         */
        private MsgEnum msgEnum;
    
        public ServerException(MsgEnum msgEnum) {
            super(msgEnum.getMessage());
            this.msgEnum = msgEnum;
        }
    
        public MsgEnum getMsgEnum() {
            return msgEnum;
        }
    }
    

    定义错误消息的枚举类

    /**
     * 定义所有消息
     * 主要用作返回错误信息,快速查找错误问题.
     * code分类:
     * *** 0 --- 业务执行成功 (表示业务执行的成功:查询成功、删除成功等)
     * *** -1 --- 系统未知错误 (表示系统出现未知错误:系统代码异常、数据错误等)
     * *** 其他小于0 --- 系统错误 (表示系统出现未知错误:系统代码异常、数据错误等)
     * *** 大于0 --- 业务不成功(表示系统没有错误但是对应的业务没有执行成功,常见场景:参数校验未通过、业务流程前置校验不通过等)
     */
    public enum MsgEnum {
    
        /**
         * 系统未知错误
         */
        ERROR(-1, "系统错误"),
        /**
         * json转换错误
         */
        JSON_CONVERT_ERROR(-2, "json转换错误"),
        /**
         * json转换错误
         */
        DATE_FORMAT_ERROR(-3, "时间转换错误"),
        /**
         * 数据转换错误
         */
        CONVERT_DATA_ERROR(-4, "数据转换错误"),
        /**
         * 数据复制错误
         */
        CLONE_ERROR(-5, "数据复制错误"),
        /**
         * 文件上传失败
         */
        UPLOAD_ERROR(-6, "文件上传失败"),
        /**
         * 文件下载失败
         */
        DOWNLOAD_ERROR(-7, "文件下载失败"),
        /**
         * 文件解析失败
         */
        FILE_IMPORT_ERROR(-8, "文件解析失败"),
        /**
         * 文件解析失败
         */
        LOGIN_ERROR(-9, "请确认是否登录"),
    
        /**
         * 业务执行成功
         */
        SUCCESS(0, "操作成功"),
    
        /**
         * 用户授权失败
         */
        AUTHORIZED_ERROR(1, "用户授权错误"),
        /**
         * 没有权限访问
         */
        NO_AUTHORIZED_ACCESS(2, "没有权限访问此接口"),
        /**
         * 非法参数
         */
        ILLEGAL_ARGUMENT(3, "非法参数"),
        /**
         * 非法状态
         */
        ILLEGAL_STATE(4, "非法状态"),
        /**
         * 没有认证
         */
        NO_AUTHENTICATED_ACCESS(5, "您已在其他地方登陆"),
        /**
         * 发送激活邮件失败
         */
        SEND_ACTIVE_EMAIL_ERROR(6, "发送邮件失败"),
        /**
         * 模板不能为空
         */
        TEMPLATE_NULL(7, "模板不能为空"),
        /**
         * excel文件不能为空
         */
        EXCEL_NULL(8, "excel文件不能为空"),
        /**
         * excel信息导入项不合法
         */
        EXCEL_IMPORT(9, "excel信息导入项不合法"),
        /**
         * 导出未配置
         */
        EXCEL_EXPORT(10, "表格导出未配置");
    
    
        private int code;
        private String message;
    
        MsgEnum(int code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    }
    

    自定义统一api返回的数据格式。

    /**
     * 统一api返回的数据格式。
     * code: {@link MsgEnum} 错误码
     * -1 系统错误(代码或数据错误)
     * 0 业务逻辑成功(业务执行成功:查询成功、修改成功)
     * 其他 业务执行失败(参数校验不成功等)
     * message: {@link MsgEnum} 错误提示信息
     * data: 返回的数据
     *
     * @author 赵腾飞
     */
    @ApiModel("返回给前台的格式")
    public final class Result {
    
        /**
         * 返回结果的code
         * -1 系统错误(代码或数据错误)
         * 0 业务逻辑成功(业务执行成功:查询成功、修改成功)
         * 其他 业务执行失败(参数校验不成功等)
         */
        @ApiModelProperty("返回结果的code")
        private int code;
        /**
         * 提示信息
         */
        @ApiModelProperty("提示信息")
        private String message;
        /**
         * 数据
         */
        @ApiModelProperty("请求的数据")
        private Object data;
    
        private Result() {
        }
    
        /**
         * 业务成功,只使用一个参数
         *
         * @param data
         * @return
         */
        public static Result ok(Object... data) {
            Result result = new Result();
            result.setCode(MsgEnum.SUCCESS.getCode());
            result.setMessage(MsgEnum.SUCCESS.getMessage());
            if (data.length > 0) {
                result.setData(data[0]);
            }
            return result;
        }
    
        /**
         * 系统错误,只使用一个参数
         *
         * @param data
         * @return
         */
        public static Result error(Object... data) {
            Result result = new Result();
            result.setCode(MsgEnum.ERROR.getCode());
            result.setMessage(MsgEnum.ERROR.getMessage());
            if (data.length > 0) {
                result.setData(data[0]);
            }
            return result;
        }
    
        /**
         * 非法参数,参数为空等
         *
         * @param e
         * @return
         */
        public static Result illegalArgument(IllegalArgumentException e) {
            Result result = new Result();
            result.setCode(MsgEnum.ILLEGAL_ARGUMENT.getCode());
            result.setMessage(e.getMessage());
            return result;
        }
    
        /**
         * 非法状态,状态不正确等
         *
         * @param e
         * @return
         */
        public static Result illegalState(IllegalStateException e) {
            Result result = new Result();
            result.setCode(MsgEnum.ILLEGAL_STATE.getCode());
            result.setMessage(e.getMessage());
            return result;
        }
    
        public static Result fail(MsgEnum msgEnum) {
            Result result = new Result();
            result.setCode(msgEnum.getCode());
            result.setMessage(msgEnum.getMessage());
            return result;
        }
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    }
    

    相关文章

      网友评论

          本文标题:Spring统一处理异常

          本文链接:https://www.haomeiwen.com/subject/lpatnctx.html