基于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..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的异常一站式解决方案的更多相关文章
- 基于Spring Boot/Spring Session/Redis的分布式Session共享解决方案
分布式Web网站一般都会碰到集群session共享问题,之前也做过一些Spring3的项目,当时解决这个问题做过两种方案,一是利用nginx,session交给nginx控制,但是这个需要额外工作较多 ...
- Spring Cloud Alibaba微服务一站式解决方案-开篇v2.2.1.RELEASE
学习路线 **本人博客网站 **IT小神 www.itxiaoshen.com 生态概述 架构演进 什么是微服务 https://martinfowler.com/microservices/ Mic ...
- 主流微服务一站式解决方案Spring Cloud Alibaba入门看这篇就足够了
学习路线 **本人博客网站 **IT小神 www.itxiaoshen.com 生态概述 架构演进 什么是微服务 https://martinfowler.com/microservices/ Mic ...
- 基于Jmeter跟Jenkins的自动化性能测试的一站式解决方案(转)
www.MyException.Cn 网友分享于:2015-08-26 浏览:0次 基于Jmeter和Jenkins的自动化性能测试的一站式解决方案 作者: Yu, Qingguo Shen, ...
- spring security 一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中 配置的Bean,充分利用了Spring ...
- 基于Spring的RPC通讯模型.
一.概念和原理 RPC(remote procedure call),远程过程调用,是客户端应用和服务端之间的会话.在客户端,它所需要的一些功能并不在该应用的实现范围之内,所以应用要向提供这些功能的其 ...
- 基于spring的安全管理框架-Spring Security
什么是spring security? spring security是基于spring的安全框架.它提供全面的安全性解决方案,同时在Web请求级别和调用级别确认和授权.在Spring Framewo ...
- 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)
通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 ja ...
- 基于Spring MVC的Web应用开发(三) - Resources
基于Spring MVC的Web应用开发(3) - Resources 上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件. 注意到本项目的we ...
随机推荐
- 《深入理解mybatis原理3》 Mybatis数据源与连接池
<深入理解mybatis原理> Mybatis数据源与连接池 对于ORM框架而言,数据源的组织是一个非常重要的一部分,这直接影响到框架的性能问题.本文将通过对MyBatis框架的数据源结构 ...
- Python之NumPy(axis=0 与axis=1)区分
转自:http://blog.csdn.net/wangying19911991/article/details/73928172 https://www.zhihu.com/question/589 ...
- [Eclipse]Eclipse快捷键
查看一个方法被谁调用:选中方法名字,Search-->Reference-->Workspace
- 个人学习jQuery笔记
1.$(“#div1”).text()是获取id为div1的文本内容,也可以填充值 $(“#div1”).html() 是获取id 为div1的HTML内容值 也可以填充值 2.$(“#div1”)是 ...
- LeetCode OJ:Peeking Iterator(peeking 迭代器)
Given an Iterator class interface with methods: next() and hasNext(), design and implement a Peeking ...
- 12.18 aop身份验证
对所有卖家页面进行身份验证,采用aop编程 步骤:1.获得request 2.查询cookie 3.查询redis 4.查询不通过时,采用抛出异常,捕捉异常,再异常里加入跳转到登陆页面的方法 准备工作 ...
- TensorFlow 的使用步骤
使用 TensorFlow 的基本步骤 学习目标: 学习基本的 TensorFlow 概念 在 TensorFlow 中使用 LinearRegressor 类并基于单个输入特征预测各城市街区的房屋价 ...
- MoreEffectiveC++Item35(操作符)(条款5-8)
条款5 对定制的"类型转换函数"保持警惕 条款6 区别increment/decrement操作符的前值和后置形式 条款7 千万不要重载&&,||,和,操作符 条款 ...
- keras系列︱keras是如何指定显卡且限制显存用量
keras在使用GPU的时候有个特点,就是默认全部占满显存. 若单核GPU也无所谓,若是服务器GPU较多,性能较好,全部占满就太浪费了. 于是乎有以下三种情况: - 1.指定GPU - 2.使用固定显 ...
- C# 加密狗 超级狗 加密程序 程序授权示例 程序授权验证
本篇针对超级狗进行讲解,对应的超级狗套件的工具包版本为2.4版本.超级狗图片如下: 主要包含两个狗,一个是超级狗,一个是开发狗,在本博文中都是必须的.首先先安装光盘中的开发套间. 接下来就开始演示一个 ...