springboot中关于自定义异常处理的套路!

江南一点雨      2022-05-03     190

关键词:

在 Spring Boot 项目中 ,异常统一处理,可以使用 Spring 中 @ControllerAdvice 来统一处理,也可以自己来定义异常处理方案。Spring Boot 中,对异常的处理有一些默认的策略,我们分别来看。

默认情况下,Spring Boot 中的异常页面 是这样的:

技术图片

我们从这个异常提示中,也能看出来,之所以用户看到这个页面,是因为开发者没有明确提供一个 /error 路径,如果开发者提供了 /error 路径 ,这个页面就不会展示出来,不过在 Spring Boot 中,提供 /error 路径实际上是下下策,Spring Boot 本身在处理异常时,也是当所有条件都不满足时,才会去找 /error 路径。那么我们就先来看看,在 Spring Boot 中,如何自定义 error 页面,整体上来说,可以分为两种,一种是静态页面,另一种是动态页面。

静态异常页面

自定义静态异常页面,又分为两种,第一种 是使用 HTTP 响应码来命名页面,例如 404.html、405.html、500.html ....,另一种就是直接定义一个 4xx.html,表示400-499 的状态都显示这个异常页面,5xx.html 表示 500-599 的状态显示这个异常页面。

默认是在 classpath:/static/error/ 路径下定义相关页面:

技术图片

此时,启动项目,如果项目抛出 500 请求错误,就会自动展示 500.html 这个页面,发生 404 就会展示 404.html 页面。如果异常展示页面既存在 5xx.html,也存在 500.html ,此时,发生500异常时,优先展示 500.html 页面。

动态异常页面

动态的异常页面定义方式和静态的基本 一致,可以采用的页面模板有 jsp、freemarker、thymeleaf。动态异常页面,也支持 404.html 或者 4xx.html ,但是一般来说,由于动态异常页面可以直接展示异常详细信息,所以就没有必要挨个枚举错误了 ,直接定义 4xx.html(这里使用thymeleaf模板)或者 5xx.html 即可。

注意,动态页面模板,不需要开发者自己去定义控制器,直接定义异常页面即可 ,Spring Boot 中自带的异常处理器会自动查找到异常页面。

页面定义如下:

技术图片

页面内容如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>5xx</h1>
<table border="1">
    <tr>
        <td>path</td>
        <td th:text="${path}"></td>
    </tr>
    <tr>
        <td>error</td>
        <td th:text="${error}"></td>
    </tr>
    <tr>
        <td>message</td>
        <td th:text="${message}"></td>
    </tr>
    <tr>
        <td>timestamp</td>
        <td th:text="${timestamp}"></td>
    </tr>
    <tr>
        <td>status</td>
        <td th:text="${status}"></td>
    </tr>
</table>
</body>
</html>

默认情况下,完整的异常信息就是这5条,展示 效果如下 :

技术图片

如果动态页面和静态页面同时定义了异常处理页面,例如 classpath:/static/error/404.htmlclasspath:/templates/error/404.html 同时存在时,默认使用动态页面。即完整的错误页面查找方式应该是这样:

发生了500错误-->查找动态 500.html 页面-->查找静态 500.html --> 查找动态 5xx.html-->查找静态 5xx.html。

自定义异常数据

默认情况下,在Spring Boot 中,所有的异常数据其实就是上文所展示出来的5条数据,这5条数据定义在 org.springframework.boot.web.reactive.error.DefaultErrorAttributes 类中,具体定义在 getErrorAttributes 方法中 :

@Override
public Map<String, Object> getErrorAttributes(ServerRequest request,
                boolean includeStackTrace) {
        Map<String, Object> errorAttributes = new LinkedHashMap<>();
        errorAttributes.put("timestamp", new Date());
        errorAttributes.put("path", request.path());
        Throwable error = getError(request);
        HttpStatus errorStatus = determineHttpStatus(error);
        errorAttributes.put("status", errorStatus.value());
        errorAttributes.put("error", errorStatus.getReasonPhrase());
        errorAttributes.put("message", determineMessage(error));
        handleException(errorAttributes, determineException(error), includeStackTrace);
        return errorAttributes;
}

DefaultErrorAttributes 类本身则是在org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration 异常自动配置类中定义的,如果开发者没有自己提供一个 ErrorAttributes 的实例的话,那么 Spring Boot 将自动提供一个ErrorAttributes 的实例,也就是 DefaultErrorAttributes 。

基于此 ,开发者自定义 ErrorAttributes 有两种方式 :

  1. 直接实现 ErrorAttributes 接口
  2. 继承 DefaultErrorAttributes(推荐),因为 DefaultErrorAttributes 中对异常数据的处理已经完成,开发者可以直接使用。

具体定义如下:

@Component
public class MyErrorAttributes  extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace);
        if ((Integer)map.get("status") == 500) {
            map.put("message", "服务器内部错误!");
        }
        return map;
    }
}

定义好的 ErrorAttributes 一定要注册成一个 Bean ,这样,Spring Boot 就不会使用默认的 DefaultErrorAttributes 了,运行效果如下图:

技术图片

自定义异常视图

异常视图默认就是前面所说的静态或者动态页面,这个也是可以自定义的,首先 ,默认的异常视图加载逻辑在 org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController 类的 errorHtml 方法中,这个方法用来返回异常页面+数据,还有另外一个 error 方法,这个方法用来返回异常数据(如果是 ajax 请求,则该方法会被触发)。

@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
                HttpServletResponse response) {
        HttpStatus status = getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
                        request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}

在该方法中 ,首先会通过 getErrorAttributes 方法去获取异常数据(实际上会调用到 ErrorAttributes 的实例 的 getErrorAttributes 方法),然后调用 resolveErrorView 去创建一个 ModelAndView ,如果这里创建失败,那么用户将会看到默认的错误提示页面。

正常情况下, resolveErrorView 方法会来到 DefaultErrorViewResolver 类的 resolveErrorView 方法中:

@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
                Map<String, Object> model) {
        ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
                modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
        }
        return modelAndView;
}

在这里,首先以异常响应码作为视图名分别去查找动态页面和静态页面,如果没有查找到,则再以 4xx 或者 5xx 作为视图名再去分别查找动态或者静态页面。

要自定义异常视图解析,也很容易 ,由于 DefaultErrorViewResolver 是在 ErrorMvcAutoConfiguration 类中提供的实例,即开发者没有提供相关实例时,会使用默认的 DefaultErrorViewResolver ,开发者提供了自己的 ErrorViewResolver 实例后,默认的配置就会失效,因此,自定义异常视图,只需要提供 一个 ErrorViewResolver 的实例即可:

@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {
    public MyErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) {
        super(applicationContext, resourceProperties);
    }
    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        return new ModelAndView("/aaa/123", model);
    }
}

实际上,开发者也可以在这里定义异常数据(直接在 resolveErrorView 方法重新定义一个 model ,将参数中的model 数据拷贝过去并修改,注意参数中的 model 类型为 UnmodifiableMap,即不可以直接修改),而不需要自定义MyErrorAttributes。定义完成后,提供一个名为123的视图,如下图:

技术图片

如此之后,错误试图就算定义成功了。

总结

实际上也可以自定义异常控制器 BasicErrorController ,不过松哥觉得这样太大动干戈了,没必要,前面几种方式已经可以满足我们的大部分开发需求了。

关注公众号牧码小子,专注于 Spring Boot+微服务,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

技术图片

io中关于自定义缓冲区和使用默认缓冲区哪个效率更高的对比

//自己测试了一个3.8G的文件,有兴趣的可以自己试试看,初学java写的不对的地方希望大家能指出,有疑问可以留言一起探讨,谢谢!1packagecn.String.Day.IO;23importjava.io.*;45/**6*CreatedbyVoidon2017/6/20.7*/8publicclasscopyInputOutput{9publicstaticvoidma... 查看详情

mfc中关于自定义消息

大家好,我是VC初学者,出现了个问题,望各位大侠帮忙?我在单文档的视类的构造函数中,创建了一个线程,我做了一个简单测试,发现不行:这是线程函数如下:DWORDWINAPICMyView::FunProS1(LPVOIDlpParameter)HWNDhWnd=(HWND)lpParameter;while(tr... 查看详情

springboot——全局异常处理

...异常页面动态异常页面自定义异常数据自定义异常视图在SpringBoot项目中,有默认对异常的处理的策略,也可以自己来定义全局异常处理方案。默认异常处理在请求处理方法中定义一个异常:inti=1/0;进行请求时,默认的错误页面... 查看详情

springboot:11.异常处理方式1(自定义异常页面)(转)

SpringBoot默认的处理异常的机制:SpringBoot默认的已经提供了一套处理异常的机制。一旦程序中出现了异常SpringBoot会向/error的url发送请求。在springBoot中提供了一个叫BasicExceptionController来处理/error请求,然后跳转到默认显示异常的... 查看详情

springboot的异常处理与自定义异常

...必用一个知识点,就是使用枚举构建自定义异常并应用于springboot的异常处理器。开始之前我先把这个案例的结构大致说明一下:1、使用idea创建一个springboot的Gradle/Maven项目,引入web模块即可(由于案例的重点是异常处理,所以... 查看详情

springboot异常处理

一.自定义错误页面SpringBoot默认的处理异常的机制:SpringBoot默认的已经提供了一套处理异常的机制。一旦程序中出现了异常SpringBoot会向/error的url发送请求。在springBoot中提供了一个叫BasicExceptionController来处理/error请求,然后跳转... 查看详情

springboot自定义异常和全局异常处理

模拟开发过程中的异常处理:场景:  如果不对异常处理,返回给前端的将是一个异常错误页日志  所以要异常处理首先准备个工具类:作为返回给前端的JsonData数据封装:  此类也可以作为工具类直接使用。packagenet.myclass... 查看详情

springboot自定义异常处理

背景Springboot默认把异常的处理集中到一个ModelAndView中了,但项目的实际过程中,这样做,并不能满足我们的要求。具体的自定义异常的处理,参看以下前提Springboot默认的applicationpropertiesSpringBoot异常处理详解具体... 查看详情

springboot集中异常处理

集中异常处理方式一:ExceptionHandle定义自己的异常类型,根据不同类型做不同处理,比如我定义的MyException:publicclassMyExceptionextendsRuntimeException{publicMyException(Stringmsg){super(msg);}}然后通过MyExceptionHandle处理该异常,需要注意的是异... 查看详情

自定义异常(代码片段)

在SpringBoot项目中,异常统一处理,可以使用Spring中@ControllerAdvice来统一处理,也可以自己来定义异常处理方案。SpringBoot中,对异常的处理有一些默认的策略,我们分别来看。默认情况下,SpringBoot中的异常页面是这样的:我们从... 查看详情

springboot自定义异常处理

1.自定义异常类importlombok.Data;@DatapublicclassUserExceptionextendsRuntimeException{privateLongid;publicUserException(Longid){super("usernotexist");this.id=id;}publicUserException(Stringmessage,Longid){sup 查看详情

Hibernate:相对于自定义 @Transactional(timeout) 的默认事务超时

...:2018-01-2210:10:33【问题描述】:在我的项目中,它是基于SpringBoot、Hibernate和PostgreSQL(使用HikariCP)构建的 查看详情

springboot异常处理

SpringBoot异常处理一、自定义错误页面  创建error.html页面,当发生错误时,将自动跳转到该页面。内部实现类为:BasicErrorController  适用范围:所有的异常都指向error.html页面,不能根据对应的异常跳转到对应的页面。 二... 查看详情

Spring Boot - 使用 RestControllerAdvice 的全局自定义异常处理机制

】SpringBoot-使用RestControllerAdvice的全局自定义异常处理机制【英文标题】:SpringBoot-GlobalCustomExceptionHandlingMechanismusingRestControllerAdvice【发布时间】:2018-01-0409:38:16【问题描述】:我正在为RestfulWeb服务使用SpringBoot。尝试设置一个全... 查看详情

springboot-自定义异常处理器

@Order(-1000)publicclassLocalExceptionResolverimplementsHandlerExceptionResolver{ @Override publicModelAndViewresolveException(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler, Ex 查看详情

springboot五种异常处理方式

1.自定义错误页面:  springboot已经内嵌了异常处理的机制,如果发生错误会自动跳转到error界面,默认的error界面为:     我们可以自定义异常界面,但是需要在src/main/resources/templates目录下创建error.html页面<!DOCTYPEHTM... 查看详情

springboot五种异常处理方式

1.自定义错误页面:  springboot已经内嵌了异常处理的机制,如果发生错误会自动跳转到error界面,默认的error界面为:     我们可以自定义异常界面,但是需要在src/main/resources/templates目录下创建error.html页面<!DOCTYPEHTM... 查看详情

springboot全局异常处理

我们知道SpringBoot已经提供了一套默认的异常处理机制,但是SpringBoot提供的默认异常处理机制却并不一定适合我们实际的业务场景,因此,我们通常会根据自身的需要对SpringBoot全局异常进行统一定制,例如定制错... 查看详情