Summary of Java knowledge points: If you want to see it, you can enter it from here
Table of Contents
-
-
- 1.8. Automatic exception handling
-
- 1.8.1. Response rules
- 1.8.2, error controller
- 1.8.3. View resolver
- 1.8.4, error attribute processing
-
1.8, automatic exception handling
Spring Boot provides automatic configuration for exception handling through the configuration class ErrorMvcAutoConfiguration, which injects the following four components into the container.
-
ErrorPageCustomizer: This component will forward the request to “/error” by default after an exception occurs in the system.
-
BasicErrorController: Handles the default “/error” request.
-
DefaultErrorViewResolver: The default error view resolver, which resolves exception information to the corresponding error view.
-
DefaultErrorAttributes: error attribute processing tool, which can get exception or error information from the request
1.8.1, Response rules
ErrorMvcAutoConfiguration injects a component called ErrorPageCustomizer into the container, which is mainly used to customize the response rules of error pages.
@Bean public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {<!-- --> return new ErrorPageCustomizer(this. serverProperties, dispatcherServletPath); }
Register error page response rules through the registerErrorPages() method. When an exception occurs in the system, the ErrorPageCustomizer component will automatically take effect
static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {<!-- --> //server properties private final ServerProperties properties; //Servlet path private final DispatcherServletPath dispatcherServletPath; protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {<!-- --> this.properties = properties; this. dispatcherServletPath = dispatcherServletPath; } @Override public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {<!-- --> //Forward the request to /errror(this.properties.getError().getPath()) ErrorPage errorPage = new ErrorPage( this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath())); // register error page errorPageRegistry. addErrorPages(errorPage); } @Override public int getOrder() {<!-- --> return 0; } }
1.8.2, error controller
ErrorMvcAutoConfiguration also injects an error controller component BasicErrorController into the container
@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())); }
It is a Controller, and Spring Boot performs unified error handling through BasicErrorController, which is mainly used to process requests whose path is /error
@Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController {<!-- --> // error attribute private final ErrorProperties errorProperties; .....Construction method.... //Used to handle the exception that occurred in the request of the browser client @RequestMapping(produces = MediaType. TEXT_HTML_VALUE) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {<!-- --> // get error status code HttpStatus status = getStatus(request); //Encapsulate model data according to error information for page display Map<String, Object> model = Collections .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML))); //Set the error status code for the response object response.setStatus(status.value()); //Call the resolveErrorView() method to generate a ModelAndView object using the view resolver ModelAndView modelAndView = resolveErrorView(request, response, status, model); //Set the page corresponding to the error error return (modelAndView != null) ? modelAndView : new ModelAndView("error", model); } //Used to handle errors in machine client requests (such as Android, IOS, Postman, etc.) @RequestMapping public 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); } }
When using a browser to access an exception, it will enter the errorHtml() method in the BasicErrorController controller for processing. In the errorHtml() method, the resolveErrorView() method of the parent class (AbstractErrorController) will be called to obtain all ErrorViewResolver objects in the container. (error view resolvers, including DefaultErrorViewResolver), together to resolve exception information.
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,Map<String, Object> model) {<!-- --> //Get all error view resolvers in the container to handle the exception information for (ErrorViewResolver resolver : this. errorViewResolvers) {<!-- --> //Call the resolveErrorView of the view resolver to resolve to the error view page ModelAndView modelAndView = resolver. resolveErrorView(request, status, model); if (modelAndView != null) {<!-- --> return modelAndView; } } return null; }
1.8.3, view resolver
ErrorMvcAutoConfiguration injects a default error view resolver component DefaultErrorViewResolver into the container
@Bean @ConditionalOnBean(DispatcherServlet. class) @ConditionalOnMissingBean(ErrorViewResolver. class) DefaultErrorViewResolver conventionErrorViewResolver() {<!-- --> return new DefaultErrorViewResolver(this. applicationContext, this. resourceProperties); }
When the requesting client is a browser, Spring Boot will obtain all ErrorViewResolver objects in the container, and call their resolveErrorView() method to resolve the exception information.
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {<!-- --> //series view 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); } ......... //Resolve error view @Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {<!-- --> //Try to parse the error status code as the error page name ModelAndView modelAndView = resolve(String. valueOf(status. value()), model); if (modelAndView == null & amp; & amp; SERIES_VIEWS. containsKey(status. series())) {<!-- --> // Parse with 4xx or 5xx as the error page modelAndView = resolve(SERIES_VIEWS. get(status. series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) {<!-- --> //Error template page, such as error/404 String errorViewName = "error/" + viewName; //When the template engine can parse these template pages, use the template engine to parse TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,this.applicationContext); if (provider != null) {<!-- --> //If the template can be parsed to the template page, return the view specified by errorViewName return new ModelAndView(errorViewName, model); } //If the template engine can't resolve it, go to the static resource to find the page corresponding to errorViewName return resolveResource(errorViewName, model); } //Find the page corresponding to errorViewName in the static resource file private ModelAndView resolveResource(String viewName, Map<String, Object> model) {<!-- --> //Loop through all static resource folders for (String location : this.resourceProperties.getStaticLocations()) {<!-- --> try {<!-- --> Resource resource = this.applicationContext.getResource(location); resource = resource.createRelative(viewName + ".html"); //If the above error page exists under the static resource folder, return directly if (resource. exists()) {<!-- --> return new ModelAndView(new HtmlResourceView(resource), model); } } catch (Exception ex) {<!-- --> } } return null; } }
DefaultErrorViewResolver generates an error view error/status according to the error status code (such as 404, 500, 400, etc.), and then tries to parse it. From the templates directory under the classpath classpath, search for error/status.html, if the template engine can resolve to error /status view, the view and data will be encapsulated into ModelAndView to return and end. If not found, search for error/status.html from the static resource folder in turn, if the error page is found in the static folder, return and end the entire parsing flow, if still not found, process the default ” /error ” request, using Spring Boot’s default error page (Whitelabel Error Page).
1.8.4, error attribute processing
ErrorMvcAutoConfiguration injects a component default error attribute processing tool DefaultErrorAttributes into the container, which can get exception or error information from the request and encapsulate it as a Map object to return.
@Bean @ConditionalOnMissingBean(value = ErrorAttributes. class, search = SearchStrategy. CURRENT) public DefaultErrorAttributes errorAttributes() {<!-- --> return new DefaultErrorAttributes(); }
public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {<!-- --> // get error attribute @Override public 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) & amp; & amp; errorAttributes.get("message") != null) {<!-- --> errorAttributes. put("message", ""); } if (!options.isIncluded(Include.BINDING_ERRORS)) {<!-- --> errorAttributes. remove("errors"); } return errorAttributes; } @Override @Deprecated public 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; } //add error status code 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 get reason errorAttributes. put("error", "Http Status " + status); } } // add error details private void addErrorDetails(Map<String, Object> errorAttributes, WebRequest webRequest, boolean includeStackTrace) {<!-- --> Throwable error = getError(webRequest); if (error != null) {<!-- --> while (error instanceof ServletException & amp; & amp; error. getCause() != null) {<!-- --> error = error. getCause(); } errorAttributes. put("exception", error. getClass(). getName()); if (includeStackTrace) {<!-- --> addStackTrace(errorAttributes, error); } } addErrorMessage(errorAttributes, webRequest, error); } // add error/exception message 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); } } //Add the exception object that caused the request processing to fail private void addExceptionErrorMessage(Map<String, Object> errorAttributes, WebRequest webRequest, Throwable error) {<!-- --> Object message = getAttribute(webRequest, RequestDispatcher. ERROR_MESSAGE); if (StringUtils.isEmpty(message) & amp; & amp; error != null) {<!-- --> message = error. getMessage(); } if (StringUtils. isEmpty(message)) {<!-- --> message = "No message available"; } errorAttributes. put("message", message); } //Add binding result error 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()); } //error/exception stack information 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()); } //The requested URL path when an error/exception is thrown private void addPath(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) {<!-- --> String path = getAttribute(requestAttributes, RequestDispatcher.ERROR_REQUEST_URI); if (path != null) {<!-- --> errorAttributes. put("path", path); } } // error message @Override public Throwable getError(WebRequest webRequest) {<!-- --> Throwable exception = getAttribute(webRequest, ERROR_ATTRIBUTE); return (exception != null) ? exception : getAttribute(webRequest,RequestDispatcher.ERROR_EXCEPTION); } }
When BasicErrorController handles errors, it will call the getErrorAttributes() method of DefaultErrorAttributes to obtain error or exception information, encapsulate it into model data (Map object), and return it to the page or JSON data.
- timestamp: timestamp;
- status: error status code
- error: error message
- exception: the exception object that caused the request processing to fail
- message: error/exception message
- trace: error/exception stack information
- path: URL path requested when an error/exception is thrown