基于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 ...
随机推荐
- BZOJ 1026 windy数 (数位DP)
题意 区间[A,B]上,总共有多少个不含前导零且相邻两个数字之差至少为2的正整数? 思路 状态设计非常简单,只需要pos.limit和一个前驱数pre就可以了,每次枚举当前位时判断是否与上一位相差2即 ...
- Ansible 小手册系列 十九(常见指令表)
Play 指令 说明 accelerate 开启加速模式 accelerate_ipv6 是否开启ipv6 accelerate_port 加速模式的端口 always_run any_error ...
- poj 2739 Sum of Consecutive Prime Numbers 素数 读题 难度:0
Sum of Consecutive Prime Numbers Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 19697 ...
- html 实体和htmlspecialchars()
HTML 中的预留字符必须被替换为字符实体. HTML 实体 在 HTML 中,某些字符是预留的. 在 HTML 中不能使用小于号(<)和大于号(>),这是因为浏览器会误认为它们是标签. ...
- 如何使用JFinal开发javaweb
介绍开始: 编辑器:MyEclipse; 数据库:MySQL; 服务器:tomcat; 1 首先新建web项目 要强调的是Target runtime必须选择为None.然后点击两次Next,选中创建 ...
- Shell 从日志文件中选择时间段内的日志输出到另一个文件
Shell 从日志文件中选择时间段内的日志输出到另一个文件 情况是这样的,某系统的日志全部写在一个日志文件内,所以这个文件非常大,非常长,每次查阅的时候非常的不方便.所以,相关人员希望能够查询某个时间 ...
- 【Spring实战】Spring容器初始化完成后执行初始化数据方法
一.背景知识及需求 在做WEB项目时,经常在项目第一次启动时利用WEB容器的监听.Servlet加载初始化等切入点为数据库准备数据,这些初始化数据是系统开始运行前必须的数据,例如权限组.系统选项.默认 ...
- SQL基础三(例子)
-----------聚合函数使用------------------------ --1.查询student表中所有学生人数 select count(stuno) from student --2 ...
- ICCS 会议 Latex 压缩文件提交主要事项
cd papers/conf latex main... check that the are no error messages ...zip -r mypaper.zip * 说明:必须在Linu ...
- CentOS 6.5 部署 Horizon
以root用户进行部署,python源也可以使用 http://mirrors.aliyun.com/pypi/simple/ 修改系统 更改SElinux的配置文件 /etc/selinux/con ...