springmvc中级-异常处理

一、先构建一个普通程序

1、测试页面:

<a href="testExceptionHandlerExceptionResolver?i=10">测试异常</a>

2、编辑控制器:

@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){
	System.out.println("result: " + (10 / i));
	return "success";
}

3、结果成功页面,就显示成功两字,很简单就不写了

二、编辑异常捕获

1、编辑捕获异常的代码

//这个是 之前已经写好的 控制器代码
@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){
	System.out.println("result: " + (10 / i));
	return "success";
}

/**
 * 1. 在 @ExceptionHandler 方法的入参中可以加入 Exception 类型的参数, 该参数即对应发生的异常对象
 * 2. @ExceptionHandler 方法的入参中不能传入 Map. 若希望把异常信息传导页面上, 需要使用 ModelAndView 作为返回值
 * 3. @ExceptionHandler 方法标记的异常有优先级的问题. 
 * 4. @ControllerAdvice: 如果在当前 Handler 中找不到 @ExceptionHandler 方法来出来当前方法出现的异常, 
 * 则将去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常. 
 */
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
	System.out.println("@ExceptionHandler+ArithmeticException.class+出异常了: " + ex);
	ModelAndView mv = new ModelAndView("error");
	mv.addObject("exception", ex);
	return mv;
}
	
@ExceptionHandler({RuntimeException.class})
public ModelAndView handleArithmeticException2(Exception ex){
	System.out.println("@ExceptionHandler+RuntimeException.class+ [出异常了]: " + ex);
	ModelAndView mv = new ModelAndView("error");
	mv.addObject("exception", ex);
	return mv;
}

注意:上面两个异常捕获器,精度不一样,实际使用时,抛出的异常按精度来捕获。【还有,异常捕获器中不能在方法中添加map 形参,这和普通的控制器不同,所以保存信息,必须先新建ModelAndView,然后保存并返回】

上面两个异常捕获器,作用域在这个控制器的类文件中,如果想要作用于所有的控制器类文件的话,需要新建一个异常捕获器类。

//因为是注解,所有这个全局异常捕获器,需要放在 注解扫描器 能够扫描到的目录
//在转发配置中,我们可以看到 <!-- 配置自动扫描的包 -->
//<context:component-scan base-package="com.test.springmvc"></context:component-scan>


@ControllerAdvice
public class SpringMVCTestExceptionHandler {

	@ExceptionHandler({RuntimeException.class})
	public ModelAndView handleArithmeticException(Exception ex){
		System.out.println("全局+@ControllerAdvice+@ExceptionHandler+ArithmeticException.class+----> 出异常了: " + ex);
		ModelAndView mv = new ModelAndView("error");
		mv.addObject("exception", ex);
		return mv;
	}
	
}

2、返回结果失败页面,就显示失败两字,很简单就不写了。

核心注意:上面提到的属于同一层的异常捕获,出现异常时,只能先内部捕获后全局捕获,且按照精确度优先原则。这一层的异常捕获,只要有一个捕获了,该层的其他捕获器就不捕获了。

三、产生异常测试

除数为0,抛出异常。

<a href="testExceptionHandlerExceptionResolver?i=0">测试异常</a>

四、@ResponseStatus注解

直接在异常页,显示编辑过的响应头信息。
1、可以注解一个类或一个方法,当注解为一个类时,该异常作用于所有控制器。

@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用户名和密码不匹配!")
public class UserNameNotMatchPasswordException extends RuntimeException{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public  UserNameNotMatchPasswordException() {
		System.out.println("全局+@ResponseStatus+RuntimeException+ArithmeticException.class+----> 出异常了: ");
	}
	
}

2、也可以直接注解在控制器方法上。此时,控制器无论是否抛出异常,都会按照设置的异常信息,展示异常网页。(也就是无论是否有异常,控制器自己都会捕获异常,并展示@ResponseStatus设置好的异常信息)

@ResponseStatus(reason="测试",value=HttpStatus.NOT_FOUND)
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
	if(i == 13){
		System.out.println("throw +testResponseStatusExceptionResolver...");
		throw new UserNameNotMatchPasswordException();
	}
	System.out.println("testResponseStatusExceptionResolver...");
		
	return "success";
}

注意:@ResponseStatus注解

1、如果控制器没有抛出异常,也不会跳转到success页面,而是用tomcat默认的异常页面,例如:  HTTP Status 404 – 测试

2、如果出现异常,且有@ExceptionHandler, @ControllerAdvice  异常捕获类,那么异常会被二次捕获了。【先ResponseStatus捕获,然后ExceptionHandler又捕获,这两种捕获器属于两个层次】

[FirstIntercept] preHandle
throw +testResponseStatusExceptionResolver...
全局+@ResponseStatus+RuntimeException+ArithmeticException.class+----> 出异常了: 
@ExceptionHandler+RuntimeException.class+ [出异常了]: com.test.springmvc.test.UserNameNotMatchPasswordException
[FirstIntercept] afterCompletion

3、如果出现异常,且没有@ExceptionHandler, @ControllerAdvice  异常捕获类,那么就由ResponseStatus 捕获一次就结束了。

五、DefaultHandlerExceptionResolver

这个就是springmvc默认的异常处理,比如我们控制器编写错误,出异常了,如果我们写异常处理模块,那么默认情况下就是这个类处理的。比如控制器只支持post请求,然后用get请求就报错,请求方法不支持。

目前测试到的异常情况看,默认异常处理器,捕获到异常后,不会将异常传递给其他异常捕获器了。

直接在网页中显示错误:

HTTP Status 405 - Request method 'GET' not supported

type Status report

message Request method 'GET' not supported

description The specified HTTP method is not allowed for the requested resource.

Apache Tomcat/8.0.36

控制台有错误信息:

org.springframework.web.servlet.PageNotFound handleHttpRequestMethodNotSupported
WARNING: Request method 'GET' not supported

六、SimpleMappingExceptionResolveer

在控制器中:

@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver(@RequestParam("i") int i){
	String [] vals = new String[10];
	System.out.println(vals[i]);
	return "success";
}

测试网页:

http://localhost:8080/springmvc-2/testSimpleMappingExceptionResolver?i=10

编辑转发配置器:

<!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
	<!-- 配置请求域 异常属性名 ex,默认值为 exception -->
	<property name="exceptionAttribute" value="ex"></property>
	<!-- 异常类型 映射 跳转页  -->
	<property name="exceptionMappings">
		<props>
			<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
		</props>
	</property>
</bean>

七、异常处理器的优先级

同一个异常:

DefaultHandlerExceptionResolver (优先级第一,捕获到异常后,就将异常截断,在网页中展示异常信息,不会再往下传递)

@ResponseStatus(优先级第二,捕获到异常后,会将异常往后传递,返回结果以最后一个异常返回网页信息为准)

在控制器中,添加这个@ResponseStatus注解,就算没有抛出异常,也会展示指定状态的异常的网页。

@ResponseStatus(reason="测试",value=HttpStatus.NOT_FOUND)
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
	if(i == 13){
		System.out.println("throw +testResponseStatusExceptionResolver...");
		throw new UserNameNotMatchPasswordException();
	}
	System.out.println("testResponseStatusExceptionResolver...");
		
	return "success";
}

如果上面的控制器没有@ResponseStatus(reason="测试",value=HttpStatus.NOT_FOUND) ,然后异常时抛出了UserNameNotMatchPasswordException类

对UserNameNotMatchPasswordException查看编码信息:

@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用户名和密码不匹配!")
public class UserNameNotMatchPasswordException extends RuntimeException{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public  UserNameNotMatchPasswordException() {
		System.out.println("全局+@ResponseStatus+RuntimeException+ArithmeticException.class+----> 出异常了: ");
	}
	
}

看上面的异常类,如果项目中没有低优先级的异常捕获器,那么网页就返回错误信息就结束了。如果有其他低优先级的异常了,那么异常还会被继续处理。

@ExceptionHandler和@ControllerAdvice(优先级第三,异常被捕获后,不会继续往下传递了)

@ExceptionHandler适用于和控制器位于同一个文件中,@ControllerAdvice单独一个文件,作用域针对所有的控制器。

SimpleMappingExceptionResolver(异常映射器,优先级最低,针对异常的类型,指定错误和异常的返回页)

<!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
	<!-- 配置请求域 异常属性名 ex,默认值为 exception -->
	<property name="exceptionAttribute" value="ex"></property>
	<!-- 异常类型 映射 跳转页  -->
	<property name="exceptionMappings">
		<props>
			<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
		</props>
	</property>
</bean>

补充:

针对指定的异常信息返回页,可以在返回页的网页中,直接读取异常信息。
默认jstl 表达式为:  ${requestScope.exception},在SimpleMappingExceptionResolver 中信息配置时,将异常的属性名改成了ex,所以错误信息展示页 该调用 ${requestScope.ex}来获取异常信息。

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments