目录

一、springboot的默认异常处理规则

1.浏览器访问不存在的资源会返回404页面

2.用postman访问不存在的资源会响应json

3.如果用浏览器访问资源后台报错,会响应500错误页面

4.如果用postman访问资源后台报错,会响应json数据

二、自定义错误页面

1.设置自定义错误页面

2.500页面取出错误信息

三、异常处理的自动配置原理

1.ErrorMvcAutoConfiguration

2.BasicErrorController

3.DefaultErrorViewResolver

4.DefaultErrorAttributes

5.总结

四、默认异常处理流程

1.springmvc处理逻辑参考博文

2.DispatcherServlet的doDispatch方法

3.视图解析流程

4.处理handler发生的异常

5.系统默认的异常解析器

6.如果没有任何处理器能处理异常

五、自定义错误处理逻辑

1.自定义错误页面

2.@ControllerAdvice+@ExceptionHandler处理全局异常(推荐)

3.@ResponseStatus+自定义异常

4.Spring底层的异常

5.自定义异常解析器


一、springboot的默认异常处理规则

1.浏览器访问不存在的资源会返回404页面

2.用postman访问不存在的资源会响应json

3.如果用浏览器访问资源后台报错,会响应500错误页面

4.如果用postman访问资源后台报错,会响应json数据

二、自定义错误页面

1.设置自定义错误页面

(1)在静态资源目录下(/static (or /public or /resources or /META-INF/resources),或者模板目录下(/templates) 放一个/error目录。

然后在里面放上对应的4xx.html、5xx.html。

再在浏览器访问4xx错误、5xx错误,就会显示新增的4xx.html、5xx.html页面。

2.500页面取出错误信息

会将以下的信息放到请求域中。

${message} // 错误信息
${trace} // 堆栈信息
${status} // 错误码

三、异常处理的自动配置原理

1.ErrorMvcAutoConfiguration

ErrorMvcAutoConfiguration 自动配置了默认的异常处理规则

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
// Load before the main WebMvcAutoConfiguration so that the error View is available
// 在mvc配置好之后再配置该类
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
// 绑定一些配置属性
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class, WebMvcProperties.class })
public class ErrorMvcAutoConfiguration {private final ServerProperties serverProperties;public ErrorMvcAutoConfiguration(ServerProperties serverProperties) {this.serverProperties = serverProperties;}// 注册一个DefaultErrorAttributes组件,id为errorAttributes,实现了ErrorAttributes, HandlerExceptionResolver, Ordered接口@Bean@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)public DefaultErrorAttributes errorAttributes() {return new DefaultErrorAttributes();}// 注册一个BasicErrorController,id为basicErrorController@Bean@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,ObjectProvider<ErrorViewResolver> errorViewResolvers) {return new BasicErrorController(errorAttributes, this.serverProperties.getError(),errorViewResolvers.orderedStream().collect(Collectors.toList()));}// 错误页的定制化器@Beanpublic ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);}@Beanpublic static PreserveErrorControllerTargetClassPostProcessor preserveErrorControllerTargetClassPostProcessor() {return new PreserveErrorControllerTargetClassPostProcessor();}@Configuration(proxyBeanMethods = false)static class DefaultErrorViewResolverConfiguration {private final ApplicationContext applicationContext;private final ResourceProperties resourceProperties;DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext,ResourceProperties resourceProperties) {this.applicationContext = applicationContext;this.resourceProperties = resourceProperties;}// 配置一个错误视图解析器@Bean@ConditionalOnBean(DispatcherServlet.class)@ConditionalOnMissingBean(ErrorViewResolver.class)DefaultErrorViewResolver conventionErrorViewResolver() {return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);}}@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", matchIfMissing = true)@Conditional(ErrorTemplateMissingCondition.class)protected static class WhitelabelErrorViewConfiguration {private final StaticView defaultErrorView = new StaticView();// 容器中还会放一个View组件,id为error,该View会响应默认错误页@Bean(name = "error")@ConditionalOnMissingBean(name = "error")public View defaultErrorView() {return this.defaultErrorView;}// If the user adds @EnableWebMvc then the bean name view resolver from// WebMvcAutoConfiguration disappears, so add it back in to avoid disappointment.// 容器中放组件 BeanNameViewResolver(视图解析器);按照返回的视图名作为组件的id去容器中找View对象。@Bean@ConditionalOnMissingBeanpublic BeanNameViewResolver beanNameViewResolver() {BeanNameViewResolver resolver = new BeanNameViewResolver();resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);return resolver;}}/*** {@link SpringBootCondition} that matches when no error template view is detected.*/private static class ErrorTemplateMissingCondition extends SpringBootCondition {@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {ConditionMessage.Builder message = ConditionMessage.forCondition("ErrorTemplate Missing");TemplateAvailabilityProviders providers = new TemplateAvailabilityProviders(context.getClassLoader());TemplateAvailabilityProvider provider = providers.getProvider("error", context.getEnvironment(),context.getClassLoader(), context.getResourceLoader());if (provider != null) {return ConditionOutcome.noMatch(message.foundExactly("template from " + provider));}return ConditionOutcome.match(message.didNotFind("error template view").atAll());}}/*** Simple {@link View} implementation that writes a default HTML error page.响应默认错误页*/private static class StaticView implements View {private static final MediaType TEXT_HTML_UTF8 = new MediaType("text", "html", StandardCharsets.UTF_8);private static final Log logger = LogFactory.getLog(StaticView.class);@Overridepublic void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)throws Exception {if (response.isCommitted()) {String message = getMessage(model);logger.error(message);return;}response.setContentType(TEXT_HTML_UTF8.toString());StringBuilder builder = new StringBuilder();Object timestamp = model.get("timestamp");Object message = model.get("message");Object trace = model.get("trace");if (response.getContentType() == null) {response.setContentType(getContentType());}builder.append("<html><body><h1>Whitelabel Error Page</h1>").append("<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>").append("<div id='created'>").append(timestamp).append("</div>").append("<div>There was an unexpected error (type=").append(htmlEscape(model.get("error"))).append(", status=").append(htmlEscape(model.get("status"))).append(").</div>");if (message != null) {builder.append("<div>").append(htmlEscape(message)).append("</div>");}if (trace != null) {builder.append("<div style='white-space:pre-wrap;'>").append(htmlEscape(trace)).append("</div>");}builder.append("</body></html>");response.getWriter().append(builder.toString());}private String htmlEscape(Object input) {return (input != null) ? HtmlUtils.htmlEscape(input.toString()) : null;}private String getMessage(Map<String, ?> model) {Object path = model.get("path");String message = "Cannot render error page for request [" + path + "]";if (model.get("message") != null) {message += " and exception [" + model.get("message") + "]";}message += " as the response has already been committed.";message += " As a result, the response may have the wrong status code.";return message;}@Overridepublic String getContentType() {return "text/html";}}……
}

2.BasicErrorController

处理默认 /error 路径的请求,可以通过配置server.error.path或者error.path修改。

页面响应 new ModelAndView("error", model);

可以依据内容协商 响应白页(浏览器)、json(客户端)。

内容协商参考博文:https://blog.csdn.net/A_art_xiang/article/details/122242942

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {private final ErrorProperties errorProperties;/*** Create a new {@link BasicErrorController} instance.* @param errorAttributes the error attributes* @param errorProperties configuration properties*/public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {this(errorAttributes, errorProperties, Collections.emptyList());}/*** Create a new {@link BasicErrorController} instance.* @param errorAttributes the error attributes* @param errorProperties configuration properties* @param errorViewResolvers error view resolvers*/public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties,List<ErrorViewResolver> errorViewResolvers) {super(errorAttributes, errorViewResolvers);Assert.notNull(errorProperties, "ErrorProperties must not be null");this.errorProperties = errorProperties;}@Overridepublic String getErrorPath() {return null;}// 写出html,寻找error视图@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, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));response.setStatus(status.value());ModelAndView modelAndView = resolveErrorView(request, response, status, model);return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);}// 写出去json数据@RequestMappingpublic ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {HttpStatus status = getStatus(request);if (status == HttpStatus.NO_CONTENT) {return new ResponseEntity<>(status);}Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));return new ResponseEntity<>(body, status);}@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)public ResponseEntity<String> mediaTypeNotAcceptable(HttpServletRequest request) {HttpStatus status = getStatus(request);return ResponseEntity.status(status).build();}……}

3.DefaultErrorViewResolver

默认的错误视图解析器

public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {private static final Map<Series, String> SERIES_VIEWS;static {Map<Series, String> views = new EnumMap<>(Series.class);views.put(Series.CLIENT_ERROR, "4xx");views.put(Series.SERVER_ERROR, "5xx");SERIES_VIEWS = Collections.unmodifiableMap(views);}private ApplicationContext applicationContext;private final ResourceProperties resourceProperties;private final TemplateAvailabilityProviders templateAvailabilityProviders;private int order = Ordered.LOWEST_PRECEDENCE;/*** Create a new {@link DefaultErrorViewResolver} instance.* @param applicationContext the source application context* @param resourceProperties resource properties*/public DefaultErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties) {Assert.notNull(applicationContext, "ApplicationContext must not be null");Assert.notNull(resourceProperties, "ResourceProperties must not be null");this.applicationContext = applicationContext;this.resourceProperties = resourceProperties;this.templateAvailabilityProviders = new TemplateAvailabilityProviders(applicationContext);}DefaultErrorViewResolver(ApplicationContext applicationContext, ResourceProperties resourceProperties,TemplateAvailabilityProviders templateAvailabilityProviders) {Assert.notNull(applicationContext, "ApplicationContext must not be null");Assert.notNull(resourceProperties, "ResourceProperties must not be null");this.applicationContext = applicationContext;this.resourceProperties = resourceProperties;this.templateAvailabilityProviders = templateAvailabilityProviders;}// 解析得到视图对象@Overridepublic 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); //获取跳转的页面,以http状态码命名的html页面。}return modelAndView;}private ModelAndView resolve(String viewName, Map<String, Object> model) {String errorViewName = "error/" + viewName; // 添加一个/errpr/+视图名TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,this.applicationContext);if (provider != null) {return new ModelAndView(errorViewName, model);}return resolveResource(errorViewName, model);}private ModelAndView resolveResource(String viewName, Map<String, Object> model) {for (String location : this.resourceProperties.getStaticLocations()) {try {Resource resource = this.applicationContext.getResource(location);resource = resource.createRelative(viewName + ".html");if (resource.exists()) {return new ModelAndView(new HtmlResourceView(resource), model);}}catch (Exception ex) {}}return null;}@Overridepublic int getOrder() {return this.order;}public void setOrder(int order) {this.order = order;}/*** {@link View} backed by an HTML resource.*/private static class HtmlResourceView implements View {private Resource resource;HtmlResourceView(Resource resource) {this.resource = resource;}@Overridepublic String getContentType() {return MediaType.TEXT_HTML_VALUE;}@Overridepublic void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)throws Exception {response.setContentType(getContentType());FileCopyUtils.copy(this.resource.getInputStream(), response.getOutputStream());}}
}

4.DefaultErrorAttributes

DefaultErrorAttributes:定义错误页面中可以包含哪些数据。

@Order(Ordered.HIGHEST_PRECEDENCE)
public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {private static final String ERROR_ATTRIBUTE = DefaultErrorAttributes.class.getName() + ".ERROR";private final Boolean includeException;/*** Create a new {@link DefaultErrorAttributes} instance.*/public DefaultErrorAttributes() {this.includeException = null;}/*** Create a new {@link DefaultErrorAttributes} instance.* @param includeException whether to include the "exception" attribute* @deprecated since 2.3.0 in favor of* {@link ErrorAttributeOptions#including(Include...)}*/@Deprecatedpublic DefaultErrorAttributes(boolean includeException) {this.includeException = includeException;}@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE;}// 返回ModelAndview,一个最终跳转的页面地址@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {// 保存错误的属性storeErrorAttributes(request, ex);return null;}private void storeErrorAttributes(HttpServletRequest request, Exception ex) {request.setAttribute(ERROR_ATTRIBUTE, ex);}// 可以保存的属性 @Overridepublic Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {Map<String, Object> errorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE));if (Boolean.TRUE.equals(this.includeException)) {options = options.including(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;}// 属性@Override@Deprecatedpublic Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {Map<String, Object> errorAttributes = new LinkedHashMap<>();errorAttributes.put("timestamp", new Date());addStatus(errorAttributes, webRequest);addErrorDetails(errorAttributes, webRequest, includeStackTrace);addPath(errorAttributes, webRequest);return errorAttributes;}// 属性private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {Integer status = getAttribute(requestAttributes, RequestDispatcher.ERROR_STATUS_CODE);if (status == null) {errorAttributes.put("status", 999);errorAttributes.put("error", "None");return;}errorAttributes.put("status", status);try {errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());}catch (Exception ex) {// Unable to obtain a reasonerrorAttributes.put("error", "Http Status " + status);}}// 异常信息private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest,boolean includeStackTrace) {Throwable error = getError(webRequest);if (error != null) {while (error instanceof ServletException && error.getCause() != null) {error = error.getCause();}errorAttributes.put("exception", error.getClass().getName());if (includeStackTrace) {addStackTrace(errorAttributes, error);}}addErrorMessage(errorAttributes, webRequest, error);}private void addErrorMessage(Map<String, Object> errorAttributes, WebRequest webRequest, Throwable error) {BindingResult result = extractBindingResult(error);if (result == null) {addExceptionErrorMessage(errorAttributes, webRequest, error);}else {addBindingResultErrorMessage(errorAttributes, result);}}private void addExceptionErrorMessage(Map<String, Object> errorAttributes, WebRequest webRequest, Throwable error) {Object message = getAttribute(webRequest, RequestDispatcher.ERROR_MESSAGE);if (StringUtils.isEmpty(message) && error != null) {message = error.getMessage();}if (StringUtils.isEmpty(message)) {message = "No message available";}errorAttributes.put("message", message);}private void addBindingResultErrorMessage(Map<String, Object> errorAttributes, BindingResult result) {errorAttributes.put("message", "Validation failed for object='" + result.getObjectName() + "'. "+ "Error count: " + result.getErrorCount());errorAttributes.put("errors", result.getAllErrors());}private BindingResult extractBindingResult(Throwable error) {if (error instanceof BindingResult) {return (BindingResult) error;}if (error instanceof MethodArgumentNotValidException) {return ((MethodArgumentNotValidException) error).getBindingResult();}return null;}private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) {StringWriter stackTrace = new StringWriter();error.printStackTrace(new PrintWriter(stackTrace));stackTrace.flush();errorAttributes.put("trace", stackTrace.toString());}// 错误路径private void addPath(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {String path = getAttribute(requestAttributes, RequestDispatcher.ERROR_REQUEST_URI);if (path != null) {errorAttributes.put("path", path);}}@Overridepublic Throwable getError(WebRequest webRequest) {Throwable exception = getAttribute(webRequest, ERROR_ATTRIBUTE);return (exception != null) ? exception : getAttribute(webRequest, RequestDispatcher.ERROR_EXCEPTION);}@SuppressWarnings("unchecked")private <T> T getAttribute(RequestAttributes requestAttributes, String name) {return (T) requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST);}}

5.总结

错误 -> /error -> BasicErrorController -> 按照bean名字查找错误view -> DefaultErrorViewResolver以http状态作为视图页地址找到视图(404/5xx) -> 如果是浏览器就响应白页,postman就响应json

四、默认异常处理流程

1.springmvc处理逻辑参考博文

springBoot-springMVC请求处理原理_私人博客,有需要请联系17854238061(vx同号)-CSDN博客

2.DispatcherServlet的doDispatch方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 执行目标方法,目标方法有任何异常都会被catch住// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}// 执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException封装。catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}// 进入视图解析流程(是否出现异常都会执行),如果出现异常,mv就会是null// 默认最终会抛出一个异常,被下面捕获(详见4、5)processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}// 异常被捕获,但是未处理,又抛出异常。catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}
}

3.视图解析流程

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;// 如果有异常,会进入异常处理if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else { //进入Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);// 处理handler的异常。mv会被重新定义mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}// Did the handler return a view to render?if (mv != null && !mv.wasCleared()) {render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {// Concurrent handling started during a forwardreturn;}if (mappedHandler != null) {// Exception (if any) is already handled..mappedHandler.triggerAfterCompletion(request, response, null);}
}

4.处理handler发生的异常

处理handler发生的异常,处理完成返回ModelAndView

@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {// 移出request中的一些属性// Success and error responses may use different content typesrequest.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);// Check registered HandlerExceptionResolvers...ModelAndView exMv = null;// 遍历所有的 handlerExceptionResolvers,看谁能处理当前异常【HandlerExceptionResolver是一个处理器异常解析器】if (this.handlerExceptionResolvers != null) {for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {// 调用异常解析器的resolveException方法,默认没有任何人能处理异常,所以异常会被抛出exMv = resolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}if (exMv != null) {if (exMv.isEmpty()) {request.setAttribute(EXCEPTION_ATTRIBUTE, ex);return null;}// We might still need view name translation for a plain error model...if (!exMv.hasView()) {String defaultViewName = getDefaultViewName(request);if (defaultViewName != null) {exMv.setViewName(defaultViewName);}}if (logger.isTraceEnabled()) {logger.trace("Using resolved error view: " + exMv, ex);}else if (logger.isDebugEnabled()) {logger.debug("Using resolved error view: " + exMv);}WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());return exMv;}// 默认没有任何人能处理异常,所以异常会被抛出throw ex;
}

5.系统默认的异常解析器

(1)DefaultErrorAttributes先来处理异常。把异常信息保存到request域,并且返回null

// org.springframework.boot.web.servlet.error.DefaultErrorAttributes#resolveException
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {// 保存错误属性storeErrorAttributes(request, ex);return null;
}

(2)HandlerExceptionResolverComposite里面有三个resolver,会遍历执行

// org.springframework.web.servlet.handler.HandlerExceptionResolverComposite#resolveException
@Override
@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {if (this.resolvers != null) {for (HandlerExceptionResolver handlerExceptionResolver : this.resolvers) {ModelAndView mav = handlerExceptionResolver.resolveException(request, response, handler, ex);if (mav != null) {return mav;}}}return null;
}

(3)其他三个解析器会走父类

// org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver#resolveException
@Override
@Nullable
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {if (shouldApplyTo(request, handler)) {prepareResponse(ex, response);// 解析异常ModelAndView result = doResolveException(request, response, handler, ex);if (result != null) {// Print debug message when warn logger is not enabled.if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));}// Explicitly configured warn logger in logException method.logException(ex, request);}return result;}else {return null;}
}

6.如果没有任何处理器能处理异常

(1)最终底层就会转发到 /error 请求,/error请求会被底层的BasicErrorController处理(详看上面)。

(2)解析错误视图;遍历所有的 ErrorViewResolver 看谁能解析。

(3)默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址,error/500.html

(4)模板引擎最终响应这个页面 error/500.html

五、自定义错误处理逻辑

1.自定义错误页面

详情见上面。

2.@ControllerAdvice+@ExceptionHandler处理全局异常(推荐)

@ControllerAdvice+@ExceptionHandler处理全局异常;底层是 ExceptionHandlerExceptionResolver 支持的

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;/**
* 处理整个web controller的异常
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {@ResponseBody // 可以不加,返回一个视图@ExceptionHandler({ArithmeticException.class,NullPointerException.class})  //指定处理的异常public String handleArithException(Exception e){log.error("异常是:{}",e);return "exception handle success";}
}

3.@ResponseStatus+自定义异常

用处:发生该异常会响应指定的错误码,可以定义错误码对应的错误页面。

底层是 ResponseStatusExceptionResolver ,把responsestatus注解的信息底层调用 response.sendError(statusCode, resolvedReason);相当于给tomcat发送的/error。

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;// 返回一个状态码
@ResponseStatus(value= HttpStatus.FORBIDDEN,reason = "我的自定义异常")
public class MyException extends RuntimeException {public MyException(){}public MyException(String message){super(message);}
}

4.Spring底层的异常

Spring底层封装了一些异常,这些异常会有默认的异常处理逻辑。

如:

参数类型转换异常;DefaultHandlerExceptionResolver,也是会调用 response.sendError(statusCode, resolvedReason);相当于给tomcat发送的/error。

5.自定义异常解析器

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Order(value= Ordered.HIGHEST_PRECEDENCE)  //优先级,数字越小优先级越高
@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response,Object handler, Exception ex) {try {// 写出数据response.sendError(511,"我喜欢的错误");} catch (IOException e) {e.printStackTrace();}return new ModelAndView();}
}

springboot-异常处理使用与原理解析相关推荐

  1. SpringBoot自动装配原理解析——面试可以这样会回答

    1. 前言 SpringBoot是目前软件中最主流的框架,无论是工作还是面试基本都有它的身影,SpringBoot主要解决了传统spring的重量级xml配置Bean,实现了自动装配:所以,我们也常在 ...

  2. SpringBoot @Validated原理解析

    文章目录 一.开发使用`@Validated` 出现问题 1.1 代码 1.2 请求 1.3 响应 二.源码: RequestResponseBodyMethodProcessor.resolveAr ...

  3. springboot原理解析

    Java SpringBoot SpringBoot01:Hello,World! 回顾什么是Spring Spring是一个开源框架,2003 年兴起的一个轻量级的Java 开发框架,作者:Rod ...

  4. 002 第一季SpringBoot2核心技术-核心功能:配置文件、Web开发(原生组件)、数据访问、单元测试、指标监控、原理解析:@Value、命令行参数、手动获取bean、自定义starter

    三.核心技术之- ->核心功能 1. 配置文件 1.1 文件类型 1.1.1 properties 同以前的properties用法 优先级高于yml的方式. 1.1.2 yaml 1) 简介 ...

  5. Java 进阶之路:异常处理的内在原理及优雅的处理方式

    永远不要期待程序在完全理想的状态下运行,异常往往不期而遇,如果没有完善的异常处理机制,后果可能是灾难性的.对于 Java 工程师而言,合理地处理异常是一种基本而重要的能力,然而,在近来的面试中,笔者发 ...

  6. Spring Boot:(二)启动原理解析

    Spring Boot:(二)启动原理解析 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏. ...

  7. Spring Boot(18)---启动原理解析

    Spring Boot(18)---启动原理解析 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会 ...

  8. 分布式缓存系统Redis原理解析

    Redis作为内存数据库已经广泛应用于大数据领域,已经成为分布式架构下的基础组件.本文主要介绍了Redis内部的实现原理包括IO模型.内存管理.数据持久化等以及三种集群架构,旨在了解其中的实现机制. ...

  9. java网关限流_基于网关GateWay实现限流-令牌桶 及原理解析

    一.使用流程 1) 引入坐标 org.springframework.boot spring-boot-starter-data-redis-reactive 2.1.3.RELEASE 2) 创建b ...

  10. SpringMVC @RequestBody和@ResponseBody原理解析

    SpringMVC @RequestBody和@ResponseBody原理解析 前言 @RequestBody作用是将http请求解析为对应的对象.例如: http请求的参数(application ...

最新文章

  1. Linux环境HBase安装配置及使用
  2. 苹果回应 XcodeGhost:暂未发现个人信息受影响
  3. Android学习笔记--动画特效
  4. JavaScript常用基础算法
  5. 【转】 asp.net从视频文件中抓取一桢并生成图像文件的方法 实现多语言本地化应用程序 自动返回上次请求页面...
  6. mysql 降序_MySQL 8 新特性之降序索引底层实现
  7. 自动化yaml文件_从YAML到TypeScript:开发人员对云自动化的看法
  8. WRF模式中eta层的设置以及分别对应的高度(转)
  9. Adobe DreamweaverCS4 beta+可用序列号,FireworkCS4 beta及SoundboothCS4 beta 官方下载地址...
  10. 呼叫中心服务器怎么设置,呼叫中心服务器类型有哪几种?
  11. 【工具】idea去掉UML类图的虚线箭头(依赖关系)
  12. 数字孪生典型应用案例
  13. 使用Kinect制作变身钢铁侠
  14. SSD时代,你的固态硬盘选哪款?
  15. 字节跳动面试--二面算法题复盘
  16. string.h头文件
  17. idea最新Activation code
  18. 应用金数据在线订单功能 为中小企业带来真正价值
  19. 市值3万亿的facebook再出丑闻,你的数据,到底应该归谁?
  20. JQuery 入门 - 附案例代码

热门文章

  1. ffmpeg 添加水印LOGO
  2. delphi ui编辑工具源码_一种无侵入比swagger-ui兼容性更好更简单的API文档生成方案
  3. html登录界面_php实现登录功能
  4. redis启动后 允许访问_解决Redis开启远程访问及密码问题
  5. java横线_知识点:java一些方法会有横线?以Date 过期方法为例
  6. 海南大计算机与网络,2010-2014年海南省计算机与网络发展情况
  7. 打印dataframe的前十行_小学生之十行Python解高思五星题(一)
  8. matlab无限长序列卷积,怎样求未知长度序列的卷积
  9. java mysql语句_java mysql查询语句怎么写
  10. python学习基础知识_python学习基本知识