zoukankan      html  css  js  c++  java
  • 基于spring的异常一站式解决方案

    https://segmentfault.com/a/1190000006749441#articleHeader4

    https://lrwinx.github.io/2016/04/28/%E5%A6%82%E4%BD%95%E4%BC%98%E9%9B%85%E7%9A%84%E8%AE%BE%E8%AE%A1java%E5%BC%82%E5%B8%B8/

    1,异常分类

      1,继承RuntimeException子类,比如nullPointException,称非受检异常,不要求写try/catch语句

       2,其他异常都是,比如数据库连接异常,称受检异常,要求显示写try/catch语句

        我们如何选择我们的异常种类,就一句,如果你的这个服务的编写者,你希望服务者显式调用try/catch语句,就抛出受检异常。

    但异常一定要接受的,不管是受检异常还是非受检异常。当你的非受检异常没有接受,就会一直往上面抛出,最后都没有人接受,

    如果应用是单线程的,整个应用就会停掉,在tomcat中不会停是因为tomcat有让这个应用恢复过来的功能。

    2,入参约束

    maven导入,@vaild是jsr303的标准,hibernate-validator是它的实现,在方法上的注解@validated是spring-context的注解

    <dependency>
                <groupId>javax.validation</groupId>
                <artifactId>validation-api</artifactId>
                <version>2.0.1.Final</version>
            </dependency>
            <dependency>
                <groupId>org.hibernate.validator</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>6.0.13.Final</version>
            </dependency>
    View Code

     之前一直在考虑,入参约束在controller做还是service做,最后发现service一定要做,因为service会互相调用,

    在其他service调用是难免会有不正确的入参,但是我们仍然可以在controller做,多一重保险

    controller

     @PostMapping("/insertAccessories")
        public SuccessResponse<Object> insertAccessories(@RequestBody @Valid DataRequest<AccessoriesDO> dataRequest) {
            accessoriesService.insertAccessories(dataRequest.getBody());
            return new SuccessResponse<Object>("0", "成功", null);
        }

    service (上面有@validated

    @Validated
    public interface AccessoriesService {
    
        /**
         * @author      : kooing
         * @Date        : 2018/4/22 13:24
         * @Desription  : 增加辅料
         * @return      :
         */
        public void insertAccessories(@Valid AccessoriesDO accessoriesDO);

    在实体里面或DTO加上你的约束

     @NotEmpty(message="姓名不能为空")
        private String memberUsername;
        @NotEmpty(message="密码不能为空")
        private String memberPassword;

    3,异常抛出和捕获

    所有非检查异常的基类

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public abstract class BaseServiceException extends RuntimeException {
        private String code;
        private String message;
    }

    后面继承他的要重写有参构造方法,可以一个模块一个异常类,也可以细分一点一个异常一个异常类

    @Data
    public class AccessoriesException extends BaseServiceException {
        public AccessoriesException(String errorCode, String errorMsg) {
            super(errorCode, errorMsg);
        }
    }

    如何捕获异常,在controller用@ExceptionHandler注解能捕获,但代码会冗余在一起,我是放在多个全局异常捕获,但是有个全局异常捕获会

    捕获基类异常的,如果spring配到是这个异常(或者他的父类,过程像catch一样)就不会继续需要更加切合的异常了,所有这个全局异常捕获

    有个优先级问题,最后我的方案是,最后业务的异常捕获不使用全局捕获,写在另外controller里面,再由业务的controller继承他,(勉强实现

    了代码分离和优先级的问题)

    @RestController
    @Slf4j
    public class AccessoriesExceptionHandler {
    
        @ExceptionHandler(AccessoriesException.class)
        public Object BaseServiceException(HttpServletRequest req, BaseServiceException e) {
            log.error("---AccessoriesException Handler---Host {} invokes url {} CODE:{}  MESSAGE: {}", req.getRemoteHost()
                    , req.getRequestURL()
                    , e.getCode()
                    , e.getMessage());
            ExceptionResponse exceptionResponse = new ExceptionResponse();
            exceptionResponse.setCode(e.getCode());
            exceptionResponse.setMessage(e.getMessage());
            return exceptionResponse;
        }
    }
    @Slf4j
    @RestController
    @RequestMapping("accessoriesRecord")
    public class AccessoriesRecordController extends AccessoriesExceptionHandler{

    全局异常捕获类,下面我分别捕获了404异常,controller入参异常,service入参异常,业务异常(没有对应的exceptionHandler),和Exception(避免应用关闭,但调试的时候注释掉,方便看报错)

    @RestController
    @ControllerAdvice
    @Slf4j
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(value = NoHandlerFoundException.class)
        public Object noHandlerFoundException(HttpServletRequest req, Exception e) throws Exception {
            log.error("---404 Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
            return new ExceptionResponse(GlobalCode.CODE_404, GlobalCode.MSG_404);
        }
    
        @ExceptionHandler(value = MethodArgumentNotValidException.class)
        public Object bindException(HttpServletRequest req, Exception e) throws Exception {
            log.error("---controller---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
            return new ExceptionResponse(GlobalCode.CODE_CONTROLLER, GlobalCode.MSG_CONTROLLER);
        }
    
        @ExceptionHandler(value = ConstraintViolationException.class)
        public Object methodArgumentNotValidException(HttpServletRequest req, Exception e) throws Exception {
            log.error("---service---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
            return new ExceptionResponse(GlobalCode.CODE_SERVICE, GlobalCode.MSG_SERVICE);
        }
    
        @ExceptionHandler(BaseServiceException.class)
        public Object BaseServiceException(HttpServletRequest req, BaseServiceException e) {
            log.error("---service Exception Handler---Host {} invokes url {} CODE:{}  MESSAGE: {}", req.getRemoteHost()
                    , req.getRequestURL()
                    , e.getCode()
                    , e.getMessage());
            ExceptionResponse exceptionResponse = new ExceptionResponse();
            exceptionResponse.setCode(e.getCode());
            exceptionResponse.setMessage(e.getMessage());
            return exceptionResponse;
        }
    
    
    //    @ExceptionHandler(value = Exception.class)
    //    public Object defaultErrorHandler(HttpServletRequest req, Exception e)  {
    //        log.error("---DefaultException Handler---Host {} invokes url {} ERROR: {}", req.getRemoteHost(), req.getRequestURL(), e.getMessage());
    //        return new ExceptionResponse(GlobalCode.CODE_UNKNOWN, GlobalCode.MSG_UNKNOWN);
    //    }
    
    }

    异常错误码,设计了两个string类的code和mssage,用总的异常码,和模块异常码

    public class ErrorCodeBase {
        public static final long Global = 10000L;
        public static final long ACCESSION = 20000L;
        public static final long MATERIAL = 30000L;
        public static final long MEMBER = 40000L;
        public static final long PACKGE_IT = 50000L;
        public static final long PRODUCT = 60000L;
    }
    public class GlobalCode {
        public static final String CODE_CONTROLLER = String.valueOf(ErrorCodeBase.Global + 1L);
        public static final String MSG_CONTROLLER = "控制层入参错误";
    
        public static final String CODE_SERVICE = String.valueOf(ErrorCodeBase.Global + 2L);
        public static final String MSG_SERVICE = "服务入参错误";
    
        public static final String CODE_404 = String.valueOf(ErrorCodeBase.Global + 3L);
        public static final String MSG_404 = "没有这个api接口";
    
        public static final String CODE_UNKNOWN = String.valueOf(ErrorCodeBase.Global + 4L);
        public static final String MSG_UNKNOWN = "服务器未知错误";
    }
    public class ResultCode {
        public static final String CODE_NUMBER = String.valueOf(ErrorCodeBase.ACCESSION + 1L);
        public static final String MSG_NUMBER = "数量不够";
    
        public static final String CODE_RECORD = String.valueOf(ErrorCodeBase.ACCESSION + 2L);
        public static final String MSG_RECORD = "没有这个辅料出入库纪录";
    }

    最后补上一个抛出异常的方法和一个服务的文件目录结构

    if(accessoriesRecordDOTemp == null){
                throw new AccessoriesException(ResultCode.CODE_RECORD, ResultCode.CODE_RECORD);
            }

  • 相关阅读:
    一行转多行 及多行转一行的 hive语句
    sparkSQL、dataframe
    特征工程
    python相关
    pyspark dataframe 格式数据输入 做逻辑回归
    hive sql 随机抽样
    pyspark 逻辑回归程序
    3.27模拟赛
    luogu P3217 [HNOI2011]数矩形
    bzoj 4403 序列统计
  • 原文地址:https://www.cnblogs.com/vhyc/p/8834913.html
Copyright © 2011-2022 走看看