基于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 ...
随机推荐
- 设计模式--观察者模式C++实现
观察者模式C++实现 1定义 Observer/Publish/subscribe发布订阅模式 定义对象间一种一对多的依赖关系,使得当一个对象改变状态时,所有依赖他的对象都能获得通知并被自动更新 2类 ...
- 1-22-shell脚本的基础
1.1 shell 脚本的编写规范 1.2 变量与特殊变量应用 1.3局部变量与全局变量 1.4 条件测试表达式 ------------------------------------------- ...
- nyoj115——裸dijksta(点之间最短路)
城市平乱 时间限制:1000 ms | 内存限制:65535 KB 难度:4 描述 南将军统领着N个部队,这N个部队分别驻扎在N个不同的城市. 他在用这N个部队维护着M个城市的治安,这M个城市 ...
- 安装Linux环境
虚拟机:虚拟机(Virtual Machine),在计算机科学中的体系结构里,是指一种特殊的软件,他可以在计算机平台和终端用户之间建立一种环境,而终端用户则是基于这个软件所建立的环境来操作软件.在计算 ...
- HDU 4734 F(x) ★(数位DP)
题意 一个整数 (AnAn-1An-2 ... A2A1), 定义 F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1,求[0..B]内有多少 ...
- vue本地项目静态资源管理
vue本地项目静态资源管理 统一放在src/static里面css,images,js index.html里面使用重置样式 <link rel="stylesheet" h ...
- CF911A
题解: 先按照a大小排序(要双关键字) 然后和a[1]一样的按照b减一减,取最小 代码: #include<bits/stdc++.h> using namespace std; ; in ...
- C++ 各种继承方式的类内存布局
body, table{font-family: 微软雅黑; font-size: 10pt} table{border-collapse: collapse; border: solid gray; ...
- Solr后台管理界面配置
配置来源 https://stackoverflow.com/questions/28043957/how-to-set-apache-solr-admin-password 注意:配置用户名密码后 ...
- 浅析Symbol
不知道大家有没有留意ES6中的Symbol函数?在此之前,我对Symbol的认识知识这样的: 一.Symbol()和Symbol.for('str') Symbol()是独一无二的,你无法创建两个相 ...