简单认识springboot的错误处理机制

1.Spring Boot默认的错误处理机制

默认的处理效果:

  1. 浏览器返回一个错误页面404
  2. 如果是其他客户端,默认响应一个json数据

原理:ErrorMvcAutoConfiguration错误处理的自动配置

给容器添加了一下组件:

//ErrorAttributes,容器中没有这个组件则自动配置一个DefaultErrorAttributes,即我们看见的错误处理
@Bean
@ConditionalOnMissingBean(
    value = {ErrorAttributes.class},
    search = SearchStrategy.CURRENT
)
public DefaultErrorAttributes errorAttributes() {
    return new DefaultErrorAttributes();
}
//ErrorController
 @Bean
    @ConditionalOnMissingBean(
        value = {ErrorController.class},
        search = SearchStrategy.CURRENT
    )
    public BasicErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider errorViewResolvers) {
        return new BasicErrorController(errorAttributes, this.serverProperties.getError(), (List)errorViewResolvers.orderedStream().collect(Collectors.toList()));
    }
//ErrorPageCustomizer
    @Bean
    public ErrorMvcAutoConfiguration.ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {
        return new ErrorMvcAutoConfiguration.ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
    }
//DefaultErrorViewResolver
@Bean
        @ConditionalOnBean({DispatcherServlet.class})
        @ConditionalOnMissingBean({ErrorViewResolver.class})
        DefaultErrorViewResolver conventionErrorViewResolver() {
            return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
        }

步骤:

​ 一旦系统出现4xx或者5xx之类的错误,ErrorPageCustomizer就会生效(定制错误的响应规则),就会被BasicErrorController处理

​ 响应页面:去哪个页面是由DefaultErrorViewResolver解析得到的;通过DefaultErrorAttributes获得错误页面可共享的信息:

​ Status:状态码

​ exception:异常对象

​ message:异常消息

​ error :错误提示

​ timestamp:时间戳

​ errors:jsr303校验的错误

源码如下:

  1. DefaultErrorAttributes

    //帮我们在页面共享信息
        public Map getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
            Map errorAttributes = this.getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));   
            if (this.includeException != null) {
                options = options.including(new Include[]{Include.EXCEPTION});
            }
            if (!options.isIncluded(Include.EXCEPTION)) {
                errorAttributes.remove("exception");
            }
            if (!options.isIncluded(Include.STACK_TRACE)) {
                errorAttributes.remove("trace");
            }
            if (!options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") != null) {
                errorAttributes.put("message", "");
            }
            if (!options.isIncluded(Include.BINDING_ERRORS)) {
                errorAttributes.remove("errors");
            }
            return errorAttributes;
        }
    
        public Map getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
            Map errorAttributes = new LinkedHashMap();
            errorAttributes.put("timestamp", new Date());
            this.addStatus(errorAttributes, webRequest);
            this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);
            this.addPath(errorAttributes, webRequest);
            return errorAttributes;
        }
    
  2. BasicErrorController

    @Controller
    @RequestMapping({"${server.error.path:${error.path:/error}}"})
    public class BasicErrorController extends AbstractErrorController {
    //响应ErrorPageCustomizer发出的错误请求,${server.error.path:${error.path:/error}}如果有配置文件中配置路径,也能获取
        
        
        //浏览器发送的请求请求头带有text/html,会来到这个方法处理
         @RequestMapping(
            produces = {"text/html"}   //返回html格式
        )
        public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
            HttpStatus status = this.getStatus(request);
            Map model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
            response.setStatus(status.value());
            //ModelAndView  : 获得去往那个页面作为错误页面
            ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
            return modelAndView != null ? modelAndView : new ModelAndView("error", model);
        }
    
        //其他的设备请求头不会有text/html,就来到这个方法
        @RequestMapping   //ResponseEntity相应体力是map,list,javabean,输出格式也是json
        public ResponseEntity> error(HttpServletRequest request) {
            HttpStatus status = this.getStatus(request);
            if (status == HttpStatus.NO_CONTENT) {
                return new ResponseEntity(status);
            } else {
                Map body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
                return new ResponseEntity(body, status);
            }
        }
    
  3. ErrorPageCustomizer

       @Value("${error.path:/error}")
        private String path = "/error";
    系统出现错误以后,来到error请求进行处理;
    
  4. DefaultErrorViewResolver

    protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map model) {
        Iterator var5 = this.errorViewResolvers.iterator();
    
        ModelAndView modelAndView;
        do {
            if (!var5.hasNext()) {
                return null;
            }
    			//调用ErrorViewResolver获取所有错误页面,取到对应的就返回
            ErrorViewResolver resolver = (ErrorViewResolver)var5.next();
            modelAndView = resolver.resolveErrorView(request, status, model);
        } while(modelAndView == null);
    
        return modelAndView;
    }
    
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map model) {
        ModelAndView modelAndView = this.resolve(String.valueOf(status.value()), model);
        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 model) {
        //默认SpringBoot可以去到一个页面?error/404
        String errorViewName = "error/" + viewName;
        
        //模板引擎可以解析这个页面地址就用模板引擎
        TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);
        //模板引擎可用的情况下返回errorViewName指定的视图地址,不可用则在静态资源文件夹下找errorViewName页面
        //error/404.html
        return provider != null ? new ModelAndView(errorViewName, model) : this.resolveResource(errorViewName, model);
    }
    

你可能感兴趣的:(简单认识springboot的错误处理机制)