目录
2、HandlerExceptionResolver-处理全局异常
3、SimpleMappingExceptionResolver-处理全局异常
4、使用@ControllerAdvice+@ExceptionHandler
在 Spring MVC 应用的开发中,不管是操作底层数据库,还是业务层或控制层,都会不可避免地遇到各种可预知的、不可预知的异常。我们需要捕捉处理异常,才能保证程序不被终止。
Spring MVC 有以下 3 种处理异常的方式:
- 使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver。
- 实现 Spring 的异常处理接口 HandlerExceptionResolver,自定义自己的异常处理器。
- 使用 @ExceptionHandler 注解实现异常处理
在springmvc中,异常处理的思路
上图所示,系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。
springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。明白了springmvc中的异常处理机制,下面就开始分析springmvc中的异常处理。
1、@ExceptionHandler-处理局部异常
局部异常处理仅能处理指定 Controller 中的异常。
下面使用 @ExceptionHandler 注解实现。//注意:该注解不是加在产生异常的方法上,而是加在处理异常的方法上。
定义一个处理过程中可能会存在异常情况的 submit 方法,当 i=0 时会产生算术运算异常,在同一个类中定义处理异常的方法controllerExceptionHandler,捕获运算异常。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author swadian
* @date 2022/2/21
* @Version 1.0
*/
@Controller
@RequestMapping
public class ExceptionController {
@RequestMapping("/submit") // 抛错方法
public String submit(HttpServletRequest req,
HttpServletResponse resp) throws Exception {
String num = req.getParameter("num");
System.out.println(10 / Integer.valueOf(num));
return "success";
}
@ExceptionHandler({ArithmeticException.class}) //捕获运算异常
public String controllerExceptionHandler(Exception e) {
System.out.println("打印错误信息 ===> ArithmeticException:" + e);
// 跳转到指定页面
return "error";
}
}
异常页面 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>错误页面</title>
</head>
<body>
发生算术运算异常,请重新输入数据!
</body>
</html>
工程中web.xml配置如下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"
metadata-complete="true">
<!--配置SpringMvc的核心控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置DispatcherServlet的初始化參數:设置文件的路径和文件名称 -->
<init-param>
<param-name>contextConfigLocation</param-name> <!--名称不能动,DispatcherServlet固定读取key-->
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 配置 servlet 的对象的创建时间点:1-应用加载时创建。取值只能是非 0 正整数,表示启动顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern> <!--拦截所有请求,.do/.action也可以,/方便支持restful风格-->
</servlet-mapping>
</web-app>
工程中springmvc-servlet.xml配置如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 启用注解,使用了<context:component-scan/> 后就可以将该配置移除。 -->
<!--<context:annotation-config />-->
<mvc:annotation-driven/>
<!-- 设置使用注解的类所在的包 -->
<context:component-scan base-package="com.springmvc.*"></context:component-scan>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置视图的前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--配置视图的后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
整个项目的路径如下:
启动项目,访问submit方法,程序会进入到错误处理方法,最终跳转到错误页面
@ExceptionHandler 注解定义的方法优先级问题:例如发生的是 NullPointerException,但是声明的异常有 RuntimeException 和 Exception,这时候会根据异常的最近继承关系找到继承深度最浅的那个@ExceptionHandler 注解方法,即标记了 RuntimeException 的方法。
被 @ExceptionHandler 标记为异常处理方法,不能在方法中设置别的形参。但是可以使用 ModelAndView 向前台传递数据。
使用局部异常处理,仅能处理某个 Controller 中的异常,若需要对所有异常进行统一处理,可使用以下两种方法。
2、HandlerExceptionResolver-处理全局异常
Spring MVC 通过 HandlerExceptionResolver 处理程序异常,包括处理器异常、数据绑定异常以及控制器执行时发生的异常。HandlerExceptionResolver 仅有一个接口方法,源码如下。
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest var1,
HttpServletResponse var2,
Object var3,
Exception var4);
}
发生异常时,Spring MVC 会调用 resolveException() 方法,并转到 ModelAndView 对应的视图中,返回一个异常报告页面反馈给用户。
创建一个 HandlerExceptionResolver 接口的实现类 MyExceptionHandler,代码如下。
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @author swadian
* @date 2022/2/21
* @Version 1.0
*/
@Component
public class MyExceptionHandler implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o,
Exception e) {
Map<String, Object> model = new HashMap<String, Object>();
model.put("errorMessage", "程序运行出错");
//根据不同错误转向不同页面(统一处理),即异常与View的对应关系
if (e instanceof ArithmeticException) {
return new ModelAndView("error", model);
}
return new ModelAndView("other_error", model);
}
}
添加新的错误页面other_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>系统正忙页面</title>
</head>
<body>
errorMessage:${errorMessage} <br />
系统正忙,请稍后再试!
</body>
</html>
更改ExceptionController类,新增空指针异常
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author swadian
* @date 2022/2/21
* @Version 1.0
*/
@Controller
@RequestMapping
public class ExceptionController {
@RequestMapping("/submit") // 抛错方法
public String submit(HttpServletRequest req,
HttpServletResponse resp) throws Exception {
String num = req.getParameter("num");
System.out.println("输入的参数长度为:" + num.length());//空指针异常
System.out.println(10 / Integer.valueOf(num));
return "success";
}
}
启动项目,访问submit方法,不输入参数,程序会进入到错误处理方法,最终跳转到其他错误页面
3、SimpleMappingExceptionResolver-处理全局异常
全局异常处理可使用 SimpleMappingExceptionResolver 来实现。它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。
在 springmvc-servlet.xml 中配置全局异常,代码如下。
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 定义默认的异常处理页面,当该异常类型注册时使用 -->
<property name="defaultErrorView" value="other_error"></property>
<!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
<property name="exceptionAttribute" value="errorMessage"></property>
<!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常页名作为值 -->
<property name="exceptionMappings">
<props>
<prop key="ArithmeticException">error</prop>
<!-- 在这里还可以继续扩展对不同异常类型的处理 -->
</props>
</property>
</bean>
启动项目,访问submit方法,不输入参数,程序会进入到错误处理方法,最终跳转到其他错误页面
4、使用@ControllerAdvice+@ExceptionHandler
注意该方式需要开启注解支持
<!-- 启用注解,使用了<context:component-scan/> 后就可以将该配置移除。 -->
<!--<context:annotation-config />-->
<mvc:annotation-driven/>
<!-- 设置使用注解的类所在的包 -->
<context:component-scan base-package="com.springmvc.*"></context:component-scan>
全局异常处理类:MyExceptionHandler.java
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @author swadian
* @date 2022/2/21
* @Version 1.0
*/
@ControllerAdvice
public class MyExceptionHandler {
/**
* 处理异常:捕获数据运算异常
*
* @param e
* @return
*/
@ExceptionHandler({ArithmeticException.class}) //捕获所有异常 Exception.class
@ResponseBody //返回字符串而非视图
@ResponseStatus(HttpStatus.OK)
public String arithmeticExceptionHandler(Exception e) {
System.out.println("打印错误信息 ===> ArithmeticException:" + e);
//返回错误信息
return "错误信息:" + e.getMessage();
}
}
测试类
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author swadian
* @date 2022/2/21
* @Version 1.0
*/
@RestController
@RequestMapping
public class SubmitController {
//计算
@RequestMapping("/submit")
public String submit(HttpServletRequest request, HttpServletResponse response) throws Exception {
String num = request.getParameter("num");
Integer i = 10 / Integer.valueOf(num);
return "计算成功,结果为:" + i;
}
}
启动程序,参数输入为0,得到如下处理结果:
上文说到 @ ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个 Controller里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个controller 中了。
这也是 Spring 3.2 带来的新特性。从名字上可以看出大体意思是控制器增强。
也就是说,@controlleradvice + @ ExceptionHandler 也可以实现全局的异常捕捉。 请确保此WebExceptionHandle 类能被扫描到并装载进 Spring 容器中。
若在其他的由@Controller标记的Handler类中的Handle方法抛出异常,且没有在Handler类中定义@ExceptionHandler方法,则会去由@ControllerAdvice标记的类中去找,若也找不到,则在页面抛出异常。
问题:Tomcat方式启动和Application方式启动有什么区别呢?