Spring MVC 同样拦截器。

来源:互联网 发布:俄罗斯社交软件vk 编辑:程序博客网 时间:2024/06/05 03:33

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

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);        }        ......      }  

老规矩,先看看 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 都应用这些拦截器(链)不就完了嘛。

其实,我也不得不这样去想,但我们经常碰到的情况是:

在我们定义的 handler 中,都要检查 session 是否存在,这个拦截器定义在 HanlerMapping 中无可厚非。可是,总有那么几个 handler 在处理请求时不需要 session,怎么办?

即便我把那几个按非常规处理的 handler 剥离出来,不和按常规处理请求的 handler 用同一个 HandlerMapping,这几个 handler 用其它的 HandlerMapping 来做映射。OK,这样的确解决了,请继续往下看。

即便如此,再来怪一点的需求:这几个特殊的 handler 假若有 10 个,它们要分别应用 10 个不同的拦截器,咋搞?

再来个更怪异点的:即便对于一个 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 了, 采取 , 为某个 url 的请求单独应用拦截器,或者为所有 Controller 应用同样的拦截器。这种

0 0
原创粉丝点击