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对异常的处理

  • 相关阅读:
    iframe,table,window.open求救问题
    你的明星臉~~哈哈~~~(要附正面照片哦==)
    DataGrid的表頭排序問題(GridView雷同了啦)
    致歉(TO师傅)
    程式設計師的著裝(哈哈哈~~~)
    SQL(top与group by)求助
    MySql与超级终端
    hdu 1061 Rightmost Digit
    hdu 2669 Romantic
    poj 1061 青蛙的约会
  • 原文地址:https://www.cnblogs.com/question-sky/p/7240628.html
Copyright © 2011-2022 走看看