关于Web应用的全局异常处理,上一篇介绍了ControllerAdvice
结合@ExceptionHandler
的方式来实现web应用的全局异常管理;css
本篇博文则带来另一种并不常见的使用方式,经过实现自定义的HandlerExceptionResolver
,来处理异常状态html
上篇博文连接: SpringBoot系列教程web篇之全局异常处理 本篇原文: SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolverjava
首先得搭建一个web应用才有可能继续后续的测试,借助SpringBoot搭建一个web应用属于比较简单的活;git
建立一个maven项目,pom文件以下github
<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>
复制代码
HandlerExceptionResolver
顾名思义,就是处理异常的类,接口就一个方法,出现异常以后的回调,四个参数中还携带了异常堆栈信息web
@Nullable
ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
复制代码
咱们自定义异常处理类就比较简单了,实现上面的接口,而后将完整的堆栈返回给调用方spring
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;
}
复制代码
仔细观察上面的代码实现,有下面几个点须要注意json
response.addHeader("Content-Type", "text/html; charset=UTF-8");
若是没有这一行,会出现中文乱码的状况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);
}
}
复制代码
咱们依然使用上篇博文的用例来测试后端
@Controller
@RequestMapping(path = "page")
public class ErrorPageRest {
@ResponseBody
@GetMapping(path = "divide")
public int divide(int sub) {
return 1000 / sub;
}
}
复制代码
下面分别是404异常和500异常的实测状况websocket
500异常会进入咱们的自定义异常处理类, 而404依然走的是默认的错误页面,因此若是咱们须要捕获404异常,依然须要在配置文件中添加
# 出现错误时, 直接抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
# 设置静态资源映射访问路径
spring.mvc.static-path-pattern=/statics/**
# spring.resources.add-mappings=false
复制代码
为何404须要额外处理?
下面尽可能以通俗易懂的方式说明下这个问题
@ResponseBody
来代表一个url返回的是json数据(一般状况下是这样的,不考虑自定义实现)@Controller
中经过@RequestMapping
定义的REST服务,返回的是静态资源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;
}
复制代码
本篇博文虽然也介绍了一种新的全局异常处理方式,实现效果和ControllerAdvice
也差很少,可是并不推荐用这种方法, 缘由以下
HandlerExceptionResolver
的方式没有ControllerAdvice
方式简介优雅DefaultHandlerExceptionResolver
已经很是强大了,基本上覆盖了http的各类状态码,咱们本身再去定制的必要性不大尽信书则不如,以上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛