Spring MVC统一异常处理

Posted 平常心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring MVC统一异常处理相关的知识,希望对你有一定的参考价值。

springMVC提供的异常处理主要有两种方式,一种是直接实现自己的HandlerExceptionResolver,另一种是使用注解的方式实现一个专门用于处理异常的Controller——ExceptionHandler。

 

1、实现自己的HandlerExceptionResolver,HandlerExceptionResolver是一个接口,springMVC本身已经对其有了一个自身的实现——DefaultExceptionResolver,该解析器只是对其中的一些比较典型的异常进行了拦截处理。

 

  1. import javax.servlet.http.HttpServletRequest;   
  2. import javax.servlet.http.HttpServletResponse;   
  3.   
  4. import org.springframework.web.servlet.HandlerExceptionResolver;   
  5. import org.springframework.web.servlet.ModelAndView;   
  6.   
  7. public class ExceptionHandler implements HandlerExceptionResolver {   
  8.   
  9.     @Override  
  10.     public ModelAndView resolveException(HttpServletRequest request,   
  11.             HttpServletResponse response, Object handler, Exception ex) {   
  12.         // TODO Auto-generated method stub   
  13.         return new ModelAndView("exception");   
  14.     }   
  15.   
  16. }  

 

 上述的resolveException的第4个参数表示对哪种类型的异常进行处理,如果想同时对多种异常进行处理,可以把它换成一个异常数组。

定义了这样一个异常处理器之后就要在applicationContext中定义这样一个bean对象,如:

  1. <bean id="exceptionResolver" class="com.tiantian.xxx.web.handler.ExceptionHandler"/>  

 

2、使用@ExceptionHandler进行处理

使用@ExceptionHandler进行处理有一个不好的地方是进行异常处理的方法必须与出错的方法在同一个Controller里面

如:

  1. import org.springframework.stereotype.Controller;   
  2. import org.springframework.web.bind.annotation.ExceptionHandler;   
  3. import org.springframework.web.bind.annotation.RequestMapping;   
  4.   
  5. import com.tiantian.blog.web.servlet.MyException;   
  6.   
  7. @Controller  
  8. public class GlobalController {   
  9.   
  10.        
  11.     /**  
  12.      * 用于处理异常的  
  13.      * @return  
  14.      */  
  15.     @ExceptionHandler({MyException.class})   
  16.     public String exception(MyException e) {   
  17.         System.out.println(e.getMessage());   
  18.         e.printStackTrace();   
  19.         return "exception";   
  20.     }   
  21.        
  22.     @RequestMapping("test")   
  23.     public void test() {   
  24.         throw new MyException("出错了!");   
  25.     }   
  26.        
  27.        
  28. }  

 

这里在页面上访问test方法的时候就会报错,而拥有该test方法的Controller又拥有一个处理该异常的方法,这个时候处理异常的方法就会被调用

  

当发生异常的时候,上述两种方式都使用了的时候,第一种方式会将第二种方式覆盖

 http://gaojiewyh.iteye.com/blog/1297746

 

 

最近使用spring mvc开发一个web系统,发现在controller里发生未捕获异常时不出日志。 

分析DispatcherServlet,初始化handlerExceptionResolvers

  1.         /**  
  2.      * Initialize the strategy objects that this servlet uses.  
  3.      * <p>May be overridden in subclasses in order to initialize  
  4.      * further strategy objects.  
  5.      */  
  6.     protected void initStrategies(ApplicationContext context) {   
  7.         initMultipartResolver(context);   
  8.         initLocaleResolver(context);   
  9.         initThemeResolver(context);   
  10.         initHandlerMappings(context);   
  11.         initHandlerAdapters(context);   
  12. // 初始化异常处理支持器   
  13.         initHandlerExceptionResolvers(context);   
  14.         initRequestToViewNameTranslator(context);   
  15.         initViewResolvers(context);   
  16.     }   
  17.   
  18. // 进入初始化处理方法,具体内容就不贴了,主要是先到上下文中搜寻我们自己定义的ExceptionResolvers,如果没有自定义的resolvers,从默认配置中读取。   
  19. private void initHandlerExceptionResolvers(ApplicationContext context)   
  20.   
  21. // 从默认策略中取得默认配置,从DispatcherServlet.properties文件中取得相关的配置策略,但是在spring2.5的mvc jar包中properties文件中没有HandlerExceptionResolver的默认配置,返回一个EmptyList给handlerExceptionResolvers   
  22. protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface)  
        分析DispatcherServlet,分发处理请求 
  1. // 从dispatch方法中看到,系统对请求进行具体的逻辑处理部分被catch住了一次exception,然后会使用servlet持有的ExceptionResolver进行处理   
  2. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {   
  3.         HttpServletRequest processedRequest = request;   
  4.         HandlerExecutionChain mappedHandler = null;   
  5.         int interceptorIndex = -1;   
  6.   
  7.         // Expose current LocaleResolver and request as LocaleContext.   
  8.         LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();   
  9.         LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);   
  10.   
  11.         // Expose current RequestAttributes to current thread.   
  12.         RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();   
  13.         ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);   
  14.         RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);   
  15.   
  16.         if (logger.isTraceEnabled()) {   
  17.             logger.trace("Bound request context to thread: " + request);   
  18.         }   
  19.            
  20.         try {   
  21.             ModelAndView mv = null;   
  22.             boolean errorView = false;   
  23.   
  24.             try {   
  25.                 processedRequest = checkMultipart(request);   
  26.   
  27.                 // Determine handler for the current request.   
  28.                 mappedHandler = getHandler(processedRequest, false);   
  29.                 if (mappedHandler == null || mappedHandler.getHandler() == null) {   
  30.                     noHandlerFound(processedRequest, response);   
  31.                     return;   
  32.                 }   
  33.   
  34.                 // Apply preHandle methods of registered interceptors.   
  35.                 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();   
  36.                 if (interceptors != null) {   
  37.                     for (int i = 0; i < interceptors.length; i++) {   
  38.                         HandlerInterceptor interceptor = interceptors[i];   
  39.                         if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {   
  40.                             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);   
  41.                             return;   
  42.                         }   
  43.                         interceptorIndex = i;   
  44.                     }   
  45.                 }   
  46.   
  47.                 // Actually invoke the handler.   
  48.                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());   
  49.                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());   
  50.   
  51.                 // Do we need view name translation?   
  52.                 if (mv != null && !mv.hasView()) {   
  53.                     mv.setViewName(getDefaultViewName(request));   
  54.                 }   
  55.   
  56.                 // Apply postHandle methods of registered interceptors.   
  57.                 if (interceptors != null) {   
  58.                     for (int i = interceptors.length - 1; i >= 0; i--) {   
  59.                         HandlerInterceptor interceptor = interceptors[i];   
  60.                         interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);   
  61.                     }   
  62.                 }   
  63.             }   
  64.             catch (ModelAndViewDefiningException ex) {   
  65.                 logger.debug("ModelAndViewDefiningException encountered", ex);   
  66.                 mv = ex.getModelAndView();   
  67.             }   
  68. // 这里catch住controller抛出的异常,使用持有的ExceptionResolver处理,当没有配置自己的处理器时,程序会将异常继续往上抛出,最终交给我们的容器处理   
  69.             catch (Exception ex) {   
  70.                 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);   
  71.                 mv = processHandlerException(processedRequest, response, handler, ex);   
  72.                 errorView = (mv != null);   
  73.             }   
  74.   
  75.             // Did the handler return a view to render?   
  76.             if (mv != null && !mv.wasCleared()) {   
  77.                 render(mv, processedRequest, response);   
  78.                 if (errorView) {   
  79.                     WebUtils.clearErrorRequestAttributes(request);   
  80.                 }   
  81.             }   
  82.             else {   
  83.                 if (logger.isDebugEnabled()) {   
  84.                     logger.debug("Null ModelAndView returned to DispatcherServlet with name ‘" +   
  85.                             getServletName() + "‘: assuming HandlerAdapter completed request handling");   
  86.                 }   
  87.             }   
  88.   
  89.             // Trigger after-completion for successful outcome.   
  90.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);   
  91.         }   
  92. // 当没有配置ExceptionResolver时,异常将到达这里,最终抛出   
  93.         catch (Exception ex) {   
  94.             // Trigger after-completion for thrown exception.   
  95.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);   
  96.             throw ex;   
  97.         }   
  98.         catch (Error err) {   
  99.             ServletException ex = new NestedServletException("Handler processing failed", err);   
  100.             // Trigger after-completion for thrown exception.   
  101.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);   
  102.             throw ex;   
  103.         }   
  104.   
  105.         finally {   
  106.             // Clean up any resources used by a multipart request.   
  107.             if (processedRequest != request) {   
  108.                 cleanupMultipart(processedRequest);   
  109.             }   
  110.   
  111.             // Reset thread-bound context.   
  112.             RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);   
  113.             LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);   
  114.   
  115.             // Clear request attributes.   
  116.             requestAttributes.requestCompleted();   
  117.             if (logger.isTraceEnabled()) {   
  118.                 logger.trace("Cleared thread-bound request context: " + request);   
  119.             }   
  120.         }   
  121.     }  



http://fancyboy2050.iteye.com/blog/1300037

spring mvc异常设置,
  1.       
  2. 此段代码ZZ from http://tdcq.iteye.com/blog/890957   
  3. <!-- 全局异常配置 start -->     
  4.      <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">     
  5.          <property name="exceptionMappings">     
  6.              <props>     
  7.                  <prop key="java.lang.Exception">errors/error</prop>     
  8.                  <prop key="java.lang.Throwable">errors/err</prop>     
  9.              </props>     
  10.          </property>     
  11.          <property name="statusCodes">     
  12.              <props>     
  13.                  <prop key="errors/error">500</prop>     
  14.                  <prop key="errors/404">404</prop>     
  15.              </props>     
  16.          </property>     
  17.          <!-- 设置日志输出级别,不定义则默认不输出警告等错误日志信息 -->     
  18.          <property name="warnLogCategory" value="WARN"></property>     
  19.          <!-- 默认错误页面,当找不到上面mappings中指定的异常对应视图时,使用本默认配置 -->     
  20.          <property name="defaultErrorView" value="errors/error"></property>     
  21.          <!-- 默认HTTP状态码 -->     
  22.          <property name="defaultStatusCode" value="500"></property>     
  23.      </bean>     
  24.      <!-- 全局异常配置 end -->    
   
用spring mvc做了个项目,但是出现异常的情况下居然没有日志输出,然后各种尝试。。。正如上面介绍的:设置日志输出级别,不定义则默认不输出警告等错误日志信息!!【当然,try catch的异常没问题】 
敬请留意。
 
 

如果我们在使用Spring MVC的过程中,想自定义异常页面的话,我们可以使用DispatcherServlet来指定异常页面,具体的做法很简单:

下面看我曾经的一个项目的spring配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8" ?>
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    <!-- 扫描web包,应用Spring的注解 -->
    <context:component-scan base-package="com.xxx.training.spring.mvc"/>
 
    <!-- 配置视图解析器,将ModelAndView及字符串解析为具体的页面 -->
    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver"
            p:viewClass="org.springframework.web.servlet.view.JstlView"
            p:prefix="/WEB-INF/views/"
            p:suffix=".jsp"/>
 
    <!--定义异常处理页面-->
    <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="java.sql.SQLException">outException</prop>
                <prop key="java.io.IOException">outException</prop>
            </props>
        </property>
    </bean>
</beans>

  上面的定义异常处理部分的解释为:只要发生了SQLException或者IOException异常,就会自动跳转到WEB-INF/views/outException.jsp页面。

一般情况下我们的outException.jsp页面的代码为:

1
2
3
4
5
6
7
8
9
10
11
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>异常处理页面</title>
</head>
<body>
<% Exception ex = (Exception) request.getAttribute("Exception");%>
<H2>Exception:<%=ex.getMessage()%>
</H2>
</body>
</html>

  当然你也可以修改样式,这个就看个人喜好了、

另外记得要在web.xml也使用类似下面的方式处理异常哦。:

1
2
3
4
5
6
7
8
9
<error-page>
     <error-code>404</error-code>
     <location>/WEB-INF/pages/404.jsp</location>
 </error-page>
 
 <error-page>
     <exception-type>java.lang.Exception</exception-type>
     <location>/WEB-INF/pages/exception.jsp</location>
 </error-page>

  因为这两个异常处理的维度是不一样的,简单说,spring的resolver是spring内部使用的,而web。xml里的是整个webapp共同使用的。

建议两个都配置上,

因为spring的resolver可以和spring结合的更紧密,可扩展的更多。

以上是关于Spring MVC统一异常处理的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC统一异常处理

spring mvc 异常统一处理方式

spring 或 springboot统一异常处理

MVC过滤器使用案例:统一处理异常顺道精简代码

MVC过滤器使用案例:统一处理异常顺道精简代码

spring MVC实践