Spring MVC核心功能异常处理机制原理详解
2023-02-23 11:04:47来源:实战案例锦集
如果在请求映射期间发生异常或从请求处理程序(例如@Controller)抛出异常,DispatcherServlet将委托给HandlerExceptionResolver
(资料图片)
下表列出了可用的HandlerExceptionResolver实现:
HandlerExceptionResolver | 描述 |
SimpleMappingExceptionResolver | 异常类名和错误视图名之间的映射。用于在浏览器应用程序中渲染错误页面。 |
DefaultHandlerExceptionResolver | 解析Spring MVC引发的异常,并将其映射为HTTP状态码。另见可选的ResponseEntityExceptionHandler和REST API异常。 |
ResponseStatusExceptionResolver | 使用@ResponseStatus注解解析异常,并根据注解中的值将异常映射为HTTP状态码。 |
ExceptionHandlerExceptionResolver | 通过在@Controller或@ControllerAdvice类中调用@ExceptionHandler方法来解决异常。 |
你可以在Spring配置中声明多个HandlerExceptionResolver
HandlerExceptionResolver的约定规定它可以返回:
指向错误视图的ModelAndView如果异常是在解析器中处理的,则返回空的ModelAndView如果异常仍然未解决,则为null,以便后续的解析器尝试,如果异常在最后仍然存在,则允许它向上冒泡到Servlet容器。MVC配置自动为默认的Spring MVC异常、@ResponseStatus注解的异常以及@ExceptionHandler方法声明了内置的解析器。
容器错误页配置如果任何HandlerExceptionResolver都无法解决异常,因此需要传播,或者响应状态被设置为错误状态(即4xx、5xx), Servlet容器可以在HTML中渲染一个默认的错误页面。要定制容器的默认错误页面,可以在web.xml中声明错误页面映射。如下面的例子所示:
/error
根据前面的示例,当出现异常冒泡或响应具有错误状态时,Servlet容器将在容器中向配置的URL(例如/error)发送错误。然后由DispatcherServlet处理,可能会将其映射到一个@Controller,这个@Controller可以实现为返回一个带有模型的错误视图名称,或者渲染一个JSON响应,如下面的例子所示:
@RestControllerpublic class ErrorController { @RequestMapping(path = "/error") public Map错误处理原理handle(HttpServletRequest request) { Map map = new HashMap (); map.put("status", request.getAttribute("javax.servlet.error.status_code")); map.put("reason", request.getAttribute("javax.servlet.error.message")); return map; }}
public class DispatcherServlet { // 取得容器中所有的异常解析器 private ListhandlerExceptionResolvers; protected void initStrategies(ApplicationContext context) { // 初始化异常解析器 initHandlerExceptionResolvers(context); } private void initHandlerExceptionResolvers(ApplicationContext context) { this.handlerExceptionResolvers = null; if (this.detectAllHandlerExceptionResolvers) { // 获取容器中所有的异常解析器 Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values()); AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers); } } }}
发生异常后逻辑处理
public class DispatcherServlet { protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } 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) { mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { // 获取当前处理句柄HandlerMethod Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); // 处理异常 mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } if (mv != null && !mv.wasCleared()) { render(mv, request, response); } } protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); ModelAndView exMv = null; if (this.handlerExceptionResolvers != null) { // 遍历在上面初始化查找到的所有异常解析器 for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) { // 执行异常解析 exMv = resolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } } if (exMv != null) { // ... return exMv ; } // 如果所有的异常解析器都返回null,则直接抛出异常,该异常会被Servlet进行处理 throw ex ; }}异常解析器默认配置
public class WebMvcAutoConfiguration { @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(WebProperties.class) public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration { } }public class WebMvcConfigurationSupport { @Bean public HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) { ListexceptionResolvers = new ArrayList<>(); // 处理自定义异常解析器的配置 configureHandlerExceptionResolvers(exceptionResolvers); if (exceptionResolvers.isEmpty()) { // 如果没有异常解析器,那么添加默认的异常解析器 addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager); } extendHandlerExceptionResolvers(exceptionResolvers); HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); // 设置高优先级,Composite成为统一的入口 composite.setOrder(0); composite.setExceptionResolvers(exceptionResolvers); return composite; } // 默认配置 protected final void addDefaultHandlerExceptionResolvers(List exceptionResolvers, ContentNegotiationManager mvcContentNegotiationManager) { ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver(); exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager); exceptionHandlerResolver.setMessageConverters(getMessageConverters()); exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers()); exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers()); if (jackson2Present) { exceptionHandlerResolver.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice())); } if (this.applicationContext != null) { exceptionHandlerResolver.setApplicationContext(this.applicationContext); } exceptionHandlerResolver.afterPropertiesSet(); exceptionResolvers.add(exceptionHandlerResolver); ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver(); responseStatusResolver.setMessageSource(this.applicationContext); exceptionResolvers.add(responseStatusResolver); exceptionResolvers.add(new DefaultHandlerExceptionResolver()); }}
完毕!!!