`
zachary.guo
  • 浏览: 482559 次
  • 性别: 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  

相关推荐

    spring mvc 拦截器获取请求数据信息.rar

    spring mvc 拦截器获取请求数据信息 解压之后放到项目中 直接运行就可以了 (将流多次运用)

    Spring MVC 学习笔记

    2、 DispatcherServlet把请求转交给HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象.(后面会学习到拦截器) 3、 ...

    java之spring mvc之拦截器.docx

    1. springmvc 中的拦截器是由实现 HandlerInterceptor 或者继承 HandlerInterceptorAdapter 来实现的。 2. 自定义实现一个拦截器的步骤: a). 定义一个实现 HandlerInterceptor 接口 的类

    spring-mvc.zip

    简易的SpringMVC框架,主要Jar :spring-webmvc 、fastjson,其中包含Controller 、HandlerInterceptor 、HandlerExceptionResolver 的实现

    基于java的企业级应用开发:拦截器.ppt

    15.1 拦截器概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理。例如通过拦截器可以进行权限验证、记录请求信息的日志、判断用户是否登录等。 ...

    Spring-Reference_zh_CN(Spring中文参考手册)

    2.5.1. Spring MVC的表单标签库 2.5.2. Spring MVC合理的默认值 2.5.3. Portlet 框架 2.6. 其他特性 2.6.1. 动态语言支持 2.6.2. JMX 2.6 .3. 任务规划 2.6.4. 对Java 5(Tiger)的支持 2.7. 移植到Spring 2.0 ...

    spring3mvc入门资料

    HandlerMapping接口 HandlerAdapter接口 Controller接口 HandlerInterceptor 接口 View接口 LocalResolver接口HandlerExceptionResolver接口 ModelAndView类 。

    Spring中文帮助文档

    6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...

    使用Spring MVC拦截器实现日志记录的方法

    本篇文章主要介绍了使用Spring MVC拦截器实现日志记录的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    Spring 2.0 开发参考手册

    2.5.1. Spring MVC的表单标签库 2.5.2. Spring MVC合理的默认值 2.5.3. Portlet 框架 2.6. 其他特性 2.6.1. 动态语言支持 2.6.2. JMX 2.6.3. 任务规划 2.6.4. 对Java 5(Tiger)的支持 2.7. 移植到Spring ...

    spring chm文档

    6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ Load-time weaving(LTW) 6.9. ...

    Spring API

    2.5.1. Spring MVC合理的默认值 2.5.2. Portlet 框架 2.5.3. 基于Annotation的控制器 2.5.4. Spring MVC的表单标签库 2.5.5. 对Tiles 2 支持 2.5.6. 对JSF 1.2支持 2.5.7. JAX-WS支持 2.6. 其他 2.6.1. 动态...

    SpringInterceptors

    就像我们拥有Struts2拦截器一样,我们可以在Spring中创建自己的拦截器,方法是实现org.springframework.web.servlet.HandlerInterceptor接口或重写抽象类org.springframework.web.servlet.handler....

    springMVC拦截器HandlerInterceptor用法代码示例

    主要介绍了springMVC拦截器HandlerInterceptor用法代码示例,具有一定借鉴价值,需要的朋友可以参考下

    spring-framework-reference-4.1.2

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    spring-framework-reference4.1.4

    3. New Features and Enhancements in Spring Framework 4.0 ............................................ 17 3.1. Improved Getting Started Experience .........................................................

    spring-method-interceptor:Spring Handler Interceptor反思Web方法

    弹簧方法拦截器 该示例显示了一个HandlerInterceptor来拦截Web调用并注销注释值。 此示例使用启动2,唯一的区别是您将使用WebMvcConfigurer注册拦截器。

Global site tag (gtag.js) - Google Analytics