Spring MVC的异常处理

Spring MVC处理异常有3种方式

1、Spring MVC提供的简单异常处理器

使用SimpleMappingExceptionResolver进行异常处理,具有集成简单、有良好的扩展性、对已有代码没有入侵性等优点,该方法仅能获取到异常信息,若在出现异常时,对需要获取除异常以外的数据的情况不适用。

2、实现Spring的异常处理接口

使用实现HandlerExceptionResolver接口的异常处理器进行异常处理,具有集成简单、有良好的扩展性、对已有代码没有入侵性等优点,同时,在异常处理时能获取导致出现异常的对象,有利于提供更详细的异常处理信息。

@Component
public class CustomExceptionHandler implements HandlerExceptionResolver {  
  
    @Override  
    public ModelAndView resolveException(HttpServletRequest request,  
            HttpServletResponse response, Object object, Exception exception) {  
        if(exception instanceof IOException){  
            return new ModelAndView("ioexp");  
        }else if(exception instanceof SQLException){  
            return new ModelAndView("sqlexp");  
        }  
        return null;  
    }  
}

3、使用@ExceptionHandler注解实现异常处理

使用@ExceptionHandler注解实现异常处理,具有集成简单、有扩展性好、不需要附加Spring配置等优点,在异常处理时不能获取除异常以外的数据。当局部注解@ExceptionHandler和全局注解@ControllerAdvice共存时,可以进行全局异常捕获。

@ControllerAdvice
public class MyGlobalExceptionHandler {

    @ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
    public ModelAndView handlerException(Exception exception){
        System.out.println("global-------exception1");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        mv.addObject("exce",exception);
        return mv;
    }

    @ExceptionHandler(value = {Exception.class})
    public ModelAndView handlerException2(Exception exception){
        System.out.println("global-------exception2");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("error");
        mv.addObject("exce",exception);
        return mv;
    }
}

当Controller抛出的某个异常多个@ExceptionHandler标注的方法都适用时,Spring会选择最具体的异常处理方法来处理,也就是说@ExceptionHandler(value = {Exception.class})这里标注的方法优先级最低,只有当其它方法都不适用时,才会来到这里处理。

接下来在源码中看具体流程,前端控制器DispatcherServlet对象在创建时会初始化一系列的对象

public class DispatcherServlet extends FrameworkServlet {
    // ......
    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
    // ......
}

在DispatcherServlet的initHandlerExceptionResolvers(context)方法中,所有实现了HandlerExceptionResolver接口的bean会被保存起来,其中就有一个类型为ExceptionHandlerExceptionResolver的bean,这个bean在应用启动过程中会获取所有被@ControllerAdvice注解标注的bean对象做进一步处理。

public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
        implements ApplicationContextAware, InitializingBean {
    // ......
    private void initExceptionHandlerAdviceCache() {
        // ......
        List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
        AnnotationAwareOrderComparator.sort(adviceBeans);

        for (ControllerAdviceBean adviceBean : adviceBeans) {
            ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
            if (resolver.hasExceptionMappings()) {
                // 找到所有ExceptionHandler标注的方法并保存成一个ExceptionHandlerMethodResolver类型的对象缓存起来
                this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
                if (logger.isInfoEnabled()) {
                    logger.info("Detected @ExceptionHandler methods in " + adviceBean);
                }
            }
            // ......
        }
    }
}

当Controller抛出异常时,DispatcherServlet通过ExceptionHandlerExceptionResolver来解析异常,而ExceptionHandlerExceptionResolver又通过ExceptionHandlerMethodResolver 来解析异常找到适用的@ExceptionHandler标注的方法

public class ExceptionHandlerMethodResolver {
    // ......
    private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
        List<Class<? extends Throwable>> matches = new ArrayList<Class<? extends Throwable>>();
        // 找到所有适用于Controller抛出异常的处理方法,例如Controller抛出的异常
        // 是BizException(继承自RuntimeException),那么@ExceptionHandler(BizException.class)和
        // @ExceptionHandler(Exception.class)标注的方法都适用此异常
        for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
            if (mappedException.isAssignableFrom(exceptionType)) {
                matches.add(mappedException);
            }
        }
        if (!matches.isEmpty()) {
                // 这里通过排序找到最适用的方法,排序的规则依据抛出异常相对于声明异常的深度,例如
            // Controller抛出的异常是BizException(继承自RuntimeException),那么BizException
            // 相对于@ExceptionHandler(BizException.class)声明的BizException.class其深度是0,
            // 相对于@ExceptionHandler(Exception.class)声明的Exception.class其深度是2,所以
            // @ExceptionHandler(BizException.class)标注的方法会排在前面
            Collections.sort(matches, new ExceptionDepthComparator(exceptionType));
            return this.mappedMethods.get(matches.get(0));
        }
        else {
            return null;
        }
    }
    // ......
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值