Loading

Spring MVC异常处理

Spring MVC异常处理

在 Spring MVC 应用的开发中,不管是操作底层数据库,还是业务层或控制层,都会不可避免地遇到各种可预知的、不可预知的异常。我们需要捕捉处理异常,才能保证程序不被终止。

Spring MVC 有以下 3 种处理异常的方式:

  1. 使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver。
  2. 实现 Spring 的异常处理接口 HandlerExceptionResolver,自定义自己的异常处理器。
  3. 使用 @ExceptionHandler 注解实现异常处理

1. @ExceptionHandler

局部异常处理仅能处理指定 Controller 中的异常。

使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。而被注解的方法,其返回值可以是 ModelAndView、String,或 void,方法名随意,方法参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值。对于异常处理注解的用法,也可以直接将异常处理方法注解于 Controller 之中。

示例 1:下面使用 @ExceptionHandler 注解实现。定义一个处理过程中可能会存在异常情况的 testExceptionHandle 方法。

@Controller
@RequestMapping("/test")
public class MyController {

    @RequestMapping(value="/register.do")
    public ModelAndView register(String name,int age) throws StudentException{
        if(!"张三".equals(name)){
            throw new NameException("姓名不正确");
        }
        return new ModelAndView("/WEB-INF/jsp/show.jsp");
    }
    
    //NameException异常处理
    @ExceptionHandler(NameException.class)
    public ModelAndView handleNameException(Exception ex){
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex",ex);
        mv.setViewName("/errors/nameErrors.jsp");
        return mv;
    }
}

不过,一般不这样使用。而是将异常处理方法专门定义在一个 Controller 中,让其它Controller 继承该 Controller 即可。但是,这种用法的弊端也很明显:Java 是“单继承多实现”的,这个 Controller 的继承将这唯一的一个继承机会使用了,使得若再有其它类需要继承,将无法直接实现。

@Controller
public class MyExceptionResolver {
    //NameException异常处理
    @RequestHandler(NameException.class)
    public ModelAndView handleNameException(Exception ex){
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex",ex);
        mv.setViewName("/errors/nameErrors.jsp");
        return mv;
    }

    //AgeException异常处理
    @ExceptionHandler(AgeException.class)
    public ModelAndView handleAgeException(Exception ex){
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex",ex);
        mv.setViewName("/errors/ageErrors.jsp");
        return mv;
    }
    
    //其他异常处理
    @ExceptionHandler
    public ModelAndView handleOtherException(Exception ex){
        ModelAndView mv = new ModelAndView();
        mv.addObject("ex",ex);
        mv.setViewName("/errors/defaultErrors.jsp");
        return mv;
    }
    
}

2.修改controller

让普通Controller继承自定义好的异常处理Controller。

@Controller
@RequestMapping("/test")
public class MyController extends MyExceptionResolver{
    
    @RequestMapping(value="/register.do")
    public ModelAndView register(String name,int age) throws StudentException{
        
        if(!"张三".equals(name)){
            throw new NameException("姓名不正确");
        }
        
        if(age>50){
            throw new AgeException("年龄太大");
        }
        
        return new ModelAndView("/WEB-INF/jsp/show.jsp");
    }
}

image-20220127210749928

控制器输出结果如下。

打印错误信息 ===> ArithmeticException:java.lang.ArithmeticException: / by zero

@ExceptionHandler 注解定义的方法优先级问题:例如发生的是 NullPointerException,但是声明的异常有 RuntimeException 和 Exception,这时候会根据异常的最近继承关系找到继承深度最浅的那个@ExceptionHandler 注解方法,即标记了 RuntimeException 的方法。

被 @ExceptionHandler 标记为异常处理方法,不能在方法中设置别的形参。但是可以使用 ModelAndView 向前台传递数据。

使用局部异常处理,仅能处理某个 Controller 中的异常,若需要对所有异常进行统一处理,可使用以下两种方法。

2. HandlerExceptionResolver

Spring MVC 通过 HandlerExceptionResolver 处理程序异常,包括处理器异常、数据绑定异常以及控制器执行时发生的异常。HandlerExceptionResolver 仅有一个接口方法,源码如下。

public interface HandlerExceptionResolver {
    @Nullable
    ModelAndView resolveException(
            HttpServletRequest request, HttpServletResponse response, 
      @Nullable Object handler, Exception ex);
}

发生异常时,Spring MVC 会调用 resolveException() 方法,并转到 ModelAndView 对应的视图中,返回一个异常报告页面反馈给用户。

使用 SpringMVC 定义好的 SimpleMappingExceptionResolver 异常处理器,可以实现发生指定异常后的跳转。但若要实现在捕获到指定异常时,执行一些操作的目的,它是完成不了的。此时,就需要自定义异常处理器。自定义异常处理器,需要实现HandlerExceptionResolver接口,并且该类需要在SpringMVC配置文件中进行注册。

public class MyExceptionResolver implements HandlerExceptionResolver{
    
     public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
         
         ModelAndView mv = new ModelAndView();
         //将异常对象加入数据模型中
         mv.addObject("ex",ex);
         
         //设置默认错误响应页面
         mv.setViewName("/errors/defaultErrors.jsp");
         
         //设置NameException响应页面
         if(ex instanceof NameException){
             mv.setViewName("/errors/nameErrors.jsp");
         }
         
         //设置AgeException响应页面
         if(ex instanceof AgeException){
             mv.setViewName("/errors/ageErrors.jsp");
         }
         return mv;
     }
}

示例 2:在 net.biancheng.exception 包中创建一个 HandlerExceptionResolver 接口的实现类 MyExceptionHandler,代码如下。

package net.biancheng.exception;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
public class MyExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
            Exception arg3) {
        Map<String, Object> model = new HashMap<String, Object>();
        // 根据不同错误转向不同页面(统一处理),即异常与View的对应关系
        if (arg3 instanceof ArithmeticException) {
            return new ModelAndView("error", model);
        }
        return new ModelAndView("error-2", model);
    }
}

在 springmvc-servlet.xml 文件中添加以下代码。

<!--托管MyExceptionHandler-->
<bean class="net.biancheng.exception.MyExceptionHandler"/>

再次访问 http://localhost:8080/springmvcDemo2/testExceptionHandle?i=0,页面跳转到 error.jsp 页面,运行结果如上图所示。

3. SimpleMappingExceptionResolver

全局异常处理可使用 SimpleMappingExceptionResolver 来实现。它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。

示例 3:在 springmvc-servlet.xml 中配置全局异常,代码如下。

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!-- 定义默认的异常处理页面,当该异常类型注册时使用 -->
    <property name="defaultErrorView" value="error"></property>
    <!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
    <property name="exceptionAttribute" value="ex"></property>
    <!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常页名作为值 -->
    <property name="exceptionMappings">
        <props>
            <prop key="ArithmeticException">error</prop>
            <!-- 在这里还可以继续扩展对不同异常类型的处理 -->
        </props>
    </property>
</bean>

再次访问 http://localhost:8080/springmvcDemo2/testExceptionHandle?i=0,页面跳转到 error.jsp 页面,运行结果如上图所示。

posted @ 2022-01-27 21:22  BearBrick0  阅读(50)  评论(0编辑  收藏  举报