zoukankan      html  css  js  c++  java
  • SpringMVC源码情操陶冶-AbstractHandlerExceptionResolver

    springmvc支持服务端在处理业务逻辑过程中出现异常的时候可以配置相应的ModelAndView对象返回给客户端,本文介绍springmvc默认的几种HandlerExceptionResolver类

    实际应用

    springmvc的xml配置化-Exception配置

    <bean id="exceptionHandler" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    	<!--设置默认返回viewName,通常与freemarker引擎搭配使用-->
    	<property name="defaultErrorView" value="error/defaultError" />
    	<!--设置默认返回response status-->
    	<property name="defaultStatusCode" value="500" />
    	
    	<!--配置相应的异常类与viewName的映射-->
    	<property name="exceptionMappings">
            <props>
                <prop key="SessionTimeoutException">redirect:../login.html</prop>
    			<prop key="AuthenticationException">error/403</prop>
            </props>
    	</property>
    </bean>
    

    以上的配置会对出SessionTimeoutException异常则跳转至login页面,对AuthenticationException异常则跳转至403页面,对其他的异常则默认跳转至defaultError页面呈现并返回500的错误码

    HandlerExceptionResolver-异常解析接口

    接口内只有一个方法resolveException(),通过解析异常查询配置以得到符合条件的ModelAndView对象

    	/**
    	 * Try to resolve the given exception that got thrown during handler execution,
    	 * returning a {@link ModelAndView} that represents a specific error page if appropriate.
    	 * <p>The returned {@code ModelAndView} may be {@linkplain ModelAndView#isEmpty() empty}
    	 * to indicate that the exception has been resolved successfully but that no view
    	 * should be rendered, for instance by setting a status code.
    	 * @param request current HTTP request
    	 * @param response current HTTP response
    	 * @param handler the executed handler, or {@code null} if none chosen at the
    	 * time of the exception (for example, if multipart resolution failed)
    	 * @param ex the exception that got thrown during handler execution
    	 * @return a corresponding {@code ModelAndView} to forward to, or {@code null}
    	 * for default processing
    	 */
    	ModelAndView resolveException(
    			HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
    

    AbstractHandlerExceptionResolver-异常解析抽象基类

    所有的spring内置异常解析类都继承于此,直奔主题看resolveException()方法

    	@Override
    	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
    			Object handler, Exception ex) {
    		//判断是否需要解析
    		if (shouldApplyTo(request, handler)) {
    			//此处一般是判断内部属性preventResponseCaching是否为true,是则设置响应包头cache-control:no-store
    			prepareResponse(ex, response);
    			//使用模板方法doResolveException()方法供子类实现
    			ModelAndView result = doResolveException(request, response, handler, ex);
    			if (result != null) {
    				//日志打印一发
    				logException(ex, request);
    			}
    			return result;
    		}
    		else {
    			return null;
    		}
    	}
    

    附带着分析下shouldApplyTo()方法

    	/**
    	**可以配置mappedHandlers和mappedHandlerClasses属性来特定匹配
    	**默认情况下两者都为空则直接返回true,表明对所有的handler都进行异常解析
    	*/
    	protected boolean shouldApplyTo(HttpServletRequest request, Object handler) {
    		//此处的handler一般为bean对象
    		if (handler != null) {
    			if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
    				return true;
    			}
    			if (this.mappedHandlerClasses != null) {
    				for (Class<?> handlerClass : this.mappedHandlerClasses) {
    					if (handlerClass.isInstance(handler)) {
    						return true;
    					}
    				}
    			}
    		}
    		// Else only apply if there are no explicit handler mappings.
    		return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
    	}
    

    1. SimpleMappingExceptionResolver-异常映射实现类

    比较简单的实现类,可以配绑定viewName和exception以完成简单的异常映射视图页面

    	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
    			Object handler, Exception ex) {
    
    		// Expose ModelAndView for chosen error view.
    		String viewName = determineViewName(ex, request);
    		if (viewName != null) {
    			//如果配置了statusCodes属性,则对此异常的状态码进行设置
    			Integer statusCode = determineStatusCode(request, viewName);
    			if (statusCode != null) {
    				applyStatusCodeIfPossible(request, response, statusCode);
    			}
    			//创建ModelAndView对象
    			return getModelAndView(viewName, ex, request);
    		}
    		else {
    			return null;
    		}
    	}
    

    针对以上的源码我们分两步去简单分析下

    SimpleMappingExceptionResolver#determineViewName()-找寻viewName

    	protected String determineViewName(Exception ex, HttpServletRequest request) {
    		String viewName = null;
    		//判断异常是否属于excludeExceptions集合内,是则直接返回null
    		if (this.excludedExceptions != null) {
    			for (Class<?> excludedEx : this.excludedExceptions) {
    				if (excludedEx.equals(ex.getClass())) {
    					return null;
    				}
    			}
    		}
    		// Check for specific exception mappings.
    		// 从exceptionMappings集合内根据exception获取到相应的viewName
    		if (this.exceptionMappings != null) {
    			viewName = findMatchingViewName(this.exceptionMappings, ex);
    		}
    		//当exceptionMappings集合内不存在指定的exception但是默认视图指定则直接返回默认视图
    		if (viewName == null && this.defaultErrorView != null) {
    			viewName = this.defaultErrorView;
    		}
    		return viewName;
    	}
    
    • excludedExceptions集合可以过滤指定的exception,对其不进行解析直接返回null。可配置

    • exceptionMappings绑定了exception与viewName的关系,如果在其集合内没找到相应的viewName,但是defaultErrorView属性指定,则会直接返回defaultErrorView对应的视图

    SimpleMappingExceptionResolver#getModelAndView()-创建ModelAndView对象

    	protected ModelAndView getModelAndView(String viewName, Exception ex) {
    		ModelAndView mv = new ModelAndView(viewName);
    		//exceptionAttribute默认为exception
    		if (this.exceptionAttribute != null) {
    			//将exception信息添加到model中
    			mv.addObject(this.exceptionAttribute, ex);
    		}
    		return mv;
    	}
    

    主要就是将exceptionAttribute对应的参数值默认为exception属性添加到视图对象的model中

    2. ResponseStatusExceptionResolver-响应状态异常解析类

    主要是解析带有@ResponseStatus的异常类,将其中的异常信息描述直接返回给客户端

    	@Override
    	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
    			Object handler, Exception ex) {
    		//获取相应类上的注解@ResponseStatus
    		ResponseStatus responseStatus = AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);
    		if (responseStatus != null) {
    			try {
    				return resolveResponseStatus(responseStatus, request, response, handler, ex);
    			}
    			catch (Exception resolveEx) {
    				logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx);
    			}
    		}
    		else if (ex.getCause() instanceof Exception) {
    			ex = (Exception) ex.getCause();
    			//递归
    			return doResolveException(request, response, handler, ex);
    		}
    		return null;
    	}
    

    ResponseStatusExceptionResolver#resolveResponseStatus()-返回异常信息给客户端

    读取@ResponseStatus注解信息,返回异常内容给客户端

    	protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
    			HttpServletResponse response, Object handler, Exception ex) throws Exception {
    		//状态码
    		int statusCode = responseStatus.code().value();
    		//异常原因描述
    		String reason = responseStatus.reason();
    		if (this.messageSource != null) {
    			reason = this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale());
    		}
    		//通过response对象直接返回错误信息给客户端
    		if (!StringUtils.hasLength(reason)) {
    			response.sendError(statusCode);
    		}
    		else {
    			//通过response对象直接返回错误信息给客户端
    			response.sendError(statusCode, reason);
    		}
    		return new ModelAndView();
    	}
    

    3. DefaultHandlerExceptionResolver-springmvc默认的异常解析处理

    源码就不公布了,读者可自行去查询,基本都是调用response的sendError()方法返回错误信息给客户端。本文对其中的异常归下类

    1. 请求方式异常
    • HttpRequestMethodNotSupportedException-服务端不支持相应的请求方法
    • HttpMediaTypeNotSupportedException/HttpMediaTypeNotAcceptableException-服务端/客户端不支持相应的mediaType,比如application/json
    • MissingPathVariableException-@PathVaribale指定参数请求中不包含
    • MissingServletRequestParameterException/ServletRequestBindingException-请求参数绑定错误
    • MethodArgumentNotValidException-@Valid注解指定的参数校验失败
    • AsyncRequestTimeoutException-异步请求超时
    1. 消息内容异常
    • ConversionNotSupportedException-服务端找寻不到相应的Convert对象来解析javabean
    • TypeMismatchException-设置javabean属性类型出错
    • HttpMessageNotReadableException/HttpMessageNotWritableException-消息内容不可读/不可写
    • MissingServletRequestPartException-文件上传类错误,可能请求没有multipart/form-data或者服务不支持文件上传
    • NoHandlerFoundException-handler处理器没有找到,即可能没有对应的请求处理供响应

    4. ExceptionHandlerExceptionResolver-处理handlerMethod对象过程中的异常

    具体的逻辑本文则不展开了,简述下其中的逻辑:
    当处理handlerMethod业务逻辑过程中出现了异常,则此解析器

    1. 尝试从handlerMethod所在的class类去找寻是否含有@ExceptionHandler注解的方法

    2. 判断@ExceptionHandler指定的exception类与产生的异常一致,一致则执行相应的方法,当有多个@ExceptionHandler(value),则默认采用第一个

    3. 当上述在class类找寻不到则尝试判断class类是否含有@ControllerAdvice注解,有则按照上述第一二步的步骤再次找寻@ControllerAdvice指定的类

    小结

    springmvc开放了对异常也可以包装成页面显示的功能,通过本文的简单分析可以帮助博主和读者更好的理解springmvc对异常的处理

  • 相关阅读:
    LeetCode 623. Add One Row to Tree
    LeetCode 894. All Possible Full Binary Trees
    LeetCode 988. Smallest String Starting From Leaf
    LeetCode 979. Distribute Coins in Binary Tree
    LeetCode 814. Binary Tree Pruning
    LeetCode 951. Flip Equivalent Binary Trees
    LeetCode 426. Convert Binary Search Tree to Sorted Doubly Linked List
    LeetCode 889. Construct Binary Tree from Preorder and Postorder Traversal
    LeetCode 687. Longest Univalue Path
    LeetCode 428. Serialize and Deserialize N-ary Tree
  • 原文地址:https://www.cnblogs.com/question-sky/p/7240628.html
Copyright © 2011-2022 走看看