Spring异常处理——详解
源码文件链接在最后
背景
一个较为常见的系统,会涉及控制层,服务(业务)层、缓存层、存储层以及接口调用等,其中每一个环节都不可避免的会遇到各种不可预知的异常需要处理。如果每个步骤都单独try…catch会使系统显的很杂乱,可读性差,维护成本高;常见的方式就是,实现统一的异常处理,从而将各类异常从各个模块中解耦出来;
测试案例的制作
前端error.jsp代码
文件名:/springmvc/src/main/webapp/views/error.jsp
代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div align="center">
<h2>ERROR PAGE</h2>
<div>
errorINof:${exception }
</div>
</div>
</body>
</html>
先来个简单的数组用来测试今天的异常处理
文件名:/springmvc/src/main/java/init/wuji/springboot/mvc/exception/action/SecondExceptionController.java
代码
package init.wuji.springboot.mvc.exception.action;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class SecondExceptionController {
@RequestMapping("/sectstException")
public String sectstException(int nameIndex) {
String[] userNames = {"张三", "Lisi", "小黑"};
System.out.println("hello Exception! userName:" + userNames[nameIndex]);
return "success";
}
}
那个视图解析器就不说
别return success看不懂
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
配置在了上下文
也就是我的error文件在views/error.jsp
1、在上下文在配置异常处理(较常见)
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionAttribute" value="myException"></property>
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
exceptionAttribute是exception用来前端接收的名字,源码中默认为exception,如果要修改就定义成别的名字
2、@ControllerAdvice全局作用(较常见)
这里虽说是ControllerAdvice注解,其实是其与ExceptionHandler的组合使用。在上文中可以看到,单独使用@ExceptionHandler时,其必须在一个Controller中,然而当其与ControllerAdvice组合使用时就完全没有了这个限制。换句话说,二者的组合达到的全局的异常捕获处理。
文件名:/springmvc/src/main/java/init/wuji/springboot/mvc/exception/action/MyExceptionHandler.java
代码:
package init.wuji.springboot.mvc.exception.action;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
public class MyExceptionHandler{
@ExceptionHandler(Throwable.class)
public ModelAndView handleException(Throwable e) {
ModelAndView mav = new ModelAndView("error");
System.out.println("=======MyhandleException=========");
mav.addObject("exception", e);
return mav;
}
}
运行结果
一般来说全局注解比较常见使用
3、注解ExceptionHandler(局部)
注解ExceptionHandler作用对象为方法,最简单的使用方法就是放在controller文件中,详细的注解定义不再介绍。如果项目中有多个controller文件,通常可以在baseController中实现ExceptionHandler的异常处理,而各个contoller继承basecontroller从而达到统一异常处理的目的。因为比较常见,简单代码如下:
文件名:/springmvc/src/main/java/init/wuji/springboot/mvc/exception/action/TestExceptionController.java
代码:
package init.wuji.springboot.mvc.exception.action;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestExceptionController {
@RequestMapping("/tstException")
public String tstException(int nameIndex) {
String[] userNames = {"张三", "Lisi", "小黑"};
System.out.println("hello Exception! userName:" + userNames[nameIndex]);
return "success";
}
/**
*
* 在使用@ExceptionHandler进行异常处理时,不可以将exception直接设置到方法参数中声明的modle, 必须返回ModelAndView
*
* @param e
* @param map
* @return
*/
@ExceptionHandler
public ModelAndView handleException(Exception e) {
ModelAndView mav = new ModelAndView("error");
System.out.println("=======handleException=========");
mav.addObject("exception", e);
return mav;
}
}
运行结果
=======handleException=========
17:06:20.458 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Resolved [java.lang.ArrayIndexOutOfBoundsException: 5] to ModelAndView [view="error"; model={exception=java.lang.ArrayIndexOutOfBoundsException: 5}]
17:06:20.458 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.DispatcherServlet - Using resolved error view: ModelAndView [view="error"; model={exception=java.lang.ArrayIndexOutOfBoundsException: 5}]
17:06:20.459 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.view.JstlView - View name 'error', model {exception=java.lang.ArrayIndexOutOfBoundsException: 5}
17:06:20.459 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.view.JstlView - Forwarding to [/views/error.jsp]
17:06:20.461 [http-nio-8080-exec-11] DEBUG org.springframework.web.servlet.DispatcherServlet - Completed 200 OK
4、接口
实现HandlerExceptionResolver接口
HandlerExceptionResolver本身SpringMVC内部的接口,其内部只有resolveException一个方法,通过实现该接口我们可以达到全局异常处理的目的。
只需要将该Bean加入到Spring容器,可以通过Xml配置,也可以通过注解方式加入容器。
总结(异常处理顺序)
三种都讲完了,下面说一下,当有多个exception时的运行机制,
通过实践可知,单独@ExceptionHandle异常处理排在了首位,@ControllerAdvice排在了第二位,也就是优先选择离异常近的