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..Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0..Final</version>
</dependency>

之前一直在考虑,入参约束在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>("", "成功", 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);
}

基于spring的异常一站式解决方案的更多相关文章

  1. 基于Spring Boot/Spring Session/Redis的分布式Session共享解决方案

    分布式Web网站一般都会碰到集群session共享问题,之前也做过一些Spring3的项目,当时解决这个问题做过两种方案,一是利用nginx,session交给nginx控制,但是这个需要额外工作较多 ...

  2. Spring Cloud Alibaba微服务一站式解决方案-开篇v2.2.1.RELEASE

    学习路线 **本人博客网站 **IT小神 www.itxiaoshen.com 生态概述 架构演进 什么是微服务 https://martinfowler.com/microservices/ Mic ...

  3. 主流微服务一站式解决方案Spring Cloud Alibaba入门看这篇就足够了

    学习路线 **本人博客网站 **IT小神 www.itxiaoshen.com 生态概述 架构演进 什么是微服务 https://martinfowler.com/microservices/ Mic ...

  4. 基于Jmeter跟Jenkins的自动化性能测试的一站式解决方案(转)

    www.MyException.Cn  网友分享于:2015-08-26  浏览:0次   基于Jmeter和Jenkins的自动化性能测试的一站式解决方案 作者: Yu, Qingguo Shen, ...

  5. spring security 一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中 配置的Bean,充分利用了Spring ...

  6. 基于Spring的RPC通讯模型.

    一.概念和原理 RPC(remote procedure call),远程过程调用,是客户端应用和服务端之间的会话.在客户端,它所需要的一些功能并不在该应用的实现范围之内,所以应用要向提供这些功能的其 ...

  7. 基于spring的安全管理框架-Spring Security

    什么是spring security? spring security是基于spring的安全框架.它提供全面的安全性解决方案,同时在Web请求级别和调用级别确认和授权.在Spring Framewo ...

  8. 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)

    通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 ja ...

  9. 基于Spring MVC的Web应用开发(三) - Resources

    基于Spring MVC的Web应用开发(3) - Resources 上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件. 注意到本项目的we ...

随机推荐

  1. guava的事件发布订阅功能

    事件的重要性,不用说很重要,在很多时候我们做完一个操作的时候,需要告知另外一个对象让他执行相应操作,比如当用户注册成功的时候,需要抛出一个注册成功的事件,那么有监听器捕获到这个事件,完成后续用户信息初 ...

  2. SpringBoot+MyBatis简单数据访问应用

    因为实习用的是MyBatis框架,所以写一篇关于SpringBoot整合MyBatis框架的总结. 一,Pom文件 <?xml version="1.0" encoding= ...

  3. leetcode算法总结

    算法思想 二分查找 贪心思想 双指针 排序 快速选择 堆排序 桶排序 搜索 BFS DFS Backtracking 分治 动态规划 分割整数 矩阵路径 斐波那契数列 最长递增子序列 最长公共子系列 ...

  4. 4.彻底理解synchronized

    1. synchronized简介 在学习知识前,我们先来看一个现象: public class SynchronizedDemo implements Runnable {    private s ...

  5. Opencv+pycharm+anaconda配置

    最简便的方法,没有之一 首先下载annconda:(不要从官网下,还要FQ,还要慢) https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 请注 ...

  6. umilit 修改 linux 最多可打开文件数

    ulimit -n 修改 临时修改:   ulimit -SHn 65535 永久修改:    echo '*  -  nofile  65535' >> /etc/security/li ...

  7. springboot拦截器注入service为空

    一般都是因为除了在拦截器之外,还需要在拦截器的配置类中,注册拦截器时没有使用spring的bean,而是使用了new创建bean造成的. @Configuration public class Web ...

  8. 转:kafka入门

    一.基本概念 介绍 Kafka是一个分布式的.可分区的.可复制的消息系统.它提供了普通消息系统的功能,但具有自己独特的设计. 这个独特的设计是什么样的呢? 首先让我们看几个基本的消息系统术语:Kafk ...

  9. C++复习11.函数的高级特性

    C++ 函数的高级特性 20131002 C++函数增加了重载(override), inline, const, virtual四种机制,重载和内联机制既可以用于全局函数,也可以用于类的成员函数.c ...

  10. 利用 LINQ的skip和Take 方法对List实现分页效果

    var testList=new List<string>(); )).Take(pageSize); //skip是跳过的条数,pageSize*(pageIndex-),Take 是返 ...