SpringMVC框架 |异常处理


一、异常解析的源码流程

SpringMVC通过HandlerExceptionResolver处理程序的异常,包括Handler映射、数据绑定,以及目标方法执行时发生的异常。
在这里插入图片描述
SpringMVC默认使用下面三个ExceptionResolver:

HandlerExceptionResolver=
	AnnotationMethodHandlerExceptionResolver,
	ResponseStatusExceptionResolver,
	DefaultHandlerExceptionResolver

但是自从SpringMVC中配置了<mvc:annotation-driven></mvc:annotation-driven>以后,AnnotationMethodHandlerExceptionResolver被替换为ExceptionHandlerExceptionResolver。所以SpringMVC中默认的就变成了下面三个异常处理器:

  • ①ExceptionHandlerExceptionResolver:处理@ExceptionHandler注解
  • ②ResponseStatusExceptionResolver:处理@ResponseStatus注解
  • ③DefaultHandlerExceptionResolver:判断 是否是SpringMVC自带的异常

页面渲染之前,有异常会先渲染异常

  • 如果上面三个异常解析器都无法处理,会向上抛给tomcat
  • 处理异常内部的默认工作流程:所有异常解析器依次尝试解析,解析完成进行后续操作,解析失败,下一个解析器继续尝试解析。
源码:
public class DispatcherServlet extends FrameworkServlet {
	...
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

		boolean errorView = false;

【如果有异常】if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
【处理异常】 	mv = processHandlerException(request, response, handler, exception);//如果三个异常解析器都无法处理,会向上抛给tomcat。
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
【页面渲染】:render(mv, request, response);//来到页面
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
						"': assuming HandlerAdapter completed request handling");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}
	...
}

二、@ExceptionHandler返回一个自定义错误页面

@ExceptionHandler标注在方法上,告诉SpringMVC这个方法专门处理这个类发生的异常。

  • 方法上写一个Exception用来接收发生的异常。
  • 要携带异常信息不能给参数位置写Model,正确的做法是返回ModelAndView。
  • 如果有多个@ExceptionHandler都能处理这个异常,精确优先
	@ExceptionHandler(value = { ArithmeticException.class, NullPointerException.class }) // 告诉SpringMVC,这个方法专门处理这个类发送的所有异常
	public ModelAndView handleException01(Exception exception) {
		System.out.println("handleException01..." + exception);
		ModelAndView view = new ModelAndView("myerror");
		view.addObject("ex", exception);	
		return view;
	}

前端页面直接接受隐含模型

myerror.jsp页面
<body>
	<h1>出错啦!</h1>
	<h2>出错信息:${ex }</h2>
</body>

当出现除0算数异常时,提示如下:
在这里插入图片描述

统一异常管理

上面的测试是将@ExceptionHandler放在了处理器中,实际上更好的方式是将@ExceptionHandler放在一个单独的类中,进行全局异常处理

  • 统一异常管理类需要通过@ControllerAdvice注解加入IoC容器中。
  • 全局异常处理与本类异常处理同时存在,本类优先
@ControllerAdvice
public class MyException {
	// 处理空指针异常
	@ExceptionHandler(value = { NullPointerException.class })
	public ModelAndView handleException01(Exception exception) {
		System.out.println("全局的handleException01..." + exception);
		ModelAndView view = new ModelAndView("myerror");
		view.addObject("ex", exception);
		return view;
	}

	// 处理算数异常
	@ExceptionHandler(value = { ArithmeticException.class })
	public ModelAndView handleException02(Exception exception) {
		System.out.println("全局的handleException02..." + exception);
		ModelAndView view = new ModelAndView("myerror");
		view.addObject("ex", exception);
		return view;
	}
}

三、@ResponseStatus返回一个服务器错误页面

@ResponseStatus标注在自定义异常上.

@ResponseStatus(reason = "用户被拒绝登录", value = HttpStatus.NOT_ACCEPTABLE)
public class UsernameNotFoundException extends RuntimeException {

	private static final long serialVersionUID = 1L;
}

处理器中,抛出自定义UsernameNotFoundException 异常。

	@RequestMapping("/handle02")
	public String handle02(@RequestParam("username") String username) {
		if (!"admin".equals(username)) {
			System.out.println("登录失败");
			throw new UsernameNotFoundException();
		}
		System.out.println("登录成功");
		return "success";
	}

发送username=admin02的请求:

<a href="${ctp }/handle02?username=admin02">handl01</a>

返回到服务器错误页面:
在这里插入图片描述

四、DefaultHandlerExceptionResolver默认异常

DefaultHandlerExceptionResolver判断是否是SpringMVC自带的异常。

例如定义如下的POST处理请求,通过GET进行访问,会出错。

	@RequestMapping(value = "/handle03", method = RequestMethod.POST)
	public String handle03() {
		return "success";
	}

SpringMVC自己的异常,如HttpRequestMethodNotSupportedException。若没有处理,会进入到服务器错误页面。
在这里插入图片描述

默认的异常

源码:
public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
	...
	@Override
	protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
			Object handler, Exception ex) {

		try {
			if (ex instanceof NoSuchRequestHandlingMethodException) {
				return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,
						handler);
			}
			else if (ex instanceof HttpRequestMethodNotSupportedException) {
				return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
						response, handler);
			}
			else if (ex instanceof HttpMediaTypeNotSupportedException) {
				return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,
						handler);
			}
			else if (ex instanceof HttpMediaTypeNotAcceptableException) {
				return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,
						handler);
			}
			else if (ex instanceof MissingPathVariableException) {
				return handleMissingPathVariable((MissingPathVariableException) ex, request,
						response, handler);
			}
			else if (ex instanceof MissingServletRequestParameterException) {
				return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,
						response, handler);
			}
			else if (ex instanceof ServletRequestBindingException) {
				return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response,
						handler);
			}
			else if (ex instanceof ConversionNotSupportedException) {
				return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler);
			}
			else if (ex instanceof TypeMismatchException) {
				return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMessageNotReadableException) {
				return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);
			}
			else if (ex instanceof HttpMessageNotWritableException) {
				return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);
			}
			else if (ex instanceof MethodArgumentNotValidException) {
				return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response,
						handler);
			}
			else if (ex instanceof MissingServletRequestPartException) {
				return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request,
						response, handler);
			}
			else if (ex instanceof BindException) {
				return handleBindException((BindException) ex, request, response, handler);
			}
			else if (ex instanceof NoHandlerFoundException) {
				return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
			}
		}
		catch (Exception handlerException) {
			if (logger.isWarnEnabled()) {
				logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
			}
		}
		return null;
	}
	...
}

五、基于配置的异常处理

SimpleMappingExceptionResolver:通过配置的方式完成异常处理。
在这里插入图片描述

  • exceptionMappings属性:配置不同的异常去不同的页面。
  • key:异常全类名。
  • value:表示要去的视图页面。
  • exceptionAttribute属性:指定错误信息取出时使用的key,默认为exception;可以通过这个key在前端获得异常信息。
	<bean
		class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
		<property name="exceptionMappings">
			<props>
				<prop key="java.lang.NullPointerException">myerror</prop>
			</props>
		</property>
		<!-- 指定错误信息取出时使用的key,默认为exception -->
		<property name="exceptionAttribute" value="ex"></property>
	</bean>

前端显示页面

myerror.jsp
<body>
	<h1>出错啦!</h1>
	<h2>出错信息:${ex }</h2>
</body>

通过配置的异常处理,出现空指针异常时,在回显页面打印出了信息。
在这里插入图片描述

发布了461 篇原创文章 · 获赞 1531 · 访问量 53万+

猜你喜欢

转载自blog.csdn.net/weixin_43691058/article/details/105738233
今日推荐