我们知道,系统中异常包括:编译时异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。在开发中,不管是dao层、service层还是controller层,都有可能抛出异常,在springmvc中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护。

一、异常处理思路

 

SpringMVC的学习(六)——SpringMVC中的统一异常处理

如上图所示,系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。明白了springmvc中的异常处理机制,下面就开始分析springmvc中的异常处理。

二、SpringMVC异常处理

Spring MVC通过HandlerExceptionResolver处理程序的异常,包括处理映射,数据绑定及处理器执行时发生异常。HandlerExceptionResolver仅有一个接口方法:

  ModelAndView resolveException(HttpServletRequest reqeust,HttpServletResponse response,Object handler,Exception ex);

当发生异常时,Spring MVC将调用 resolveException()方法,并转到ModelAndView对应视图中,作为一个异常报告页面,反馈给用户!

HandlerExceptionResolver拥有4个实现类:

- DefaultHandlerExceptionResolver

- SimpleMappingExceptionResolver

- AnnotationMethodHandlerExceptionResolver

- ResponseStatusExceptionResolver

三、异常处理方案

①DefaultHandlerExceptionResolver

Spring MVC默认装配了DefaultHandlerExceptionResolver,它会将Spring MVC框架的异常转换为相应的相应状态码!

异常和相应状态码对应表:

SpringMVC的学习(六)——SpringMVC中的统一异常处理

在web.xml响应状态码配置一个对应页面

<error-page>
	<error>404</error>
	<location>/static/404.jsp</location>
</error-page>

注意: 静态资源注意会被DispatcherServlet拦截!

②SimpleMappingExceptionResolver

如果希望对所有的异常进行统一的处理,比如当指定的异常发生时,把它映射到要显示的错误的网页中,此时用SimpleMappingExceptionResolver进行解析。DispatcherServlet中没有实现SimpleMappingExceptionResolver的Bean,所以需要在springmvc的配置文件中进行配置。示例如下:

@Controller
public class DemoServlet2 {

	@RequestMapping("/testSimpleMappingExceptionResolver")
	public String testSimpleMappingExceptionResolver() {
		String[] values = new String[10];
		// 下标越界了
		System.out.println(values[11]);
		return "success";
	}
}

发送index.jsp中的超链接请求,控制器捕获请求后处理控制器逻辑,由于在逻辑中,数组越界,会抛出ArrayIndexOutOfBoundsException异常。

处理异常

<mvc:annotation-driven />  <!--注解驱动 -->
	<!-- 配置使用SimpleMappingExceptionResolver来映射异常 -->
	<bean
	class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
		<!-- 给异常命名一个别名 -->
		<property name="exceptionAttribute" value="ex"></property>
		<property name="exceptionMappings">
			<props>
				<!-- 一定要异常的全类名。 表示出现ArrayIndexOutOfBoundsException异常,就跳转到error.jsp视图 -->
				<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
			</props>
		</property>
	</bean>

另外在/WEB-INF/jsp下新建一个error.jsp视图。因为上面配置的InternalResourceViewResolver视图解析器默认把error字符串解析为error.jsp视图。error.jsp内容为:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>Error Page</h1>
    ${requestScope.ex}
</body>
</html>

下面重新发送index.jsp中的超链接请求后,控制器截获请求并处理请求时,数组越界抛出一个ArrayIndexOutOfBoundsException一个异常,此时由SimpleMappingExceptionResolver异常解析!

SpringMVC的学习(六)——SpringMVC中的统一异常处理

③AnnotationMethodHandlerExceptionResolver

Spring MVC 默认注册了 AnnotationMethodHandlerExceptionResolver,它允许通过@ExceptionHandler注解指定处理特定异常的方法!

@Controller
public class DemoController1 {

	@ExceptionHandler(value = { RuntimeException.class })
	public ModelAndView handleArithmeticException2(Exception ex) {
		System.out.println("[出异常了]:" + ex);
		ModelAndView mv = new ModelAndView("error");
		mv.addObject("exception", ex);
		return mv;
	}

	@ExceptionHandler(value = { ArithmeticException.class })
	public ModelAndView handleArithmeticException(Exception ex) {
		System.out.println("出异常了,算术异常:" + ex);
		ModelAndView mv = new ModelAndView("error");
		mv.addObject("exception", ex);
		return mv;
	}

	@RequestMapping("/testExceptionHandler1")
	public String test1() {
		String s=null;
		System.out.println(s.length());
		return "success";
	}
	
	@RequestMapping("/testExceptionHandler2")
	public String test2() {
		int i=100/0;
		return "success";
	}
}

目标方法内抛出了一个ArithmeticException异常,将由继承关系最近的异常处理捕捉到,即由handleArithmeticException捕捉到。

若将handleArithmeticException方法注释掉,则发生ArithmeticException异常将由handleArithmeticException2进行处理。

缺点:

  • 使用该注解有一个不好的地方就是:进行异常处理的方法必须与出错的方法在同一个Controller里面。
  • 不能全局控制异常。每个类都要写一遍。

四、全局异常处理

上文说到 @ ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个Controller里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个 controller 中了。这也是 Spring 3.2 带来的新特性。从名字上可以看出大体意思是控制器增强。 也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。

请确保此WebExceptionHandle 类能被扫描到并装载进 Spring 容器中。

@Controller
@ControllerAdvice
public class WebExceptionHandle {

	@ExceptionHandler(Exception.class)
	public ModelAndView handleException(Exception ex) {
		System.out.println("全局异常:ex = " + ex);
		ModelAndView modelAndView = new ModelAndView();
		
		modelAndView.setViewName("error");
		modelAndView.addObject("exception", ex);
		return modelAndView;
	}
}

此处可以捕捉全局异常,但是不要忘了在spring配置的时候扫描该类!

若在其他的由@Controller标记的Handler类中的Handle方法抛出异常,且没有在Handler类中定义@ExceptionHandler方法,则会去由@ControllerAdvice标记的类中去找,若也找不到,则在页面抛出异常。

相关文章: