spring mvc 异常处理统一解决方案-最佳实践

spring mvc 异常处理

在spring mvc中异常处理几种种方式,由于每种方式使用的场景不一样,根据实际场景使用不同的异常处理方式,或者几种方式配合使用。几种配合时具有不同的优先级。编程的过程就是寻找最佳实践的过程。

基于web.xml方式

这种方式的实现方式简单,在web.xml配置错误码和对应页面,配置异常处理类型以及错误处理连接,在web.xml中添加如下配置即可

        <exception-type>java.lang.NullPointerException</exception-type>
        <error-code>404</error-code>
        <location>/404.html</location>
    </error-page>
    <!--定向到Controller中-->
    <error-page>
        <error-code>500</error-code>
        <location>/500</location>
    </error-page>
    <!--定向到Controller中-->
    <!-- 异常类型-->
    <error-page>
        <exception-type>java.lang.NullPointerException</exception-type>
        <location>/nullpointer.html</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/uncaughtException</location>
    </error-page>

优点

  • 简单易用,易于理解,灵活

缺点

  • 配置多,易出错
  • 异常处理需要写java代码

使用
根据特点,一般只用来配置错误码页面,具有通用性,在任何项目中都可以使用。对与异常处理需要编写相应代码完成(不同web框架实现方式不一样)。错误码的优先级在几种异常处理方式最高的的。

基于spring-servlet.xml方式

是web.xml中异常类型处理的不同形式。在spring-servlet中配置对于各种异常类型的处理。这种方式只是将web.xml中的异常类型处理挪到另一个配置文中和换另一种表达方式。

            <prop key="java.lang.NullPointerException">/NullPointerException.do</prop>
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!-- 默认错误页面,就是不在exceptionMappings指定范围内 -->
    <property name="defaultErrorView" value="/uncaughtException.do" />
    <property name="exceptionMappings">
        <props>
            <!-- 异常类名,可以是全路径,错误页面或Controller路径! -->
            <prop key="java.lang.NullPointerException">/NullPointerException.do</prop>
            <prop key="java.io.IOException">/IOException.do</prop>
        </props>
    </property>
    </bean>
    <!-- 异常处理 END -->

优点

  • 对特定异常采用特殊处理

缺点

  • 没啥新意

使用
不好用,有更好的解决方案替代。优先级低,具体位于哪个等级不清楚,不关心。

实现HandlerExceptionResolver接口方式

统一处理在Controller层抛出的异常,在`public ModelAndView resolveException(HttpServletRequest request,

        HttpServletResponse response, Object handler, Exception ex)`方法中做异常类型判断,根据不同异常类型做不同的处理。
@Component
public class CustomerExceptionHandler implements HandlerExceptionResolver {
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("Exception Resolver Handler Exception");
        ModelAndView view = new ModelAndView("/error.jsp");
        view.addObject("ex", ex);
        return view;
    }
}

优点

  • 统一处理异常,维护方便

缺点

  • 只能返回ModelAndView,没有其他可选方式,不过有其他方案弥补,例如json.

使用
建议使用统一处理异常,配合其他处理方式。优先级低

@Controller中@ExceptionHandler方式

指定处理当前Controller中抛出异常,@ExceptionHandler({“exceptionlist”}) 一个方法可以处理多种异常类型

@Controller
@RequestMapping("/showcase/exception/enhance")
public class SpringMvcExceptionEnhance {

    /**
     * @Title:        handleException 
     * @Description:  统一处理异常
     * @param:        @param exception
     * @param:        @return    
     * @return:       ModelAndView    
     * @throws 
     * @author        budingge.com
     * @Date          2014年7月26日 上午3:07:43
     */
    @ExceptionHandler(Exception.class)//制定处理异常
    public ModelAndView handleException(Exception exception) {
        ModelAndView view = new ModelAndView("/error.jsp");
        view.addObject("ex", exception);
        return view;
    }

    // 制定处理异常
    @ExceptionHandler(CustomerException.class)
    public ModelAndView handleCustomerException(CustomerException exception) {
        ModelAndView view = new ModelAndView("/error.jsp");
        view.addObject("ex", exception);
        return view;
    }
}

优点

  • 统一处理一个Controller中异常,定制方便,对处理方法几乎没有限制,优先级最高

缺点

  • 只能处理Controller中异常,维护困难,通过继承方式可以解决

使用
在需要特殊处理异常时使用,配置其他处理方式使用,优先级最高

@ControllerAdvice中@ExceptionHandler方式(spring 3.2之后版本)

能处理所有Controller中抛出异常,功能强大,优先级低于@Controller上的@ExceptionHandler

@ControllerAdvice
public class SpringMvcExceptionFutrue {

    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception exception) {
        System.out.println("Exceptions Handler By @ControllerAdvice...");
        ModelAndView view = new ModelAndView("/error.jsp");
        view.addObject("ex", exception);
        return view;
    }

    @ExceptionHandler(CustomerException.class)
    public void handleCustomerException(HttpServletResponse response,CustomerException exception) throws IOException {
        System.out.println("Exceptions Handler By @ControllerAdvice...");
        response.getWriter().write("failed");
    }
}

优点

  • 处理所有Controller中异常,优先级仅低于@Controller上的@ExceptionHandler方式

缺点

  • 我想想

使用
建议使用,可以完全处理异常

继承HandlerInterceptorAdapter拦截器方式

在拦截器中捕获Controller中抛出的异常,统一处理,与实现HandlerExceptionResolver接口方式一样,不过优先级低较低。

/**
 * @ClassName:     CustomerHandlerInterceptor.java
 * @Description:   spring mvc拦截器,处理异常
 * 
 * @author         budingge.com
 * @version        V1.0  
 * @Date           2014年7月26日 下午1:46:17 
 */
public class CustomerHandlerInterceptor extends HandlerInterceptorAdapter {

    /**
     * 在Controller之前执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Enter HandlerInterceptor preHandle....");
        return super.preHandle(request, response, handler);
    }

    /**
     * 在Controller之后和渲染视图之前执行
     */
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 在渲染视图之后执行,判断异常
     */
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

        super.afterCompletion(request, response, handler, ex);
        if (ex != null) {
            System.out
                    .println("An Exception Be Catched At HandlerInterceptor afterCompletion....");
            System.out.println(ex);
            request.setAttribute("ex", ex);
            request.getRequestDispatcher("/error.jsp").forward(request,
                    response);
        }
    }

    /**
     * servlet3.0异步功能
     */
    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        super.afterConcurrentHandlingStarted(request, response, handler);
    }

}

在spring-servlet.xml中配置

<mvc:interceptors>
        <!-- Changes the locale when a 'locale' request parameter is sent; e.g. /?locale=de -->
        <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
        <bean class="com.budingge.spring.chowcase.springmvc.web.interceptor.CustomerHandlerInterceptor" />  
    </mvc:interceptors>

优点

  • 统一处理异常

缺点

  • 优先级低,由使用场景定的,不是针对异常而设计的

使用
可以使用,记录异常日志,不过拦截器最大的用处还是权限控制。

spring AOP处理方式

跟@ControllerAdvice类似,优先级应该是最高的,没有验证过。编写AOP类和处理规则即可

优点

  • 统一处理异常,优先级应该是最高的,统一日志处理

缺点

  • 对返回值无法控制

使用
日志记录使用

总结

综上spring mvc中几种已处理方式,每种有不同的应用场景,利用他们的特点在不同场景中使用,一个项目中同时使用几种也不见怪。但同时使用时要注意优先级问题。在一个spring mvc项目中的异常处理最佳实践我觉得应该是这样:

  • web.xml处理错误码异常
  • @ControllerAdvice统一处理异常
  • @Controller @ExceptionHandler处理由特殊需求异常
  • AOP方式可用来记录日志

通过以上这配合完全可以处理Controller中的所有异常。

你可能感兴趣的:(spring mvc 异常处理统一解决方案-最佳实践)