当前位置: 首页 > news >正文

springboot 统一异常处理(包含统一数据校验)

1、统一异常处理的优势

在开发中,我们是否遇到过如下两种奇葩现象:

(1)只要没有成功,不管什么原因,前端界面给出提示:服务端错误/异常。哪怕是数据校验不过,也这样提示(嗯,反正先把锅甩出去再说,具体什么原因我才不在乎呢,老子就是这么聪明);

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

(2)前端不做任何提示,一切提示信息都来自后端,成功的时候自然没什么,失败的时候,比如将Exception的描述信息(e.getMessage)返回。

现象(1)没什么好说的,直接拖出去枪毙吧;现象(2)先把产品经理宰了再说吧,看起来好像很专业的样子,出了什么问题直接看response返回的结果就知道个大概,研发测试都很方便,只是,大家想过没有,研发测试运维的问题,凭什么要用户买单,你见过淘宝京东有时候出了问题给你类似于“out of memory”的异常提示吗?

那么异常统一处理有什么好处呢?

提高用户体验;

业务逻辑和异常处理逻辑解耦;

对异常进行分类统一处理,减少冗余代码;

便于代码风格统一,并且更优雅(比如参数校验的时候,得写很多if else,并且不同的人写法不一致);

2、统一异常处理的实现

2.1 springboot的默认异常处理

Spring Boot提供了一个默认的映射:/error,当处理中抛出异常之后,会转到该请求中处理,并且该请求有一个全局的错误页面用来展示异常内容。

比如:

@RestController
public class Test {
@RequestMapping(value = {"/test"},method = RequestMethod.GET)
public String test(@RequestParam("id")Integer id){
return "id:"+id;
}
}
运行后访问结果如下:

springboot 统一异常处理(包含统一数据校验)

这种直接返回错误页面,对于用户而言,显然是太不友好了哈!

2.2 统一异常处理

java异常详解

首先,定义自己的异常类,随便起个名字哈,MyException.java

@Data

public class MyException extends Exception{

private Integer code;
private String Message;

public MyException(Integer code,String Message) {
this.code = code;
this.Message = Message;
}
}
然后定义自己的异常处理类,ExceptionHandle.java

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

如果返回的对象是JSON的话,可以用@RestControllerAdvice

@ControllerAdvice

public class ExceptionHandle {

private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);

@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result handle(Exception e) {
if (e instanceof MyException) {
MyException myException = (MyException) e;
return ResultUtil.error(boyException.getCode(), boyException.getMessage());
}else {
logger.error("【系统异常】{}", e);
return new Result(-1, "未知错误");
}
}
}
3、统一异常处理源码解析

3.1 注解源码解析

java注解详解

@ControllerAdvice

@ExceptionHandler

@RestControllerAdvice与@ExceptionHandler注解是sprngmvc中与异常捕获与处理相关的注解,它的入口也是DispatcherServlet中的doDispatcher()方法中,如下:

this.processDispatchResult(processedRequest,
response, mappedHandler, mv, (Exception)dispatchException);
后面会进入HandlerExceptionResolverComposite的resolveException方法,这个ExceptionHandlerResolverComposite包含三个ExcpetionHandlerResolver,是在springmvc中生成的,在springboot中其生成代码如下:

@Bean
public HandlerExceptionResolver handlerExceptionResolver() {
    List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();
    configureHandlerExceptionResolvers(exceptionResolvers);
    if (exceptionResolvers.isEmpty()) {
        addDefaultHandlerExceptionResolvers(exceptionResolvers);
    }
    extendHandlerExceptionResolvers(exceptionResolvers);
    HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();
    composite.setOrder(0);
    composite.setExceptionResolvers(exceptionResolvers);
    return composite;
}

后面他会进入ExceptionHandlerExceptionResolver类的方法:

protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
        HttpServletResponse response, @Nullable HandlerMethod handlerMethod,

Exception exception) {

     ServletInvocableHandlerMethod exceptionHandlerMethod = 

getExceptionHandlerMethod(handlerMethod, exception);
}
在这个方法中的第一行,getExceptionHandlerMethod方法,其进行了查找对应的带有@ControllerAdvice注解的类型和对应匹配的方法,然后在doResolverHandlerMethod方法中进行了处理,这就是整个流程。

@ControllerAdvice的加载过程:

首先在springboot扫描的时候,会把@ControllerAdvice的bean放入到beanFactory里面去,此时只要从beanFactory中获取到需要的bean即可,处理方式在ExceptionHandlerExceptionResolver类中:

@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBodyAdvice beans
    initExceptionHandlerAdviceCache();
private void initExceptionHandlerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for exception mappings: " + getApplicationContext());
    }

    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

封装好后,获取带有@Exceptionhandler的注解方法,即根据异常类型进行调用了。

相关文章:

  • JavaSE-note1-随机访问类RandomAccessFile
  • git进阶之.gitignore 和 .gitmodules
  • jquery 延迟加载代码
  • WingMoney APP逆向,实现自动话费充值
  • ListView滚动到底部判断
  • 架构进阶笔记—如何阅读一个开源项目的源码?
  • 模块化JavaScript设计模式(一)
  • 码出高效JAVA开发手册(这个应该人手一份)
  • Web前端实践经验总结
  • GitHub(hexo)博客页面访问量错误以及中文乱码解决
  • POJ 3270 置换群问题
  • Amazon Aurora是如何设计原生云关系型数据库的?
  • mysql DEPENDENT SUBQUERY(转载)
  • 技本功丨请带上纸笔刷着看:解读MySQL执行计划的type列和extra列
  • C#属性和字段
  • JavaScript 如何正确处理 Unicode 编码问题!
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • create-react-app做的留言板
  • Map集合、散列表、红黑树介绍
  • mongodb--安装和初步使用教程
  • NSTimer学习笔记
  • Spring声明式事务管理之一:五大属性分析
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 基于组件的设计工作流与界面抽象
  • 开源SQL-on-Hadoop系统一览
  • 前端相关框架总和
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 正则表达式
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • (3)选择元素——(17)练习(Exercises)
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (安卓)跳转应用市场APP详情页的方式
  • (独孤九剑)--文件系统
  • (二)c52学习之旅-简单了解单片机
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (一一四)第九章编程练习
  • (转载)hibernate缓存
  • .bashrc在哪里,alias妙用
  • .NET MVC 验证码
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .net反编译的九款神器
  • ??myeclipse+tomcat
  • [ACTF2020 新生赛]Include
  • [boost]使用boost::function和boost::bind产生的down机一例
  • [C#]C#学习笔记-CIL和动态程序集
  • [C++]:for循环for(int num : nums)
  • [CSS]盒子模型
  • [FZSZOJ 1223] 上海红茶馆