- SpringBoot中异常处理的流程图
在SpringBoot项目中,如果业务逻辑发生了异常,则首先看发生异常的类中有没有@ExceptionHandle能处理,如果能,则执行方法进行处理,如果没有,则去找全局的@ControllerAdvice注解,进行异常处理,如果还是不能处理,则继续看SpringMVC的异常处理机制能否处理,这里SpringMVC的异常处理机制就是可以处理默认的十几种异常,如请求类型错误Method Not Allowed,如请求体内容错误等,如果不能处理,则走SpringBoot提供的异常处理机制。SpringBoot的异常处理机制就是将当前异常请求重定向到/error请求,并且会自动根据内容协商是返回页面还是json数据。
所有有时当无法处理异常时,转发到/error请求后又会来带一些其他问题。
- ErrorMvcAutoConfiguration 自动配置类
ErrorMvcAutoConfiguration 这个类是SpringBoot提供用于处理异常机制的。
首先这个类中给容器中注入了一个 Controller
@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()));}
@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;}@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);}@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();}protected ErrorAttributeOptions getErrorAttributeOptions(HttpServletRequest request, MediaType mediaType) {ErrorAttributeOptions options = ErrorAttributeOptions.defaults();if (this.errorProperties.isIncludeException()) {options = options.including(Include.EXCEPTION);}if (isIncludeStackTrace(request, mediaType)) {options = options.including(Include.STACK_TRACE);}if (isIncludeMessage(request, mediaType)) {options = options.including(Include.MESSAGE);}if (isIncludeBindingErrors(request, mediaType)) {options = options.including(Include.BINDING_ERRORS);}return options;}/*** Determine if the stacktrace attribute should be included.* @param request the source request* @param produces the media type produced (or {@code MediaType.ALL})* @return if the stacktrace attribute should be included*/protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {switch (getErrorProperties().getIncludeStacktrace()) {case ALWAYS:return true;case ON_PARAM:return getTraceParameter(request);default:return false;}}/*** Determine if the message attribute should be included.* @param request the source request* @param produces the media type produced (or {@code MediaType.ALL})* @return if the message attribute should be included*/protected boolean isIncludeMessage(HttpServletRequest request, MediaType produces) {switch (getErrorProperties().getIncludeMessage()) {case ALWAYS:return true;case ON_PARAM:return getMessageParameter(request);default:return false;}}/*** Determine if the errors attribute should be included.* @param request the source request* @param produces the media type produced (or {@code MediaType.ALL})* @return if the errors attribute should be included*/protected boolean isIncludeBindingErrors(HttpServletRequest request, MediaType produces) {switch (getErrorProperties().getIncludeBindingErrors()) {case ALWAYS:return true;case ON_PARAM:return getErrorsParameter(request);default:return false;}}/*** Provide access to the error properties.* @return the error properties*/protected ErrorProperties getErrorProperties() {return this.errorProperties;}}
通过分析这个类的源码得知,此类主要就是给SpringMVC映射了两个请求,来处理 /error请求。
而两个方法分别是返回html页面与json数据的。