概述
[color=#345286] 在做 web 开发中,特别是使用 MVC 框架时,要是不谈谈拦截器这个概念,那可显示不出你的牛逼,o(∩_∩)o...哈哈!!!Struts2 中有拦截器,Spring MVC 同样也有拦截器。[/color]
[color=#345286] 在 [url="http://zachary-guo.iteye.com/blog/1296603"]Spring MVC 中的 HandlerAdaptor[/url] 这篇文章中,我提到过,HandlerMapping 的 getHandler(request) 方法返回的并不是用于处理请求的 handler,而是被包装过的 HandlerExecutionChain:[/color]
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);
}
......
}
[color=#345286] 正如上述代码所示,HandlerExecutionChain 中除了封装了用于处理请求的 handler,同时还包含了 HandlerInterceptor。如果我们从 HandlerInterceptor 所处的位置溯源而上(HandlerInterceptor → HandlerExecutionChain → HandlerMapping),则会发现 HandlerMapping 是其最终的发源地。因此,我们只需要将 interceptor 注入我们定义的 HandlerMapping 中即可。[/color]
[color=#345286] 老规矩,先看看 HandlerInterceptor 为何物:[/color]
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;
}
[color=#345286] 我们自己写一个拦截器,一般实现 HandlerInterceptor 接口的 preHandle 方法即可。但我为了要写一个拦截器,即便后两个接口方法我不去实现,但也得写个空实现,煞是不爽。继承 java 的光荣传统,搞一个 XXXAdaptor 就可以了,这个 XXXAdaptor 实现的方法全部为空实现,我们自己的拦截器继承此 XXXAdaptor 即可,想覆写哪个方法就覆写哪个。当然,Spring NVC 已经为我们准备好了这个 adaptor:org.springframework.web.servlet.handler.HandlerInterceptorAdapter。此 adaptor 是一个抽象类。[/color]
[color=#345286] 在学习 Spring MVC 的拦截器的概念时,我一直在想:为什么不把拦截器定义成 handler 的属性,各自的 handler 应用各自的 interceptor(s)?那你肯定会回答,定义上百个 handler,每个 handler 都注入 interceptor,累不累啊?肯定要定义在 HandlerMapping 里,使应用其 HandlerMapping 来映射的 handler 都应用这些拦截器(链)不就完了嘛。[/color]
[color=#345286] 其实,我也不得不这样去想,但我们经常碰到的情况是:[/color]
[list=1]
[*] [color=#345286]在我们定义的 handler 中,都要检查 session 是否存在,这个拦截器定义在 HanlerMapping 中无可厚非。可是,总有那么几个 handler 在处理请求时不需要 session,怎么办?[/color]
[*] [color=#345286]即便我把那几个按非常规处理的 handler 剥离出来,不和按常规处理请求的 handler 用同一个 HandlerMapping,这几个 handler 用其它的 HandlerMapping 来做映射。OK,这样的确解决了,请继续往下看。[/color]
[*] [color=#345286]即便如此,再来怪一点的需求:这几个特殊的 handler 假若有 10 个,它们要分别应用 10 个不同的拦截器,咋搞?[/color]
[*] [color=#345286]再来个更怪异点的:即便对于一个 handler 而言,通过这个 url 过来的请求要用到 session 检查的拦截器;通过另外一个 url 过来的请求不做 session 检查,那不是傻眼了吗?[/color]
[/list]
[color=#345286] 暂且不管我为什么提出上述的问题,如果正如我一开始说的那样,拦截器是定义在 handler 里的,属于 handler 的属性,那么,上面的问题就不是问题了(最后一个问题仍然无法解决,当然,你可以认为这是个无理的需求,不合理的需求)。[/color]
[color=#345286] 如果真的是一个 hanlder 就定义一个 interceptor(s),这是合理的软件设计理念吗?别忘了【二八原则】:花 20% 的精力去解决那 80% 的问题,往往很小的努力能解决很多类似的问题。那些特殊的问题也就不过 20%,尽管解决起来不那么容易,但毕竟出现这些问题的几率也不大。[/color]
[color=#345286] 将 interceptor(s) 定义在 HandlerMapping 里,正是 Spring MVC 所采取的策略,利用这种方式,我们来说说如何解决上面我提出的问题。首先说明的是,一般情况下我们使用 ControllerClassNameHandlerMapping 这一个 HandlerMapping 就足够了,毕竟它是约定优于配置的体现,但为了解决那 20% 的问题,我不得不使用 SimpleUrlHandlerMapping。[/color]
[color=#345286] [b]第一个问题[/b],将那些特殊的 handler 不采用 ControllerClassNameHandlerMapping 而采用 SimpleUrlHandlerMapping 来映射。只不过我们写 url 和 handler 的映射关系时,可以用 ControllerClassNameHandlerMapping 式的 url 来做匹配,这样既没有丧失约定优于配置的优点,又可以实现特殊的 handler 应用特殊的拦截器(将这些特殊用到的拦截器定义在 SimpleUrlHandlerMapping 里即可)。[/color]
[color=#345286] [b]第二/三个问题[/b],我们整 10 个 SimpleUrlHandlerMapping 来一一映射这 10 个特殊应用的 handler,然后将 10 个不同的拦截器分别注入到这 10 个 SimpleUrlHandlerMapping 即可。[/color]
[color=#345286] [b]第四个问题[/b],同解决第二/三个问题采取的思路一样,为某一个 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 都行,这就是我说的 [b]url 的映射不存在关联关系[/b]。[/color]
[color=#345286] 如此说来,Spring MVC 的 interceptor 机制,甚至都可以控制到 Controller 的方法级了,够细粒度了吧。还是那句话,发生这种情况的可能性只有 20%,也就是说,搞如此麻烦的配置的几率不大。这也终于解开了学习 interceptor 的结了。[/color]
[color=#345286] 这里,再插一句话,为什么会提出那四个问题,是因为我一直在 struts2 的环境下开发,上述的问题的确在实际业务上碰到了。Struts2 可以为单个 action 定义拦截器(链),既而产生了我最开始的疑问。细细一想,Struts2 真的是基于类而定义的拦截器吗,或者说,Struts 解决 80% 的 Action 要使用的拦截器是怎么做的?回忆一下,使用 Struts2 时,经常使用的 url 是什么?是不是 [b]/package/actionName.action[/b],这个能不能理解成是 Struts2 的 HandlerMapping,只不过它只有一个 HandlerMapping 而已。其余的,大家可以自由发挥地去思考。另外,第四个问题,同一个 action 对不同的 url 映射应用不同的拦截器,采取新闻生成的案例,或许不恰当。因为在做静态生成时,一般不会再发 http 请求,现在都采用模板技术了。这里之所以拿这个案例来说,因为这是个历史遗留问题了而已。[/color]
[color=#008000] [b]这篇文章旨在讲解原理,事实上,从 Spring 2.5 后,都是采用基于注解的 Controller 了, 采取 <mvc:interceptor>,<mvc:mapping path="/user/*" /> 为某个 url 的请求单独应用拦截器,或者为所有 Controller 应用同样的拦截器。这种 <mvc:...> 的方式来定义更为简洁,是,简单归简单,毕竟这只是表现形式而已。但是,万变不离其宗,不变的永远是其背后的原理![/b][/color]
最后
以上就是洁净纸鹤为你收集整理的Spring MVC 中的 HandlerInterceptor的全部内容,希望文章能够帮你解决Spring MVC 中的 HandlerInterceptor所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复