zoukankan      html  css  js  c++  java
  • Spring MVC中的拦截器Interceptor

    谈谈spring中的拦截器

           在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、预先设置数据以及统计方法的执行效率等等。今天就来详细的谈一下spring中的拦截器。spring中拦截器主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。

    一,HandlerInterceptor拦截器

    HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址,比MethodInterceptor先执行。实现一个HandlerInterceptor拦截器可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。这两种方法殊途同归,其实HandlerInterceptorAdapter也就是声明了HandlerInterceptor接口中所有方法的默认实现,而我们在继承他之后只需要重写必要的方法。下面就是HandlerInterceptorAdapter的代码,可以看到一个方法只是默认返回true,另外两个是空方法:

     1 public abstract class HandlerInterceptorAdapter implements HandlerInterceptor {  
     2   
     3     /** 
     4      * This implementation always returns <code>true</code>. 
     5      */  
     6     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
     7         throws Exception {  
     8         return true;  
     9     }  
    10   
    11     /** 
    12      * This implementation is empty. 
    13      */  
    14     public void postHandle(  
    15             HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)  
    16             throws Exception {  
    17     }  
    18   
    19     /** 
    20      * This implementation is empty. 
    21      */  
    22     public void afterCompletion(  
    23             HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)  
    24             throws Exception {  
    25     }  
    26   
    27 }  

    这三个方法都是干什么的,有什么作用,什么时候调用,不同的拦截器之间是怎样的调用顺序呢?这还得参考一下DispatcherServlet的doDispatch方法:

     1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
     2         HttpServletRequest processedRequest = request;
     3         HandlerExecutionChain mappedHandler = null;
     4         boolean multipartRequestParsed = false;
     5 
     6         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
     7 
     8         try {
     9             ModelAndView mv = null;
    10             Exception dispatchException = null;
    11 
    12             try {
    13                 processedRequest = checkMultipart(request);
    14                 multipartRequestParsed = (processedRequest != request);
    15 
    16                 // Determine handler for the current request.
    17                 mappedHandler = getHandler(processedRequest);
    18                 if (mappedHandler == null || mappedHandler.getHandler() == null) {
    19                     noHandlerFound(processedRequest, response);
    20                     return;
    21                 }
    22 
    23                 // Determine handler adapter for the current request.
    24                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    25 
    26                 // Process last-modified header, if supported by the handler.
    27                 String method = request.getMethod();
    28                 boolean isGet = "GET".equals(method);
    29                 if (isGet || "HEAD".equals(method)) {
    30                     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    31                     if (logger.isDebugEnabled()) {
    32                         logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
    33                     }
    34                     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    35                         return;
    36                     }
    37                 }
    38 
    39                 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    40                     return;
    41                 }
    42 
    43                 // Actually invoke the handler.
    44                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    45 
    46                 if (asyncManager.isConcurrentHandlingStarted()) {
    47                     return;
    48                 }
    49 
    50                 applyDefaultViewName(processedRequest, mv);
    51                 mappedHandler.applyPostHandle(processedRequest, response, mv);
    52             }
    53             catch (Exception ex) {
    54                 dispatchException = ex;
    55             }
    56             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    57         }
    58         catch (Exception ex) {
    59             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    60         }
    61         catch (Error err) {
    62             triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
    63         }
    64         finally {
    65             if (asyncManager.isConcurrentHandlingStarted()) {
    66                 // Instead of postHandle and afterCompletion
    67                 if (mappedHandler != null) {
    68                     mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    69                 }
    70             }
    71             else {
    72                 // Clean up any resources used by a multipart request.
    73                 if (multipartRequestParsed) {
    74                     cleanupMultipart(processedRequest);
    75                 }
    76             }
    77         }
    78     }

    它封装了springMVC处理请求的整个过程。首先根据请求找到对应的HandlerExecutionChain,它包含了处理请求的handler和所有的HandlerInterceptor拦截器;然后在调用hander之前分别调用每个HandlerInterceptor拦截器的preHandle方法,若有一个拦截器返回false,则会调用triggerAfterCompletion方法,并且立即返回不再往下执行;若所有的拦截器全部返回true并且没有出现异常,则调用handler返回ModelAndView对象;再然后分别调用每个拦截器的postHandle方法;最后,即使是之前的步骤抛出了异常,也会执行triggerAfterCompletion方法。关于拦截器的处理到此为止,接下来看看triggerAfterCompletion做了什么

     1 void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
     2             throws Exception {
     3 
     4         HandlerInterceptor[] interceptors = getInterceptors();
     5         if (!ObjectUtils.isEmpty(interceptors)) {
     6             for (int i = this.interceptorIndex; i >= 0; i--) {
     7                 HandlerInterceptor interceptor = interceptors[i];
     8                 try {
     9                     interceptor.afterCompletion(request, response, this.handler, ex);
    10                 }
    11                 catch (Throwable ex2) {
    12                     logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
    13                 }
    14             }
    15         }
    16     }

     triggerAfterCompletion做的事情就是从当前的拦截器开始逆向调用每个拦截器的afterCompletion方法,并且捕获它的异常,也就是说每个拦截器的afterCompletion方法都会调用。

            根据以上的代码,分析一下不同拦截器及其方法的执行顺序。假设有5个拦截器编号分别为12345,若一切正常则方法的执行顺序是12345的preHandle,54321的postHandle,54321的afterCompletion。若编号3的拦截器的preHandle方法返回false或者抛出了异常,接下来会执行的是21的afterCompletion方法。这里要注意的地方是,我们在写一个拦截器的时候要谨慎的处理preHandle中的异常,因为这里一旦有异常抛出就不会再受到这个拦截器的控制。12345的preHandle的方法执行过之后,若handler出现了异常或者某个拦截器的postHandle方法出现了异常,则接下来都会执行54321的afterCompletion方法,因为只要12345的preHandle方法执行完,当前拦截器的拦截器就会记录成编号5的拦截器,而afterCompletion总是从当前的拦截器逆向的向前执行。

            另外,实现HandlerInterceptor拦截器还有一个方法,就是实现WebRequestInterceptor接口。其实它和刚才的两种方法也是殊途同归,最终还是被spring适配成HandlerInterceptor。有一点不同,它的preHandle方法最终只会返回true。

    拦截器的配置

    <mvc:interceptors>  
        <!-- 使用 bean 定义一个 Interceptor,直接定义在 mvc:interceptors 下面的 Interceptor 将拦截所有的请求 -->  
        <bean class="com.psm.interceptor.WrongCodeInterceptor"/>  
        <mvc:interceptor>  
            <mvc:mapping path="/login.do"/>  
            <!-- 定义在 mvc:interceptor 下面的 Interceptor,表示对特定的请求进行拦截 -->  
            <bean class="com.psm.interceptor.LoginInterceptor"/>  
        </mvc:interceptor>  
    </mvc:interceptors> 

    在 Spring 的XML 配置文件中,咱们可以通过mvc:interceptors标签声明一系列的拦截器,例如:

    1 <mvc:interceptors>
    2         <bean class="com.hit.interceptor.ContextInterceptor"/>
    3         <bean class="com.hit.interceptor.LoginInterceptor"/>
    4         <bean class="com.hit.interceptor.WrongCodeInterceptor"/>
    5 </mvc:interceptors>

    如上所示,这些拦截器就够成了一个拦截器链,或者称之为拦截器栈。而这些拦截器的执行顺序是按声明的先后顺序执行的,即:先声明的拦截器先执行,后声明的拦截器后执行。在mvc:interceptors标签下声明interceptor标签主要有两种方式:

    • 直接定义一个 Interceptor 实现类的 bean 对象,使用这种方式声明的 Interceptor 拦截器将会对所有的请求进行拦截;
    • 使用mvc:interceptor标签进行声明,使用这种方式进行声明的 Interceptor 可以通过mvc:mapping子标签来定义需要进行拦截的请求路径。

    此外,由于拦截器是 AOP 编程思想的典型应用,也就意味着咱们可以“切”到具体的“面”进行某些操作。例如,

    <bean id="WrongCodeInterceptor" class="com.hit.interceptor.WrongCodeInterceptor">
            <property name="userName" value="user-module"></property>
    </bean>
    
    <bean id="loginInterceptor" class="com.hit.interceptor.LoginInterceptor">
        <property name="excludePackages">
           <list>
              <value>com.hit.user.exception</value>
              <value>com.hit.order.exception</value>
           </list>
        </property>
    </bean>
    
    <aop:config>
        <aop:advisor advice-ref="WrongCodeInterceptor" pointcut="execution(* com.hit.*.demo..*.*(..)) || execution(* com.hit.*.demo..*.*(..)) " />
        <aop:advisor advice-ref="loginInterceptor" pointcut="execution(* com.hit.*.demo..*.*(..))" />
    </aop:config>

    引用博客

    http://blog.csdn.net/qq_35246620/article/details/68487904

    http://blog.csdn.net/hongxingxiaonan/article/details/48090075

  • 相关阅读:
    Educational Codeforces Round 59 (Rated for Div. 2)E. Vasya and Binary String 区间dp
    MySQL语法大全
    D. Buy a Ticket(优先队列+dijkstra)
    Two Sets(并查集分类)
    KMP浅显易懂
    深度理解链式前向星
    快速幂(幂运算取模的logn算法)
    hdu---1950---Bridging signals解题报告(求Lis n*logn贪心+二分搜索)
    dp背包
    线段树模板
  • 原文地址:https://www.cnblogs.com/oldzhang1222/p/8252805.html
Copyright © 2011-2022 走看看