首页 > 编程语言 > 详细

SpringMVC 异常处理

时间:2020-03-11 23:51:49      阅读:113      评论:0      收藏:0      [点我收藏+]

我们知道进行异常处理是 Controller 层的职责之一,但在每一个处理方法里使用try...catch块感觉又不太聪明的亚子...在 SpringMVC 中有 4 种常用方式,对异常进行统一处理:

  • 使用 SpringMVC 预定义的SimpleMappingExceptionResolver,发生指定异常后跳转页面;
  • 实现HandlerExceptionResolver,自定义异常处理器;
  • 使用注解@ExceptionHandler
  • 使用注解@ControllerAdvice配合@ExceptionHandler

下面来说说这 4 种方法的具体使用。

1. 配置 SimpleMappingExceptionResolver

只需要在 SpringMVC 配置文件中注册该异常处理器 Bean 即可,没有 id 属性,无需显式调用或被注入给其它 Bean。它对异常的处理方式仅限于跳转到指定的响应页面,在页面中进行处理。

示例:

// 一个自定义的异常类
public class MemberException extends Exception {
    public MemberException() {
        super();
    }
    public MemberException(String msg) {
        super(msg);
    }
}

配置指定异常和需要跳转的页面:

<!-- SpirngMVC配置文件 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="cn.cna.exception.MemberException">/errors/memberErrors.jsp</prop>
            <!-- 当请求参数的值与接收该参数的处理器方法形参的类型不匹配时,会抛出类型匹配异常TypeMismatchException -->
            <prop key="org.springframework.beans.TypeMismatchException">/errors/typeException.jsp</prop>
        </props>
    </property>
    <property name="defaultErrorView" value="/errors/defaultErrors.jsp"/>
    <property name="exceptionAttribute" value="ex"/>
</bean>

属性解释:

  • exceptionMappings:Properties 类型属性,用于指定不同类型的异常所对应的响应页面,Key 为异常类的全限定类名,value 为响应页面路径;
  • defaultErrorView:指定默认的异常响应页面,若发生的异常没有在 exceptionMappings 中指定,则使用该页面;
  • exceptionAttribute:捕获到的异常对象,可以在异常响应页面中使用。

这样,Controller 方法可以不用显式进行异常处理,而是通过添加throws异常声明,将异常处理的职责交给 HandlerAdapter,进而转交给注册好的 SimpleMappingExceptionResolver,根据不同的异常实例,进行不同的页面响应。

2. 实现 HandlerExceptionResolver

使用第 1 种方法可以实现发生指定异常后的跳转,但如果想要在捕获异常后执行其他的操作,就需要自己实现HandlerExceptionResolver接口,并同样在 SpringMVC 配置文件中注册。HandlerExceptionResolver只有一个resolveException方法需要我们去实现,顾名思义,它的作用就是处理异常。

来看看代码:

public class MemberExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, response httpServletResponse, Object handler, Exception ex) {
        ModelAndView mv = new ModelAndView();
        // 将异常对象加入数据模型中
        mv.addObject("ex",ex);
           
        // 设置默认错误响应页面
        mv.setViewName("/errors/defaultErrors.jsp");
         
        // 设置MemberException响应页面
        if (ex instanceof MemberException) {
            mv.setViewName("/errors/memberErrors.jsp");
        }
        // 其他...

        return mv;
    }
}

在 SpringMVC 配置文件中注册:

<bean class="cn.cna.resolver.MyExceptionResolver"/>

你可能有个疑问,如果项目采用的是前后端分离,需要返回 Json 数据又该如何做?有两个方法:

  1. 用 response 的 writer 直接输出 Json 数据
@Override
public ModelAndView resolveException(HttpServletRequest request, response httpServletResponse, Object handler, Exception ex) {
    // 避免中文乱码
    response.setContentType("application/json;charset=UTF-8");
    response.setCharacterEncoding("UTF-8");
    try {
        // 为了简化演示,这里省略了对象转Json字符串的操作
        String jsonStr = "";
        if (ex instanceof MemberException) {
            response.setStatus(HttpStatus.OK.value());
            jsonStr = "{'code':1,'message':'业务异常'}";
       } else {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            jsonStr = "{'code':500,'message':'服务器未知异常'}";
        }
        response.getWriter().print(jsonStr);
        response.getWriter().flush();
        response.getWriter().close();  // 关闭了输出,确保后面不会再使用该response,比如拦截器
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;  // 后续不会有渲染步骤,但若还有处理器将继续执行
}
  1. 借助 MappingJackson2JsonView
    MappingJackson2JsonView是一个 Json 视图,用法如下:
@Override
public ModelAndView resolveException(HttpServletRequest request, response httpServletResponse, Object handler, Exception ex) {
    ModelAndView mv = new ModelAndView();
    MappingJackson2JsonView view = new MappingJackson2JsonView();
    view.setJsonPrefix("fsxJson"); // 设置 Json 前缀
    // view.setModelKey(); 只序列化指定的key
    mv.setView(view);
    // 添加key-value非常方便
    mv.addObject("code", "1");
    mv.addObject("message", "业务异常");
    return mv;
}

详细可见:@ExceptionHandler or HandlerExceptionResolver?如何优雅处理全局异常?【享学Spring MVC】,YourBatman,CSDN,写得非常深入的一篇文章,值得好好研究。

3. 使用 @ExceptionHandler

@ExceptionHandler是 Spring 3.0 后提供的处理异常的注解,目的在于对 REST 应用提供更多的支持(是的,ModelAndView不再是必须的)。被@ExceptionHandler标注的方法将成为一个异常处理器,通过value属性(类型是一个Class<?>数组)指定该方法所要匹配的异常。

// org.springframework.web.bind.annotation.ExceptionHandler
@Target(ElementType.METHOD) // 只能标注在方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
    // 指定异常类型,可以多个
    Class<? extends Throwable>[] value() default {};
}
//MemberException异常处理
@ExceptionHandler(MemberException.class)
public ApiResult handleMemberException(MemberException ex){
    return ApiResult.error().setMessage(ex.getMessage());
}

但是请注意,被@ExceptionHandler注解的方法只能处理同一个 Controller 中的其他方法产生的异常。一般会将异常处理方法专门定义在一个 Controller 中,让其它 Controller 继承它。但是 Java 是单继承的呀,如果基类 Controller 中还需要封装其他逻辑,就会显得臃肿,职责也不够明确。

@ControllerAdvice + @ExceptionHandler

@ControllerAdvice是 Spring3.2 提供的新注解,基于 AOP 思想,可以指定一个 Controller 增强器,配合@ExceptionHandler可用于全局的异常处理,现在不必纠结于继承了。

// 全局异常处理
// 如果是REST应用,就使用@RestControllerAdvice,不用给每个方法写@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {
    @ResponseBody
    @ExceptionHandler(MemberException.class)
    public ApiResult handleMemberException(MemberException ex) {
        return ApiResult.error().setMessage(ex.getMessage());
    }
    // 添加其他异常处理方法...
}

优先级

从高到低:

  • @Controller + @ExceptionHandler
  • @ControllerAdvice + @ExceptionHandler
  • HandlerExceptionResolver

END

参考:
springmvc异常处理,JS_HCX,简书
@ControllerAdvice实现优雅地处理异常,KEN DO EVERTHING,CSDN
Spring 异常处理三种方式,喜欢日向雏田一样的女子啊,CSDN

SpringMVC 异常处理

原文:https://www.cnblogs.com/zzzt20/p/12466491.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!