在 Spring MVC 应用的开发中,无论是持久层,还是业务层或控制层,都会不可避免地遇到各种异常。就需要捕捉处理异常,才能保证程序不被终止。
Spring MVC 有以下 4 种处理异常的方式:
- 使用 @ExceptionHandler 注解实现异常处理
- 使用:@ControllerAdvice+@ExceptionHandler
- 使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver。
- 实现 Spring 的异常处理接口 HandlerExceptionResolver,自定义自己的异常处理器。
一、 @ExceptionHandler(只能处理当前Controller中的异常)
局部异常处理仅能处理指定 Controller 中的异常。
1.下面使用 @ExceptionHandler 注解实现。在controller中创建定义ExceptionHandleController 定义两个会存在异常情况的方法testArithmeticException和testNullPointerException
package com.augus.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptionHandleController {
@RequestMapping("/testArithmeticException")
public String testArithmeticException() {
int i = 1 / 0;
return "success";
}
@RequestMapping("/testNullPointerException")
public String testNullPointerException(){
String s = null;
//这里会报空指针异常
System.out.println(s.length());
return "success";
}
}
2.在上述方法同一个类中定义处理异常的方法。
//vale后面分别写两个异常类型的字节码文件,表示当前模块中遇到这两类异常,都用下面的方法处理
@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
public String testHandelException(Exception e) {
System.out.println("异常信息为:"+e);
//跳转到指定页面
return "error";
}
注意:
- 这个注解是加在处理异常的方法上,不是加在产生异常的方法上
- 同时需要注意这个处理异常的方法作用域只在当前模块,换言之就是这个方法只能处理当前模块中产生的异常
3.异常页面 error.html 代码如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>异常处理</title>
</head>
<body>
发生了运算错误
</body>
</html>
4.重新部署项目启动Tomcat
- 访问地址http://localhost:8080/ssm01_war_exploded/testArithmeticException,如下
@ExceptionHandler 注解定义的方法优先级问题:例如发生的是 NullPointerException,但是声明的异常有 RuntimeException 和 Exception,这时候会根据异常的最近继承关系找到继承深度最浅的那个@ExceptionHandler 注解方法,即标记了 RuntimeException 的方法。
被 @ExceptionHandler 标记为异常处理方法,不能在方法中设置别的形参。但是可以使用 ModelAndView 向前台传递数据。
使用局部异常处理,仅能处理某个 Controller 中的异常,若需要对所有异常进行统一处理,可使用以下两种方法。
二、使用:@ControllerAdvice+@ExceptionHandler实现异常的处理
这种方式是全局配置,当前任意位置出现的异常都能处理,但是此处优先级低于局部异常处理器(局部异常处理器就是上述方法一写的对于作用域只仅限于当前模块的异常处理)
1.在方式一使用环境基础上,在controller包下创建 ExceptionHandleController2代码如下
在里面定义存在异常的一个方法,代码如下:
package com.augus.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExceptionHandleController2 {
@RequestMapping("/hiArithmeticException")
public String hiArithmeticException() {
System.out.println(10/0);
return "success";
}
}
2.使用@ControllerAdvice+@ExceptionHandler注解配置全局异常处理器
在com.augus下创建exceptionhandler包,然后新建类GloableExceptionHandler,作为全局异常处理器,代码如下:
package com.augus.exceptionhandler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GloableExceptionHandler {
@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
public String ExceptionHandler(Exception exception){
System.out.println("异常信息:"+exception);
//指定跳转页面
return "fail";
}
}
3.对于全局的异常处理器需要配置包扫描
在applicationContext.xml,添加如下信息:
<!--配置扫描springmvc中异常处理类-->
<context:component-scan base-package="com.augus.exceptionhandler"></context:component-scan>
4.在WEB-INF下的templates创建全局异常处理器跳转页面
创建fail.html文件内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全局异常处理</title>
</head>
<body>
这是GloableExceptionHandler类中对于异常的处理
</body>
</html>
5.重新部署项目启动Tomcat
访问 http://localhost:8080/ssm01_war_exploded/hiArithmeticException 如下:
控制器输出信息如下:
而访问 http://localhost:8080/ssm01_war_exploded/testArithmeticException 显示如下:
总结:
这里hiArithmeticException这个url地址访问的方法是新创建的,并没有给单独设置该模块的异常处理器,所有就用全局的异常处理器来处理,而testArithmeticException虽然全局异常处理也能作用到,但是由于testArithmeticException方法在其所属模块中配置过异常处理器,则使用其模块的异常处理器处理异常,也就佐证了 此处优先级低于局部异常处理器
三、使用:SimpleMappingExceptionResolver实现异常的处理
1.在springmvc.xml中配置SimpleMappingExceptionResolver实现异常处理
注意:关闭掉方式二实现的全局异常处理器,注释掉代码和包扫描即可
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--指定出现异常时跳转的页面-->
<prop key="java.lang.ArithmeticException">newfail1</prop>
<prop key="java.lang.NullPointerException">newfail2</prop>
</props>
</property>
</bean>
2.在WEB-INF下的templates创建上面配置的两个异常的跳转页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ArithmeticException</title>
</head>
<body>
处理了异常:ArithmeticException
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>NullPointerException</title>
</head>
<body>
处理了:NullPointerException
</body>
</html>
3.重新部署项目启动Tomcat
访问 http://localhost:8080/ssm01_war_exploded/hiArithmeticException 如下:
访问 http://localhost:8080/ssm01_war_exploded/testNullPointerException 如下:
访问 http://localhost:8080/ssm01_war_exploded/testArithmeticException如下:
通过上面的发现,虽然testArithmeticException和testNullPointerException所在模块,都配置过异常处理器,但在处理异常的时候都是由我们刚刚使用SimpleMappingExceptionResolver实现的处理器实现,所以这个优先级比较高
4.这种方式也可以通过配置类的形式实现,在spring boot中比较常见
在com.augus包下创建config包,然后创建GloableExceptionHandler类(其实就是把上面的xml信息使用代码实现了),代码如下:
package com.augus.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.Properties;
@Configuration
public class GloableExceptionHandler {
@Bean
public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.put("java.lang.ArithmeticException","newfail1");
properties.put("java.lang.NullPointerException","newfail2");
simpleMappingExceptionResolver.setExceptionMappings(properties);
return simpleMappingExceptionResolver;
}
}
注意,这里一定是需要在applicationContext.xml中开启包扫描,如下
<!--配置扫描springmvc中异常处理类-->
<context:component-scan base-package="com.augus.config"></context:component-scan>
四、HandlerExceptionResolver
1.Spring MVC 通过 HandlerExceptionResolver 处理程序异常,包括处理器异常、数据绑定异常以及控制器执行时发生的异常。HandlerExceptionResolver 仅有一个接口方法,源码如下。
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
如发生异常,Spring MVC 会调用 resolveException() 方法,并转到 ModelAndView 对应的视图中,返回一个异常报告页面反馈给用户。
2.在com.augus包下创建config包,然后创建GloableExceptionHandler类实现异常处理器,代码如下:
package com.augus.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Configuration
public class GloableExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView();
//判断异常的类型,根据不同的类型,选择不同的处理方式
if(ex instanceof ArithmeticException){
//设置跳转的视图
modelAndView.setViewName("newfail1");
}else if (ex instanceof NullPointerException){
//设置跳转的视图
modelAndView.setViewName("newfail2");
}
return modelAndView;
}
}
3.在applicationContext.xml中开启包扫描
<!--配置扫描springmvc中异常处理类-->
<context:component-scan base-package="com.augus.config"></context:component-scan>
4.重新部署项目启动Tomcat
- 访问 http://localhost:8080/ssm01_war_exploded/hiArithmeticException 如下:
- 访问 http://localhost:8080/ssm01_war_exploded/testNullPointerException 如下:
- 访问 http://localhost:8080/ssm01_war_exploded/testNullPointerException 如下:
通过上面的发现,和SimpleMappingExceptionResolver实现的一样,虽然testArithmeticException和testNullPointerException所在模块,都配置过异常处理器,但在处理异常的时候都是由我们刚刚使用HandlerExceptionResolver实现的处理器实现,所以这个优先级比较高