SpringMVC源码(十):异常View视图的处理
1、调试示例代码
1.1、Controller
1 import org.springframework.stereotype.Controller; 2 import org.springframework.web.bind.annotation.ExceptionHandler; 3 import org.springframework.web.bind.annotation.RequestMapping; 4 import org.springframework.web.servlet.ModelAndView; 5 6 @Controller 7 public class ExceptionController { 8 9 @RequestMapping("/exception") 10 public ModelAndView exceptionMethod(ModelAndView view) { 11 view.setViewName("success"); 12 String str = null; 13 str.length(); 14 return view; 15 } 16 17 @ExceptionHandler(RuntimeException.class) 18 public ModelAndView handleError(RuntimeException error) { 19 ModelAndView mav = new ModelAndView(); 20 mav.setViewName("error"); 21 mav.addObject("msg", "Runtime error"); 22 return mav; 23 } 24 }
2、success.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <html> 3 <head> 4 <title>Title</title> 5 </head> 6 <body> 7 <h1>success</h1> 8 </body> 9 </html>
3、error.jsp
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %> 2 <html> 3 <head> 4 <title>Title</title> 5 </head> 6 <body> 7 <h1>This is error page</h1><br/> 8 ${msg} 9 </body> 10 </html>
2、核心流程图
3、核心流程源码分析
在执行ExceptionController控制器的exceptionMethod方法时会出现空指针异常NullPointerException,在源码(八):执行Controller控制器流程中提到在InvocableHandlerMethod的doInvoke() 方法中会进入到目标Controller控制器的逻辑中,下面重点来看doInvoke()中对异常的处理。
InvocableHandlerMethod#doInvoke() 核心伪代码:
1 // 调用执行 2 protected Object doInvoke(Object... args) throws Exception { 3 try { 4 // 调用目标控制器Controller 5 return getBridgedMethod().invoke(getBean(), args); 6 } 7 // 不合法的参数异常 8 catch (IllegalArgumentException ex) { 9 assertTargetBean(getBridgedMethod(), getBean(), args); 10 String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); 11 throw new IllegalStateException(formatInvokeError(text, args), ex); 12 } 13 catch (InvocationTargetException ex) { 14 // 获取异常信息 15 Throwable targetException = ex.getTargetException(); 16 // RuntimeException异常 17 if (targetException instanceof RuntimeException) { 18 throw (RuntimeException) targetException; 19 } 20 // Error错误 21 else if (targetException instanceof Error) { 22 throw (Error) targetException; 23 } 24 // Exception异常 25 else if (targetException instanceof Exception) { 26 throw (Exception) targetException; 27 } 28 // 报错非 RuntimeException、Error、Exception类型的异常,抛出IllegalStateException异常 29 else { 30 throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException); 31 } 32 } 33 }
当前抛出的NullPointerException,空指针的类图关系如下图,继承自Exception、RuntimeException,会捕获RuntimeException异常并抛出。
回到源码(五):MVC请求执行整体源码概览的doDispatch()流程处理主流程中,DispatcherServlet#doDispatch() 对异常的处理核心伪代码
1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 3 // 处理请求的处理器链(包含处理器和对应的interceptor) 4 HandlerExecutionChain mappedHandler = null; 5 6 // 封装model和view的容器 7 ModelAndView mv = null; 8 // 处理请求过程中抛出的异常,但是不包含渲染过程中抛出的异常 9 Exception dispatchException = null; 10 11 try { 12 13 // HandlerMapping处理器获取 14 mappedHandler = getHandler(processedRequest); 15 16 // HandlerAdapter适配器获取 17 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 18 19 // 真正的调用handler方法,调用Controller控制器,并返回视图 20 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 21 } 22 // 异常处理 23 catch (Exception ex) { 24 // 记录异常 25 dispatchException = ex; 26 } 27 catch (Throwable err) { 28 // 初始化dispatchException异常 29 dispatchException = new NestedServletException("Handler dispatch failed", err); 30 } 31 // 处理返回结果,包括处理异常、渲染页面、触发Interceptor的afterCompletion 32 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 33 34 }
Controller控制器中出现异常后,将异常对象设置给声明的Exception类型的dispatchException对象,并处理返回结果,在源码(九):无异常View视图解析中已经分析了返回正常的String类型的View视图解析渲染流程,下面我们来看看处理过程中出现异常,View视图如何进行处理。
1 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, 2 @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, 3 @Nullable Exception exception) throws Exception { 4 5 // 在处理Controller控制器过程是否异常的标识 6 boolean errorView = false; 7 8 // 如果请求处理过程中有异常抛出则处理异常 9 if (exception != null) { 10 // 从ModelAndViewDefiningException中获得ModelAndView对象 11 if (exception instanceof ModelAndViewDefiningException) { 12 mv = ((ModelAndViewDefiningException) exception).getModelAndView(); 13 } 14 // 处理异常,生成ModelAndView对象 15 else { 16 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); 17 mv = processHandlerException(request, response, handler, exception); 18 errorView = (mv != null); 19 } 20 } 21 22 // 是否进行页面渲染 23 if (mv != null && !mv.wasCleared()) { 24 // 渲染页面 25 render(mv, request, response); 26 // 清理请求中的错误消息属性 27 if (errorView) { 28 WebUtils.clearErrorRequestAttributes(request); 29 } 30 } 31 32 // 发出请求处理完成通知,触发Interceptor的afterCompletion 33 if (mappedHandler != null) { 34 mappedHandler.triggerAfterCompletion(request, response, null); 35 } 36 }
1、声明异常视图标识,默认值为false
2、异常类型判断
2.1、ModelAndViewDefiningException类型
异常为ModelAndViewDefiningException类型,直接从exception中获取异常的View视图。
2.2、非ModelAndViewDefiningException类型
1、声明异常视图对象
2、处理异常并获取ModelAndView对象
1、异常处理器的初始化
在源码(三):MVC九大内置组件初始化中已提到HandlerExceptionResolver异常处理器组件的初始化过程,此处不再赘述。
2、处理异常获取MV对象
处理异常并获取MV对象,DispatcherServlet#processHandlerException() 核心伪代码
1 protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, 2 @Nullable Object handler, Exception ex) throws Exception { 3 4 // 声明异常视图对象 5 ModelAndView exMv = null; 6 // 遍历 HandlerExceptionResolver 数组,解析异常,生成 ModelAndView 对象 7 if (this.handlerExceptionResolvers != null) { 8 // 遍历 HandlerExceptionResolver 数组 9 for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) { 10 // 解析异常,生成 ModelAndView 对象 11 exMv = resolver.resolveException(request, response, handler, ex); 12 // 生成成功,结束循环 13 if (exMv != null) { 14 break; 15 } 16 } 17 } 18 // 生成了 ModelAndView 对象,进行返回 19 if (exMv != null) { 20 // ModelAndView 对象为空,则返回 null 21 if (exMv.isEmpty()) { 22 request.setAttribute(EXCEPTION_ATTRIBUTE, ex); 23 return null; 24 } 25 // 没有视图则设置默认视图 26 if (!exMv.hasView()) { 27 String defaultViewName = getDefaultViewName(request); 28 if (defaultViewName != null) { 29 exMv.setViewName(defaultViewName); 30 } 31 } 32 33 return exMv; 34 } 35 // 未生成 ModelAndView 对象,则抛出异常 36 throw ex; 37 }
2.1、遍历异常处理解析器,解析异常
AbstractHandlerExceptionResolver#resolveException() 核心伪代码
1 // 解析异常 2 public ModelAndView resolveException( 3 HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { 4 5 // 判断是否可以应用 6 if (shouldApplyTo(request, handler)) { 7 // 阻止缓存 8 prepareResponse(ex, response); 9 // 执行解析异常,返回modelAndView对象 10 ModelAndView result = doResolveException(request, response, handler, ex); 11 // 如果ModelAndView对象非空,则进行返回 12 return result; 13 } 14 // 不可应用,直接返回null 15 else { 16 return null; 17 } 18 }
ExceptionHandlerExceptionResolver#doResolveHandlerMethodException() 核心伪代码
1 protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, 2 HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) { 3 4 // 获得异常对应的 ServletInvocableHandlerMethod 对象,handlerMethod是处理异常的方法 5 ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception); 6 if (exceptionHandlerMethod == null) { 7 return null; 8 } 9 10 // 设置 ServletInvocableHandlerMethod 对象的相关属性,主要是为了处理HandlerMethod中方法的参数以及handlerMethod的返回值 11 if (this.argumentResolvers != null) { 12 exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); 13 } 14 if (this.returnValueHandlers != null) { 15 exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); 16 } 17 18 // 创建 ServletWebRequest 对象 19 ServletWebRequest webRequest = new ServletWebRequest(request, response); 20 // 创建 ModelAndViewContainer 对象 21 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); 22 23 try { 24 // 执行处理该异常的方法 ServletInvocableHandlerMethod 的调用,主要是对参数和返回值及进行处理,通过ModelAndViewContainer作为中间变量 25 // 将一些视图名、参数放到ModelAndViewContainer中 26 Throwable cause = exception.getCause(); 27 if (cause != null) { 28 // cause不为空,将cause也作为参数传入 29 exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod); 30 } 31 else { 32 exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod); 33 } 34 } 35 catch (Throwable invocationEx) { 36 return null; 37 } 38 39 // 如果 mavContainer 已处理,则返回 '空的' ModelAndView 对象。 40 if (mavContainer.isRequestHandled()) { 41 return new ModelAndView(); 42 } 43 // 如果 mavContainer 未处,则基于mavContainer生成 ModelAndView 对象 44 else { 45 ModelMap model = mavContainer.getModel(); 46 HttpStatus status = mavContainer.getStatus(); 47 // 创建 ModelAndView 对象,并设置相关属性 48 ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status); 49 mav.setViewName(mavContainer.getViewName()); 50 // 若mavContainer的视图不是String类型,设置视图名称 51 if (!mavContainer.isViewReference()) { 52 mav.setView((View) mavContainer.getView()); 53 } 54 // 重定向的处理 55 if (model instanceof RedirectAttributes) { 56 Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); 57 // FlashMapManager组件重定向时的参数传递 58 RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); 59 } 60 return mav; 61 } 62 }
1、获取InvocableHandlerMethod对象
获取目标Controller控制器的相关信息,执行ExceptionController中的异常处理方法handlerError()。
获取处理异常的目标方法,ExceptionHandlerExceptionResolver#getExceptionHandlerMethod() 核心伪代码如下
1 // 获取处理异常的目标方法 2 protected ServletInvocableHandlerMethod getExceptionHandlerMethod( 3 @Nullable HandlerMethod handlerMethod, Exception exception) { 4 5 // 处理器的类型 6 Class<?> handlerType = null; 7 8 // 如果 handlerMethod 非空,则优先获得 Controller 对应的 @ExceptionHandler 处理器对应的方法 9 if (handlerMethod != null) { 10 // 获得 handlerType 11 handlerType = handlerMethod.getBeanType(); 12 // 根据 handlerType 对应的 ExceptionHandlerMethodResolver 对象 13 ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType); 14 if (resolver == null) { 15 resolver = new ExceptionHandlerMethodResolver(handlerType); 16 this.exceptionHandlerCache.put(handlerType, resolver); 17 } 18 // 获得异常对应的 Method 处理方法 19 Method method = resolver.resolveMethod(exception); 20 // 如果获得该异常对应的 Method 处理方法,则创建 ServletInvocableHandlerMethod 对象,并返回 21 if (method != null) { 22 return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method); 23 } 24 // 获得 handlerType 的原始类, 有可能是代理对象 25 if (Proxy.isProxyClass(handlerType)) { 26 handlerType = AopUtils.getTargetClass(handlerMethod.getBean()); 27 } 28 } 29 30 // 再次使用 ControllerAdvice 对应的 @ExceptionHandler 处理器对应的方法 31 for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) { 32 ControllerAdviceBean advice = entry.getKey(); 33 // 如果 ControllerAdvice 支持当前的 handlerType 34 if (advice.isApplicableToBeanType(handlerType)) { 35 // 获得 handlerType 对应的 ExceptionHandlerMethodResolver 对象 36 ExceptionHandlerMethodResolver resolver = entry.getValue(); 37 // 获得异常对应的 Method 处理方法 38 Method method = resolver.resolveMethod(exception); 39 if (method != null) { 40 return new ServletInvocableHandlerMethod(advice.resolveBean(), method); 41 } 42 } 43 } 44 // 获取不到,返回null 45 return null; 46 }
1.1、异常方法解析器缓存exceptionHandlerAdviceCache初始化
在MVC容器启动,创建ExceptionHandlerExceptionResolver的bean,初始化bean实例的时候,会初始化异常方法解析器缓存。
initializeBean() -> invokeInitMethods() -> ExceptionHandlerExceptionResolver#afterPropertiesSet()
初始化异常方法解析器,ExceptionHandlerMethodResolver#initExceptionHandlerAdviceCache() 核心伪代码:
1 // ControllerAdviceBean与异常方法解析器映射缓存 2 private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = 3 new LinkedHashMap<>(); 4 5 // 初始化ExceptionHandlerAdvice缓存 6 private void initExceptionHandlerAdviceCache() { 7 // 获取所有被@ControllerAdvice修饰的bean集合 8 List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); 9 // 遍历bean集合 10 for (ControllerAdviceBean adviceBean : adviceBeans) { 11 // 获取Class对象 12 Class<?> beanType = adviceBean.getBeanType(); 13 // Class对象为null,抛出异常 14 if (beanType == null) { 15 throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean); 16 } 17 // 根据Class对象创建异常方法解析器ExceptionHandlerMethodResolver对象 18 ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType); 19 // 缓存中不存在,添加进缓存 20 if (resolver.hasExceptionMappings()) { 21 this.exceptionHandlerAdviceCache.put(adviceBean, resolver); 22 } 23 // ... 24 } 25 }
1.2、获取处理异常目标方法整理流程
·根据Controller的Class对象,优先从缓存exceptionHandlerCache中获取异常处理解析器ExceptionHandlerMethodResolver,缓存中获取不到,创建异常处理解析器并设置进缓存中;
·异常处理器解析器解析异常,获取处理异常的方法Method,若获取到当前Controller中的处理异常的方法,根据处理异常的方法及当前Controller的bean创建ServletInvocableHandlerMethod对象并返回。
·判断当前Controller是否为代理类,若未代理类,获取原始类型;
·若当前Controller中无处理异常的方法,在@ControllerAdvice注解修饰的类中查找对应的@ExceptionHandler 处理器对应的方法,若能找到,创建ServletInvocableHandlerMethod对象并返回,若无法找到,返回null,抛出原始异常。
2、设置参数解析器argumentResolvers、返回值处理器returnValueHandlers
异常处理解析器ExceptionHandlerExceptionResolver中的argumentResolvers、returnValueHandlers与与源码(六):Handler处理器获取中AbstractHandlerMethodMapping#mappingRegistry属性的初始化过程类似。
在MVC容器启动,创建MVC容器中单例的bean对象,创建ExceptionHandlerExceptionResolver的bean实例过程中,在getBean() -> doGetBean() -> createBean() -> doCreateBean() -> initializeBean() -> invokeInitMethods() -> ExceptionHandlerExceptionResolver#afterPropertiesSet()
ExceptionHandlerExceptionResolver#afterPropertiesSet(),调用bean的afterPropertiesSet方法核心伪代码
1 public void afterPropertiesSet() { 2 // 初始化ControllerAdvice中定义的ExceptionHandlerAdvice处理 3 initExceptionHandlerAdviceCache(); 4 // 参数值解析器为空 5 if (this.argumentResolvers == null) { 6 // 获取默认的参数值解析器 7 List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); 8 // 填充HandlerMethodArgumentResolverComposite、ExceptionHandlerExceptionResolver中的argumentResolvers属性 9 this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); 10 } 11 if (this.returnValueHandlers == null) { 12 // 获取默认的返回值解析器 13 List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); 14 // 填充HandlerMethodReturnValueHandlerComposite、ExceptionHandlerExceptionResolver中的returnValueHandlers属性 15 this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); 16 } 17 }
获取默认的参数值解析器,ExceptionHandlerExceptionResolver#getDefaultArgumentResolvers() 核心代码
1 // 获取默认的参数值解析器 2 protected List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { 3 List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); 4 5 // 基于注解的参数值解析器 6 resolvers.add(new SessionAttributeMethodArgumentResolver()); 7 resolvers.add(new RequestAttributeMethodArgumentResolver()); 8 9 // 基于类型的参数值解析器 10 resolvers.add(new ServletRequestMethodArgumentResolver()); 11 resolvers.add(new ServletResponseMethodArgumentResolver()); 12 resolvers.add(new RedirectAttributesMethodArgumentResolver()); 13 resolvers.add(new ModelMethodProcessor()); 14 15 // 自定义的参数值解析器 16 if (getCustomArgumentResolvers() != null) { 17 resolvers.addAll(getCustomArgumentResolvers()); 18 } 19 20 return resolvers; 21 }
获取默认的返回值处理器,ExceptionHandlerExceptionResolver#getDefaultArgumentResolvers() 核心代码
1 // 获取默认的返回值处理器 2 protected List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { 3 List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(); 4 5 // 返回单一值的返回值处理器 6 handlers.add(new ModelAndViewMethodReturnValueHandler()); 7 handlers.add(new ModelMethodProcessor()); 8 handlers.add(new ViewMethodReturnValueHandler()); 9 handlers.add(new HttpEntityMethodProcessor( 10 getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice)); 11 12 // 基于注解的返回值类型 13 handlers.add(new ModelAttributeMethodProcessor(false)); 14 handlers.add(new RequestResponseBodyMethodProcessor( 15 getMessageConverters(), this.contentNegotiationManager, this.responseBodyAdvice)); 16 17 // 返回多个值的返回值处理器 18 handlers.add(new ViewNameMethodReturnValueHandler()); 19 handlers.add(new MapMethodProcessor()); 20 21 // 自定义返回值处理器 22 if (getCustomReturnValueHandlers() != null) { 23 handlers.addAll(getCustomReturnValueHandlers()); 24 } 25 26 handlers.add(new ModelAttributeMethodProcessor(true)); 27 28 return handlers; 29 }
3、创建WebRequest对象
对象中请求的访问路径uri。
4、创建 ModelAndViewContainer 对象
创建的 ModelAndViewContainer 对象,model属性和view属性后续填充。
5、调用异常处理并获取ModelAndView对象
exceptionHandlerMethod.invokeAndHandle(...)的执行,实际上是执行ServletInvocableHandlerMethod#invokeAndHandle(...)方法,流程与源码(八):执行Controller控制器流程中,根据请求路径获取到正常的Controller控制器一样,需要执行的目标方法不同,异常时执行的目标方法handlerError(),此处不再赘述。
6、判断mavContainer是否已经被处理
·若mavContainer已经被处理,返回空的ModelAndView对象
·若mavContainer未被处理
1、ModelAndViewContainer中的view属性是否为String类型,若为String类型,填充ModelAndView的View属性;
2、ModelAndViewContainer中的model属性若为重定参数,重定向的参数传递处理。
7、返回ModelAndView对象
2.2、判断异常视图exMv返回对应的ModelAndView对象
1、异常视图exMv对象为空,抛出异常
2、异常视图exMv对象中view属性、model属性为null,返回null
ModelAndView是否为空判断,ModelAndView#isEmpty() 核心伪代码
1 // ModelAndView中的model、view属性为空 2 public boolean isEmpty() { 3 return (this.view == null && CollectionUtils.isEmpty(this.model)); 4 }
3、若异常视图exMv对象中的view属性为空,为ModelAndView设置默认视图
设置默认视图,DispatcherServlet#getDefaultViewName() 核心代码
1 // 视图名称转换器 2 private RequestToViewNameTranslator viewNameTranslator; 3 4 // 为请求提供默认的视图 5 protected String getDefaultViewName(HttpServletRequest request) throws Exception { 6 // 获取默认视图 7 return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null); 8 }
1 // 前缀 2 private String prefix = ""; 3 4 // 后缀 5 private String suffix = ""; 6 7 // 获取根据request请求路径URI转换成基于配置参数的视图名称 8 public String getViewName(HttpServletRequest request) { 9 // 获得请求路径 10 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH); 11 // 获得视图名 12 return (this.prefix + transformPath(lookupPath) + this.suffix); 13 }
根据请求路径转换视图名称,DefaultRequestToViewNameTranslator#transformPath() 核心代码:
1 private static final String SLASH = "/"; 2 // 前缀 3 private String prefix = ""; 4 // 后缀 5 private String suffix = ""; 6 // 分隔符 7 private String separator = SLASH; 8 // 是否移除开头 SLASH 9 private boolean stripLeadingSlash = true; 10 // 是否移除末尾 SLASH 11 private boolean stripTrailingSlash = true; 12 // 是否移除拓展名 13 private boolean stripExtension = true; 14 15 // 转换当前请求路径 16 protected String transformPath(String lookupPath) { 17 String path = lookupPath; 18 // 移除头部 / 19 if (this.stripLeadingSlash && path.startsWith(SLASH)) { 20 path = path.substring(1); 21 } 22 // 移除尾部 / 23 if (this.stripTrailingSlash && path.endsWith(SLASH)) { 24 path = path.substring(0, path.length() - 1); 25 } 26 // 移除拓展名 27 if (this.stripExtension) { 28 path = StringUtils.stripFilenameExtension(path); 29 } 30 // 替换分隔符 31 if (!SLASH.equals(this.separator)) { 32 path = StringUtils.replace(path, SLASH, this.separator); 33 } 34 return path; 35 }
4、为Request设置异常相关属性。
1 //响应码 2 public static final String ERROR_STATUS_CODE_ATTRIBUTE = "javax.servlet.error.status_code"; 3 // 异常类型 4 public static final String ERROR_EXCEPTION_TYPE_ATTRIBUTE = "javax.servlet.error.exception_type"; 5 // 错误消息 6 public static final String ERROR_MESSAGE_ATTRIBUTE = "javax.servlet.error.message"; 7 // 异常信息 8 public static final String ERROR_EXCEPTION_ATTRIBUTE = "javax.servlet.error.exception"; 9 // 处理失败的访问路径 10 public static final String ERROR_REQUEST_URI_ATTRIBUTE = "javax.servlet.error.request_uri"; 11 // servlet名称 12 public static final String ERROR_SERVLET_NAME_ATTRIBUTE = "javax.servlet.error.servlet_name"; 13 14 // Request设置异常属性 15 public static void exposeErrorRequestAttributes(HttpServletRequest request, Throwable ex, 16 @Nullable String servletName) { 17 // 设置异常处理的响应码 18 exposeRequestAttributeIfNotPresent(request, ERROR_STATUS_CODE_ATTRIBUTE, HttpServletResponse.SC_OK); 19 // 设置异常类型 20 exposeRequestAttributeIfNotPresent(request, ERROR_EXCEPTION_TYPE_ATTRIBUTE, ex.getClass()); 21 // 设置错误消息 22 exposeRequestAttributeIfNotPresent(request, ERROR_MESSAGE_ATTRIBUTE, ex.getMessage()); 23 // 设置异常信息 24 exposeRequestAttributeIfNotPresent(request, ERROR_EXCEPTION_ATTRIBUTE, ex); 25 // 设置处理失败的访问路径 26 exposeRequestAttributeIfNotPresent(request, ERROR_REQUEST_URI_ATTRIBUTE, request.getRequestURI()); 27 // 设置异常的servlet名称 28 if (servletName != null) { 29 exposeRequestAttributeIfNotPresent(request, ERROR_SERVLET_NAME_ATTRIBUTE, servletName); 30 } 31 }
属性设置详情如下:
3、设置异常视图标识
若获取到了异常视图exMv,设置异常视图标识为true,便于在完成视图渲染后,清除request中相关的异常属性.
请求request相关异常属性,WebUtils#clearErrorRequestAttributes() 核心伪代码:
1 // 响应码 2 public static final String ERROR_STATUS_CODE_ATTRIBUTE = "javax.servlet.error.status_code"; 3 // 异常类型 4 public static final String ERROR_EXCEPTION_TYPE_ATTRIBUTE = "javax.servlet.error.exception_type"; 5 // 错误消息 6 public static final String ERROR_MESSAGE_ATTRIBUTE = "javax.servlet.error.message"; 7 // 异常信息 8 public static final String ERROR_EXCEPTION_ATTRIBUTE = "javax.servlet.error.exception"; 9 // 处理失败的访问路径 10 public static final String ERROR_REQUEST_URI_ATTRIBUTE = "javax.servlet.error.request_uri"; 11 // servlet名称 12 public static final String ERROR_SERVLET_NAME_ATTRIBUTE = "javax.servlet.error.servlet_name"; 13 14 public static void clearErrorRequestAttributes(HttpServletRequest request) { 15 // 清除响应码 16 request.removeAttribute(ERROR_STATUS_CODE_ATTRIBUTE); 17 // 清除异常类型 18 request.removeAttribute(ERROR_EXCEPTION_TYPE_ATTRIBUTE); 19 // 清除错误消息 20 request.removeAttribute(ERROR_MESSAGE_ATTRIBUTE); 21 // 清除异常信息 22 request.removeAttribute(ERROR_EXCEPTION_ATTRIBUTE); 23 // 清除处理失败的访问路径 24 request.removeAttribute(ERROR_REQUEST_URI_ATTRIBUTE); 25 // 清除servlet名称 26 request.removeAttribute(ERROR_SERVLET_NAME_ATTRIBUTE); 27 }
4、渲染异常视图
渲染View视图,在源码(九):无异常View视图解析中已提及,此处不再赘述。