`
zachary.guo
  • 浏览: 487778 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Spring MVC 中的 HandlerInterceptor

阅读更多
        在做 web 开发中,特别是使用 MVC 框架时,要是不谈谈拦截器这个概念,那可显示不出你的牛逼,o(∩_∩)o...哈哈!!!Struts2 中有拦截器,Spring MVC 同样也有拦截器。

        在 Spring MVC 中的 HandlerAdaptor 这篇文章中,我提到过,HandlerMapping 的 getHandler(request) 方法返回的并不是用于处理请求的 handler,而是被包装过的 HandlerExecutionChain:
package org.springframework.web.servlet;

public interface HandlerMapping {

    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
    
}

public class HandlerExecutionChain {

  private final Object handler;

  private HandlerInterceptor[] interceptors;

  private List<HandlerInterceptor> interceptorList;

  /**
   * Create a new HandlerExecutionChain.
   * @param handler the handler object to execute
   */
  public HandlerExecutionChain(Object handler) {
    this(handler, null);
  }
  
  ......
  
}


        正如上述代码所示,HandlerExecutionChain 中除了封装了用于处理请求的 handler,同时还包含了 HandlerInterceptor。如果我们从 HandlerInterceptor 所处的位置溯源而上(HandlerInterceptor → HandlerExecutionChain → HandlerMapping),则会发现 HandlerMapping 是其最终的发源地。因此,我们只需要将 interceptor 注入我们定义的 HandlerMapping 中即可。

        老规矩,先看看 HandlerInterceptor 为何物:
package org.springframework.web.servlet;

public interface HandlerInterceptor {

  boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
      Object handler) throws Exception;
      
  void postHandle(
      HttpServletRequest request, HttpServletResponse response, Object handler, 
          ModelAndView modelAndView) throws Exception;
      
  void afterCompletion(
      HttpServletRequest request, HttpServletResponse response, Object handler, 
      Exception ex) throws Exception;
      
}

        我们自己写一个拦截器,一般实现 HandlerInterceptor 接口的 preHandle 方法即可。但我为了要写一个拦截器,即便后两个接口方法我不去实现,但也得写个空实现,煞是不爽。继承 java 的光荣传统,搞一个 XXXAdaptor 就可以了,这个 XXXAdaptor 实现的方法全部为空实现,我们自己的拦截器继承此 XXXAdaptor 即可,想覆写哪个方法就覆写哪个。当然,Spring NVC 已经为我们准备好了这个 adaptor:org.springframework.web.servlet.handler.HandlerInterceptorAdapter。此 adaptor 是一个抽象类。

        在学习 Spring MVC 的拦截器的概念时,我一直在想:为什么不把拦截器定义成 handler 的属性,各自的 handler 应用各自的 interceptor(s)?那你肯定会回答,定义上百个 handler,每个 handler 都注入 interceptor,累不累啊?肯定要定义在 HandlerMapping 里,使应用其 HandlerMapping 来映射的 handler 都应用这些拦截器(链)不就完了嘛。

        其实,我也不得不这样去想,但我们经常碰到的情况是:
  1. 在我们定义的 handler 中,都要检查 session 是否存在,这个拦截器定义在 HanlerMapping 中无可厚非。可是,总有那么几个 handler 在处理请求时不需要 session,怎么办?
  2. 即便我把那几个按非常规处理的 handler 剥离出来,不和按常规处理请求的 handler 用同一个 HandlerMapping,这几个 handler 用其它的 HandlerMapping 来做映射。OK,这样的确解决了,请继续往下看。
  3. 即便如此,再来怪一点的需求:这几个特殊的 handler 假若有 10 个,它们要分别应用 10 个不同的拦截器,咋搞?
  4. 再来个更怪异点的:即便对于一个 handler 而言,通过这个 url 过来的请求要用到 session 检查的拦截器;通过另外一个 url 过来的请求不做 session 检查,那不是傻眼了吗?

        暂且不管我为什么提出上述的问题,如果正如我一开始说的那样,拦截器是定义在 handler 里的,属于 handler 的属性,那么,上面的问题就不是问题了(最后一个问题仍然无法解决,当然,你可以认为这是个无理的需求,不合理的需求)。

        如果真的是一个 hanlder 就定义一个 interceptor(s),这是合理的软件设计理念吗?别忘了【二八原则】:花 20% 的精力去解决那 80% 的问题,往往很小的努力能解决很多类似的问题。那些特殊的问题也就不过 20%,尽管解决起来不那么容易,但毕竟出现这些问题的几率也不大。

        将 interceptor(s) 定义在 HandlerMapping 里,正是 Spring MVC 所采取的策略,利用这种方式,我们来说说如何解决上面我提出的问题。首先说明的是,一般情况下我们使用 ControllerClassNameHandlerMapping 这一个 HandlerMapping 就足够了,毕竟它是约定优于配置的体现,但为了解决那 20% 的问题,我不得不使用 SimpleUrlHandlerMapping。

        第一个问题,将那些特殊的 handler 不采用 ControllerClassNameHandlerMapping  而采用 SimpleUrlHandlerMapping 来映射。只不过我们写 url 和 handler 的映射关系时,可以用 ControllerClassNameHandlerMapping 式的 url 来做匹配,这样既没有丧失约定优于配置的优点,又可以实现特殊的 handler 应用特殊的拦截器(将这些特殊用到的拦截器定义在 SimpleUrlHandlerMapping 里即可)。

        第二/三个问题,我们整 10 个 SimpleUrlHandlerMapping 来一一映射这 10 个特殊应用的 handler,然后将 10 个不同的拦截器分别注入到这 10 个 SimpleUrlHandlerMapping 即可。

        第四个问题,同解决第二/三个问题采取的思路一样,为某一个 Controller 应用多个 HandlerMapping,url 的映射不存在关联关系,即某个 url 只可能映射到一个 HandlerMapping。若是还不明白这个案例是什么意思,那我就举一个具体的案例:NewsAction, news.action 这样的请求要应用 session 拦截器;news_static.action 用于生成静态文件,不要应用拦截器。映射时,一个为 news.action -> NewsAction,一个为 news_static.action -> NewsAction。如果采取模糊匹配,news.action 包含了 news*.action,怎么也不会映射到 news_static.action。把 news_static.action 的这个 HandlerMapping 优先级调高就行了,news.action 不匹配 news_static.action,自然会找下一个 HandlerMapping。甚至,你定义成 a.action 和 b.action 毫无关系的映射名来映射同一个 Controller 都行,这就是我说的 url 的映射不存在关联关系

        如此说来,Spring MVC 的 interceptor 机制,甚至都可以控制到 Controller 的方法级了,够细粒度了吧。还是那句话,发生这种情况的可能性只有 20%,也就是说,搞如此麻烦的配置的几率不大。这也终于解开了学习 interceptor 的结了。

        这里,再插一句话,为什么会提出那四个问题,是因为我一直在 struts2 的环境下开发,上述的问题的确在实际业务上碰到了。Struts2 可以为单个 action 定义拦截器(链),既而产生了我最开始的疑问。细细一想,Struts2 真的是基于类而定义的拦截器吗,或者说,Struts 解决 80% 的 Action 要使用的拦截器是怎么做的?回忆一下,使用 Struts2 时,经常使用的 url 是什么?是不是 /package/actionName.action,这个能不能理解成是 Struts2 的 HandlerMapping,只不过它只有一个 HandlerMapping 而已。其余的,大家可以自由发挥地去思考。另外,第四个问题,同一个 action 对不同的 url 映射应用不同的拦截器,采取新闻生成的案例,或许不恰当。因为在做静态生成时,一般不会再发 http 请求,现在都采用模板技术了。这里之所以拿这个案例来说,因为这是个历史遗留问题了而已。

        这篇文章旨在讲解原理,事实上,从 Spring 2.5 后,都是采用基于注解的 Controller 了, 采取 <mvc:interceptor>,<mvc:mapping path="/user/*" /> 为某个 url 的请求单独应用拦截器,或者为所有 Controller 应用同样的拦截器。这种 <mvc:...> 的方式来定义更为简洁,是,简单归简单,毕竟这只是表现形式而已。但是,万变不离其宗,不变的永远是其背后的原理!
分享到:
评论
2 楼 qq342806869 2014-01-09  
看帖回帖为荣,不错的东西
1 楼 ddnzero 2013-05-28  

相关推荐

    Mastering Spring MVC 4(2015.09)源码

    在2015年的版本中,Spring MVC 4已经相当成熟,提供了许多特性来简化开发流程并提高开发效率。在这个"Mastering Spring MVC 4(2015.09)源码"中,我们可以深入理解这个框架的核心概念和实际应用。 首先,Spring MVC ...

    spring mvc 的入门级资料

    在 Spring MVC 中,核心类和接口是 DispatcherServlet、HandlerMapping、HandlerAdapter、Controller、ViewResolver、HandlerInterceptor、View 等。 DispatcherServlet 是 Spring MVC 的前置控制器,它负责处理...

    spring mvc jar包

    在提供的压缩包文件中,包含了 Spring MVC 开发所需的一些核心库,这些库是 Spring MVC 功能实现的基础。 1. `org.springframework.context-3.0.2.RELEASE.jar`:这个库提供了上下文支持,包括应用上下文...

    Spring mvc 教程

    ### Spring MVC 教程知识点详解 #### Spring Web MVC 框架简介 Spring Web MVC 是 Spring Framework 的一个重要组成部分,主要用于构建基于 Java 的 Web 应用程序。它提供了一个灵活且强大的 MVC 实现,使得开发者...

    精通Spring MVC4

    通过实现HandlerInterceptor接口并注册到Spring MVC配置中,可以实现对特定请求的拦截。 7. **异常处理** Spring MVC提供了一种优雅的异常处理机制,可以使用@ControllerAdvice和@ExceptionHandler注解定义全局...

    Spring MVC 学习记录总结1

    在这个学习记录总结中,我们将深入理解Spring MVC的核心概念、主要组件以及其工作流程。 1. Spring MVC 概述 Spring MVC 是Spring框架的一部分,它基于Spring IoC(Inversion of Control,控制反转)容器,简化了...

    spring MVC_快速入门

    要使用Spring MVC,首先需要在项目中引入Spring MVC的依赖,并配置Spring的DispatcherServlet,它是Spring MVC的前端控制器,负责调度请求到相应的处理器。 3. **Controller** Controller是处理请求的组件,通常...

    Spring MVC Locale 的使用 中文 英文 等语言 切换

    在Spring MVC框架中,`Locale`是用来处理应用程序中的多语言支持的关键组件。它定义了用户的语言和地区设置,例如中文(zh_CN)或英文(en_US)。这篇博客文章将深入探讨如何在Spring MVC应用中实现语言切换功能,以...

    spring mvc

    在 Spring MVC 中,可以通过 `@Autowired` 注解自动注入 Spring 容器中的 Bean。此外,也可以使用 `ApplicationContext` 对象手动获取 Bean。 #### 十八、Spring MVC 多视图控制器 多视图控制器允许一个控制器处理...

    Spring MVC 4.2.4.RELEASE 中文文档

    Spring MVC 中的 multipart 文件上传支持详细说明了如何使用 Spring MVC 处理文件上传请求,包括客户端发起的上传和表单中的上传。异常处理部分阐述了如何处理框架和应用中的各种异常情况。 Web 安全的介绍部分涉及...

    Spring mvc showcase

    在"Spring MVC Showcase"项目中,我们可以深入理解Spring MVC的核心概念、配置和实际应用。这篇详细解析将带你了解Spring MVC的关键要素,并通过实际代码示例来探讨其工作原理。 1. **Spring MVC 概述** Spring ...

    Spring MVC4.1.6jar包

    7. **拦截器(Interceptor)**: 通过实现HandlerInterceptor接口或使用Spring MVC的预定义拦截器,可以在请求处理前后执行自定义逻辑,如登录检查、性能监控等。 8. **异常处理**: Spring MVC允许通过@...

    spring mvc深入总结

    而 `Controller` 接口虽然在 Spring MVC 中并不直接使用,但通过 `@Controller` 注解的类可以扮演 Controller 角色。 `HandlerInterceptor` 接口提供了拦截器功能,允许开发者在请求处理前后执行自定义逻辑,例如...

    Spring MVC示例

    在Spring MVC中,自定义的拦截器需要实现`HandlerInterceptor`接口并注册到`HandlerMapping`。 10. **异常处理**: Spring MVC提供了统一的异常处理机制,可以使用`@ExceptionHandler`注解在Controller方法中处理...

    Spring MVC 中文资料

    Spring MVC 作为一款优秀的模型-视图-控制器(MVC)框架,在实际应用中展现出了诸多优点,尤其在其3.0版本之后,越来越多的开发团队选择采用 Spring MVC。 1. **学习难度低**:相较于 Struts2,Spring MVC 的学习...

    spring mvc过程详解

    Spring MVC 中的拦截器通过实现 `HandlerInterceptor` 接口或者标注 `@ControllerAdvice` 来实现。拦截器可以在请求处理前后执行自定义逻辑,例如日志记录、权限验证等。 #### 十、Spring MVC 的单元测试实践 对于...

    Spring.MVC学习指南.pdf

    Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。这份“Spring.MVC学习指南.pdf”很可能是为了帮助开发者深入理解和掌握Spring MVC的核心概念...

    spring mvc 核心jar包

    Spring MVC 是一个基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的一部分,专门用于构建 MVC(Model-View-Controller)架构的 Web 应用程序。这个核心 jar 包包含了 Spring MVC 运行所必需的类库,使得开发者...

    spring mvc3 学习资料

    - 可能包括如何在Spring MVC中使用JSR-303规范进行数据验证,以及如何展示验证错误信息。 12. 源码下载 - 为方便学习和实践,文档提供了各章节源代码的下载链接。 - 读者可以下载源代码进行实际操作,加深对...

    spring mvc项目后端源码

    Spring MVC 是一个基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的一部分,专为构建可维护、高性能的 Web 应用程序而设计。这个“spring mvc项目后端源码”包含了实现此类应用程序的核心组件和配置。下面我们...

Global site tag (gtag.js) - Google Analytics