专栏首页Java架构师进阶springboot 统一异常处理(包含统一数据校验)

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;

}

}

运行后访问结果如下:

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

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 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 adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • bash: /home/jdk1.8.0_161/bin/java: Permission denied

        报错: bash: /home/jdk1.8.0_161/bin/java: Permission denied

    拓荒者
  • 如何优化tomcat配置(从内存、并发、缓存4个方面)优化

      Tomcat内存优化主要是对 tomcat 启动参数优化,我们可以在 tomcat 的启动脚本 catalina.sh 中设置 java_OPTS 参数。 ...

    拓荒者
  • Java 线程如何正确关闭

    heidsoft
  • 如何开始使用 Java 机器学习

    这个问题已经有一段时间了,但最近这些日子几乎每个人都在谈论人工智能和机器学习。这已经不再是一个保留给科学家和研究者的秘密,而是几乎实现于每一项新兴技术中。

    哲洛不闹
  • 在win10下安装eclipse 原

    http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.h...

    拓荒者
  • 我的Java秋招面经大合集(包含BAT头条网易等公司)

    我参与了阿里巴巴中间件部门的提前批面试,一共经历了四次面试,拿到了口头offer。

    黄小斜
  • 这 10 道 Spring Boot 常见面试题你需要了解下

    多年来,随着新功能的增加,spring变得越来越复杂。只需访问https://spring.io/projects页面,我们就会看到可以在我们的应用程序中使用的...

    Java团长
  • Java 同步处理与异步处理

    heidsoft
  • Java后端学习路线图,你真的只需要这一张!

    学习路线图往往是学习一样技术的入门指南。网上搜到的Java学习路线图也是一抓一大把。

    黄小斜
  • 配置Tomcat apr运行模式 原

      bio是阻塞式IO操作,使用的是传统的java i/o处理方式,对于每一个请求都要创建一个线程来进行处理,所以开销较大不适合处理高并发的场景

    拓荒者

扫码关注云+社区

领取腾讯云代金券