关于Web应用的全局异常处理,上一篇介绍了ControllerAdvice结合@ExceptionHandler的方式来实现web应用的全局异常管理;

本篇博文则带来另外一种并不常见的使用方式,通过实现自定义的HandlerExceptionResolver,来处理异常状态

上篇博文链接: SpringBoot系列教程web篇之全局异常处理

本篇原文: SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver

I. 环境搭建

首先得搭建一个web应用才有可能继续后续的测试,借助SpringBoot搭建一个web应用属于比较简单的活;

创建一个maven项目,pom文件如下

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7</version>
<relativePath/> <!-- lookup parent from update -->
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
</dependencies> <build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

II. HandlerExceptionResolver

1. 自定义异常处理

HandlerExceptionResolver顾名思义,就是处理异常的类,接口就一个方法,出现异常之后的回调,四个参数中还携带了异常堆栈信息

@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

我们自定义异常处理类就比较简单了,实现上面的接口,然后将完整的堆栈返回给调用方

public class SelfExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
String msg = GlobalExceptionHandler.getThrowableStackInfo(ex); try {
response.addHeader("Content-Type", "text/html; charset=UTF-8");
response.getWriter().append("自定义异常处理!!! \n").append(msg).flush();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
} // 堆栈信息打印方法如下
public static String getThrowableStackInfo(Throwable e) {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
e.printStackTrace(new java.io.PrintWriter(buf, true));
String msg = buf.toString();
try {
buf.close();
} catch (Exception t) {
return e.getMessage();
}
return msg;
}

仔细观察上面的代码实现,有下面几个点需要注意

  • 为了确保中文不会乱码,我们设置了返回头 response.addHeader("Content-Type", "text/html; charset=UTF-8"); 如果没有这一行,会出现中文乱码的情况
  • 我们纯后端应用,不想返回视图,直接想Response的输出流中写入数据返回 response.getWriter().append("自定义异常处理!!! \n").append(msg).flush();; 如果项目中有自定义的错误页面,可以通过返回ModelAndView来确定最终返回的错误页面
  • 上面一个代码并不会直接生效,需要注册,可以在WebMvcConfigurer的子类中实现注册,实例如下
@SpringBootApplication
public class Application implements WebMvcConfigurer {
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
resolvers.add(0, new SelfExceptionHandler());
} public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}

2. 测试case

我们依然使用上篇博文的用例来测试

@Controller
@RequestMapping(path = "page")
public class ErrorPageRest { @ResponseBody
@GetMapping(path = "divide")
public int divide(int sub) {
return 1000 / sub;
}
}

下面分别是404异常和500异常的实测情况

500异常会进入我们的自定义异常处理类, 而404依然走的是默认的错误页面,所以如果我们需要捕获404异常,依然需要在配置文件中添加

# 出现错误时, 直接抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
# 设置静态资源映射访问路径
spring.mvc.static-path-pattern=/statics/**
# spring.resources.add-mappings=false

为什么404需要额外处理?

下面尽量以通俗易懂的方式说明下这个问题

  • java web应用,除了返回json类数据之外还可能返回网页,js,css
  • 我们通过 @ResponseBody来表明一个url返回的是json数据(通常情况下是这样的,不考虑自定义实现)
  • 我们的@Controller中通过@RequestMapping定义的REST服务,返回的是静态资源
  • 那么js,css,图片这些文件呢,在我们的web应用中并不会定义一个REST服务
  • 所以当接收一个http请求,找不到url关联映射时,默认场景下不认为这是一个NoHandlerFoundException,不抛异常,而是到静态资源中去找了(静态资源中也没有,为啥不抛NoHandlerFoundException呢?这个异常表示这个url请求没有对应的处理器,但是我们这里呢,给它分配到了静态资源处理器了ResourceHttpRequestHandler)

针对上面这点,如果有兴趣深挖的同学,这里给出关键代码位置

// 进入方法: `org.springframework.web.servlet.DispatcherServlet#doDispatch`

// debug 节点
Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
} // 核心逻辑
// org.springframework.web.servlet.DispatcherServlet#getHandler
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}

3. 小结

本篇博文虽然也介绍了一种新的全局异常处理方式,实现效果和ControllerAdvice也差不多,但是并不推荐用这种方法, 原因如下

  • HandlerExceptionResolver的方式没有ControllerAdvice方式简介优雅
  • 官方提供的DefaultHandlerExceptionResolver已经非常强大了,基本上覆盖了http的各种状态码,我们自己再去定制的必要性不大

II. 其他

web系列博文

项目源码

1. 一灰灰Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver的更多相关文章

  1. SpringBoot系列教程web篇之全局异常处理

    当我们的后端应用出现异常时,通常会将异常状况包装之后再返回给调用方或者前端,在实际的项目中,不可能对每一个地方都做好异常处理,再优雅的代码也可能抛出异常,那么在 Spring 项目中,可以怎样优雅的处 ...

  2. SpringBoot系列教程web篇之过滤器Filter使用指南扩展篇

    前面一篇博文介绍了在 SpringBoot 中使用 Filter 的两种使用方式,这里介绍另外一种直接将 Filter 当做 Spring 的 Bean 来使用的方式,并且在这种使用方式下,Filte ...

  3. SpringBoot系列教程Web篇之开启GZIP数据压缩

    本篇可以归纳在性能调优篇,虽然内容非常简单,但效果可能出乎预料的好: 分享一个真实案例,我们的服务部署在海外,国内访问时访问服务时,响应有点夸张:某些返回数据比较大的接口,耗时在 600ms+上,然而 ...

  4. SpringBoot系列教程web篇Listener四种注册姿势

    java web三要素Filter, Servlet前面分别进行了介绍,接下来我们看一下Listener的相关知识点,本篇博文主要内容为SpringBoot环境下,如何自定义Listener并注册到s ...

  5. SpringBoot系列教程web篇Servlet 注册的四种姿势

    原文: 191122-SpringBoot系列教程web篇Servlet 注册的四种姿势 前面介绍了 java web 三要素中 filter 的使用指南与常见的易错事项,接下来我们来看一下 Serv ...

  6. SpringBoot系列教程web篇之过滤器Filter使用指南

    web三大组件之一Filter,可以说是很多小伙伴学习java web时最早接触的知识点了,然而学得早不代表就用得多.基本上,如果不是让你从0到1写一个web应用(或者说即便从0到1写一个web应用) ...

  7. SpringBoot系列教程web篇之404、500异常页面配置

    接着前面几篇web处理请求的博文,本文将说明,当出现异常的场景下,如404请求url不存在,,403无权,500服务器异常时,我们可以如何处理 原文友链: SpringBoot系列教程web篇之404 ...

  8. SpringBoot系列教程web篇之重定向

    原文地址: SpringBoot系列教程web篇之重定向 前面介绍了spring web篇数据返回的几种常用姿势,当我们在相应一个http请求时,除了直接返回数据之外,还有另一种常见的case -&g ...

  9. SpringBoot系列教程web篇之如何自定义参数解析器

    title: 190831-SpringBoot系列教程web篇之如何自定义参数解析器 banner: /spring-blog/imgs/190831/logo.jpg tags: 请求参数 cat ...

随机推荐

  1. 阿里巴巴Java开发手册(格式规约篇)——查自己的漏-补自己的缺

    (三) 格式规约 1. [强制]大括号的使用约定.如果是大括号内为空,则简洁地写成{}即可,不需要换行:如果是非空代码块则: 1) 左大括号前不换行.行. 2) 左大括号后换行. 3) 右大括号前换行 ...

  2. Docker部署ngnix静态网站

    Hello World 首先获取ngnix镜像(默认的是最新版. docker pull nginx 先来编写一个最简单的Dockerfile,一个Dockerfile修改该Nginx镜像的首页 Do ...

  3. Codeforces Round #552 (Div. 3)-C-Gourmet Cat

    http://codeforces.com/contest/1154/problem/C 题意: 有一只猫,周一周四周日吃鱼肉,周二周六吃兔肉,周三周五吃鸡肉,现给出三种肉的数量,问最多能吃多少天? ...

  4. Markdown&Latex学习笔记,qwq

    目录 推荐的文章 居中 字体 加颜色 指数 分数 根号 神奇的符号(不要多想qwq) 箭头 小于号 大括号 累加符号 累乘符号 下标 \(\phi\)&\(\varphi\) \(\equiv ...

  5. GoCN每日新闻(2019-11-10)

    GoCN每日新闻(2019-11-10) 1. Go Netpoll I/O多路复用构建原生网络模型之源码深度解析 https://taohuawu.club/go-netpoll-io-multip ...

  6. OpenFOAM——前台阶

    本算例来自<ANSYS Fluid Dynamics Verification Manual>中的VMFL037:Turbulent Flow Over a Forward Facing ...

  7. Linux下查看文件和文件夹大小 删除日志

    场景:在sts中执行自动部署时候maven提示No space left on device错误,后来经检查发现是磁盘空间满了,用下面的方法分析发现tomcat下面的logs目录占用了很大的空间,删除 ...

  8. eclipse 如何将文件编辑器窗口的背景填充为背景图片?

        1.情景展示 文件编辑窗口的背景默认为白色. 我们知道,对于整日面对电脑的程序员来说,白色容易造成眼疲劳,而且对于眼睛的伤害比较大. 所以,eclipse添加了黑色主题. 切换成黑色主题 改变 ...

  9. [原创]STAR法则

    [原创]STAR法则 STAR法则是情境(situation).任务(task).行动(action).结果(result)四项的缩写. STAR法则是一种常常被面试官使用的工具,用来收集面试者与工作 ...

  10. BiseNet阅读总结

    一.思路 语义分割既需要丰富的空间信息,又需要较大的感受野.然而,现代方法通常会牺牲空间分辨率来实现实时推理速度,导致性能低下.本文提出了一种新的双边分割网络(BiSeNet)来解决这一难题.我们首先 ...