基于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 ...
随机推荐
- 转:聊聊Greenplum的那些事
笔者有幸从04年就开始从事大规模数据计算的相关工作,08年作为Greenplum 早期员工加入Greenplum团队(当时的工牌是“005”,哈哈),记得当时看了一眼Greenplum的架构(嗯,就是 ...
- bzoj2501
题解: 显然,每当进入一个小的边界,那么我们的ans+1,出去一个大的边界,ans-1 然后,我们将每一个边界排序,时间小的在前,大的在后 每一次进来一个,如果是左边的边界,+1,右边的-1 然后输出 ...
- 联想北研实习生面试-嵌入式Linux研发工程师
8月中旬暑假去联想北研参加了实习生面试,面试职位是嵌入式Linux研发工程师.投完简历第二天,主管回复我邮件,意思是说随时来面试,到北研时候给他打个电话就行.于是我回复条短信表示感谢,并约好时间第二天 ...
- UART介绍
https://baike.baidu.com/item/UART/4429746?fr=aladdin
- React-Native进阶_6.导航 Naviagtion传递数据并展示
接着上面 Navigation 继续学习传递数据给下一个页面 onPress={() => this.props.navigation.navigate('Detail',{info:movie ...
- $timeout
$timeout 会在执行后刷新页面上 与angular 相关的变量,在于jQuery共用修改页面变量时,这很可能会导致刷新跳动的现象:
- 使用HslCommunication实现PLC数据的远程客户端监视,以及web端实时监视,远程操作设备示例
前言 本文主要是演示一个例子,服务器后台程序从PLC采集数据,并推送给在线客户端显示,以及推送给web端进行实时的显示,还支持远程操作,支持安卓端的同步监视和远程操作,关于HslCommunicati ...
- 【剑指offer-25】合并两个单调递增的链表,C++实现(链表)
原创博客,转载请注明出处! 1.题目 输入两个单调递增的链表,输出两个链表合成后的链表(单调不减). 2.思路(递归) # 鲁棒性: 如果链表1是空链表,则直接输出链表2. 如果链表2是空链表,则直接 ...
- int('x', base)中的base参数
>>> int('12', 16) 16表示'12'就是16进制数,int()要将这个16进制数转化成10进制.
- HihoCoder - 1496:寻找最大值(高维前缀和||手动求子集)
描述 给定N个数A1, A2, A3, ... AN,小Ho想从中找到两个数Ai和Aj(i ≠ j)使得乘积Ai × Aj × (Ai AND Aj)最大.其中AND是按位与操作. 小Ho当然知道怎么 ...