SpringBoot错误处理机制(ControllerAdvice+ExceptionHandler自定义错误处理、默认机制源码分析、错误处理实战)

1. SpringBoot自己对错误进行处理

1.1 给一个Controller进行错误处理

使用@ExceptionHandler,处理一个@Contoller的异常

package com.hh.springboot3test.controller;

import org.springframework.web.bind.annotation.*;




@RestController
public class ControllerTest {

    /*
    对整个RestController的异常进行处理
     */
    @ResponseBody       // 将异常消息返回给客户端
    @ExceptionHandler(Exception.class)      // 标识能处理的异常
    public String handleException(Exception e) {    // 接收异常
        return "出错了, 原因: " + e.getMessage();
    }

}

1.2 使用ControllerAdvice统一处理错误

使用@ControllerAdvice处理所有@Controller发生的错误。优先级比@Controller类的低

package com.hh.springboot3test.handler;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;


// 标明这个类是集中处理所有@Controller发生的错误
@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler(Exception.class)
    public String handleException(Exception e) {

        return "出错了, 统一处理,原因: " + e.getMessage();
    }
}

2. 默认机制源码解析

如果我们没有进行自定义的错误处理,springBoot提供默认的错误处理机制。错误处理的自动配置都在ErrorMvcAutoConfiguration中。整个处理流程如下:

默认机制

如果SpringMVC处理不了,则将错误请求发送给server.error.path=/error参数配置的路径,如果没有配置参数,则默认是/error。由ErrorMvcAutoConfiguration添加的BasicErrorController组件进行处理。如下所示:

......省略部分......
@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
......省略部分......
}

SpringBoot会自适应处理错误,浏览器优先响应页面或移动端优先响应JSON数据。和内容协商一样,header头的accept参数设置application/xml,则以XML格式返回数据。BasicErrorController部分代码如下所示:

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

    @RequestMapping    // 返回ResponseEntity, 可以是JSON
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = this.getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity(status);
        } else {
            Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
            return new ResponseEntity(body, status);
        }
    }

我们先说HTML的错误处理。在errorHtml方法。错误页面先调用resolveErrorView,通过DefaultErrorViewResolver解析的

		// 解析错误的自定义视图地址
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);

ErrorMvcAutoConfiguration给容器专门添加了一个错误视图解析器DefaultErrorViewResolver

        @Bean
        @ConditionalOnBean({DispatcherServlet.class})
        @ConditionalOnMissingBean({ErrorViewResolver.class})
        DefaultErrorViewResolver conventionErrorViewResolver() {
            return new DefaultErrorViewResolver(this.applicationContext, this.resources);
        }

SpringBoot的DefaultErrorViewResolver,提供了解析自定义错误页的默认规则

    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        // 先进行精确码处理
        ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
        // 精确码处理不了,再进行4xx、5xx处理
        if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
            modelAndView = this.resolve((String)SERIES_VIEWS.get(status.series()), model);
        }

        return modelAndView;
    }

    private ModelAndView resolve(String viewName, Map<String, Object> model) {
        // 先在classpath:templates/error路径下查找
        String errorViewName = "error/" + viewName;
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
        // 如果classpath:templates/error没找到,则调用resolveResource方法,从静态路径下面的error文件夹下去找
        return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
    }

    private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
        String[] var3 = this.resources.getStaticLocations();
        int var4 = var3.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String location = var3[var5];

            try {
                Resource resource = this.applicationContext.getResource(location);
                resource = resource.createRelative(viewName + ".html");
                if (resource.exists()) {
                    return new ModelAndView(new HtmlResourceView(resource), model);
                }
            } catch (Exception var8) {
            }
        }

        return null;
    }

如果errorHtml方法,调用resolveErrorView处理不了。则在errorHtml方法访问classpath:templates/error.html

        return modelAndView != null ? modelAndView : new ModelAndView("error", model);

如果classpath:templates/error.html都未提供。ErrorMvcAutoConfiguration给容器添加一个默认名为error的view。提供了默认白板功能

        @Bean(
            name = {"error"}
        )
        @ConditionalOnMissingBean(
            name = {"error"}
        )
        public View defaultErrorView() {
            return this.defaultErrorView;
        }

再说移动端的错误处理。DefaultErrorAttributes封装了JSON格式的错误信息

    @Bean
    @ConditionalOnMissingBean(
        value = {ErrorAttributes.class},
        search = SearchStrategy.CURRENT
    )
    public DefaultErrorAttributes errorAttributes() {
        return new DefaultErrorAttributes();
    }

规则总结:

  1. 解析一个错误页
    1. 如果发生了403、404、500、503这些错误
      1. 如果有模板引擎,默认在classpath:/templates/error/精确码.html。如403.html文件
      2. 如果没有模板引擎,在静态资源文件夹下的error文件夹下找精确码.html
    2. 如果匹配不到精确码.html这些精确的错误页,就去找5xx.html,4xx.html模糊匹配
      1. 如果有模板引擎,默认在classpath:/templates/error/5xx.html。如5xx.html文件
      2. 如果没有模板引擎,在静态资源文件夹下的error文件夹下找 5xx.html
  2. 如果上面处理不了,则从模板引擎路径classpath:templates下找error.html页面。如果error.html页面都没有,则使用springboot提供的默认白板页面

3. 错误处理机制实战

  • 前后分离
    • 后台发生的所有错误,@ControllerAdvice + @ExceptionHandler进行统一异常处理,将错误数据返回给前端
  • 服务端页面渲染
    • 不可预知的一些,HTTP码表示的服务器或客户端错误
      • 给classpath:/templates/error/下面,放常用精确的错误码页面。如500.html、404.html
      • 给classpath:/templates/error/下面,放通用模糊匹配的错误码页面。如5xx.html、4xx.html
    • 发生业务错误
      • 核心业务,每一种错误,都应该代码控制,跳转到自己定制的错误页
      • 通用业务,用classpath:/templates/error.html页面,显示错误信息

model中会默认存放一些数据。如下所示。我们自己也可以向model添加数据,然后再错误页面渲染的时候进行使用
model

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您想了解关于使用 @ExceptionHandler + @ControllerAdvice +枚举实现全局异常统一处理的内容,这是一个常用的方案,可以实现全局异常的捕获和处理,让程序更加健壮和稳定。 @ExceptionHandler注解用于标记一个方法,该方法用于处理指定的异常类型。@ControllerAdvice注解用于标记一个类,该类用于处理全局异常。枚举则可以用于定义异常类型,方便异常处理时的统一管理。 在实现全局异常处理时,我们可以通过 @ExceptionHandler 注解来捕获指定的异常类型,并在方法中进行相应的处理。@ControllerAdvice注解可以让我们在一个类中定义多个 @ExceptionHandler方法,用于处理不同类型的异常。同时,我们也可以使用枚举来定义不同的异常类型,方便管理和调用。 下面是一个简单的示例,演示如何使用 @ExceptionHandler + @ControllerAdvice +枚举实现全局异常统一处理: ```java @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = BusinessException.class) @ResponseBody public ResultVO handleBusinessException(BusinessException e) { return ResultVO.error(e.getCode(), e.getMessage()); } } public enum ExceptionEnum { PARAMETER_ERROR(1001, "参数错误"), DATA_NOT_FOUND(1002, "数据不存在"), SYSTEM_ERROR(5000, "系统错误"); private final int code; private final String message; ExceptionEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } public class BusinessException extends RuntimeException { private final int code; public BusinessException(int code, String message) { super(message); this.code = code; } public BusinessException(ExceptionEnum exceptionEnum) { super(exceptionEnum.getMessage()); this.code = exceptionEnum.getCode(); } public int getCode() { return code; } } ``` 在上面的示例中,GlobalExceptionHandler类标记了@ControllerAdvice注解,用于全局异常处理。其中,handleBusinessException方法用于处理BusinessException异常,返回一个ResultVO对象,其中包含错误码和错误信息。 BusinessException则是一个自定义的异常类,它包含一个code属性和一个message属性,用于表示异常的错误码和错误信息。同时,它还提供了一个构造方法,可以根据ExceptionEnum来构造一个BusinessException对象。 ExceptionEnum则是一个枚举类,包含了不同的异常类型,每个异常类型都有一个对应的错误码和错误信息。 在实际开发中,我们可以根据实际需求来定义不同的异常类型和错误码,以便更好地管理和调用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值