过滤器(Filter)和拦截器(Interceptor)

一、引言

在面向对象编程(OOP)的过程中,很容易通过继承、多态来解决纵向扩展。但对于横向的功能,如登记所有的客户端请求耗时、统一开启事务等功能,OOP 无能为力。面向切面编程(AOP)的编程思想是对 OOP 的一个补充,本篇所讨论的过滤器和拦截器都属于 AOP 的具体实现。

二、过滤器 Filter

过滤器(Filter)的预处理发生在请求进入容器后,未进入 Servlet 之前。相应的,其后处理发生在 Servlet 处理完成后,返回给前端之前。所以过滤器的 doFilter(ServletRequest request, ServletResponse response, FilterChain chain)的入参是ServletRequest,而不是httpservletrequest。因为过滤器是在httpservlet之前。

@Override
public void init(FilterConfig arg0) throws ServletException {   
}

@Override 
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        log.info("before...");
        chain.doFilter(request, response);
        log.info("after...");
}

@Override
public void destroy() { 
}

FilterServlet都是由容器负责创建和销毁的。在一个应用程序中,一个Filter只会被创建和销毁一次。web 应用程序启动时,容器调用public void init(FilterConfig filterConfig) throws ServletException方法初始化Filter;web 应用程序被移除或者是容器关闭时,调用public void destroy()销毁Filter

Filter中声明的doFilter(ServletRequest request, ServletResponse response, FilterChain chain)方法,用于实现具体的过滤逻辑。其中FilterChain chain参数是一个过滤链对象,它包含了用户定义的一系列过滤器,这些过滤器根据其定义顺序依次被执行。通过执行chain.doFilter(request, response)方法可以跳过当前过滤器处理逻辑,执行过滤链中的下一个过滤器。事实上调用ServletdoService()方法是在chain.doFilter(request, response)这个方法中进行的。

三、拦截器 Interceptor

通过继承HandlerInterceptorAdapter类并重写下列三个方法可以快速的实现自定义拦截器:

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {     
    return true;    
}

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {     
}

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {     
} 

上述方法分别实现了拦截器的预处理preHanlde、后处理postHandle(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理afterCompletion(已经渲染了页面)。

preHandle()方法实现了拦截器的预处理。如果存在多个拦截器,则会依次调用拦截器链中每一个拦截器的preHandle()方法:

  1. preHandle方法返回false时,DispatcherServlet处理器认为拦截器已经处理完了请求,不再继续执行链中的其它拦截器和处理器,而是从当前拦截器往回执行所有拦截器的afterCompletion方法,退出拦截器链
  2. preHandle方法全为true时,执行下一个拦截器,直到所有拦截器执行完。再运行被拦截的 Controller。然后返回拦截器链,运行所有拦截器的postHandle方法,然后从最后一个拦截器往回执行所有拦截器的afterCompletion方法
  3. 当有拦截器抛出异常时,会从当前拦截器往回执行所有拦截器的afterCompletion方法

四、过滤器和拦截器的对比

二者在功能上很相似,其主要区别在于:

  1. 适用范围:Filter 依赖于 Servlet 容器,属于 Servlet 规范的一部分,只能用于 Web 程序;而拦截器是独立存在的,由 Spring 框架支持,可以用于 Web 程序、Application、Swing 程序中
  2. 作用范围:Filter 的执行由 Servlet 容器回调完成,过滤逻辑只能发生在 Servlet 调用前后;而拦截器基于 Java 反射机制,通常通过动态代理的方式来执行,能够深入到方法的前后、异常抛出的前后等,使用起来更加灵活
  3. 可支配的资源:拦截器属于Spring 组件,是通过 IoC 容器来管理,它能通过依赖注入的方式调用 Spring 里的任何资源、对象,例如 Service 对象、数据源、事务管理等;而 Filter 做不到,Filter 的生命周期由 Servlet 容器管理

五、运用场景

拦截器的主要应用场景有:

  1. 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
  2. 权限检查:如登录检测,进入处理器检测用户是否登录,如没有则跳转到登录页面;
  3. 性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
  4. 通用行为:读取 cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取 Locale、Theme 信息等,只要是多个处理器都需要的即可使用拦截器实现。
  5. OpenSessionInView:如 hibernate,在进入处理器打开Session,在完成后关闭Session。

过滤器通常用于:

  • 在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改 HttpServletRequest 的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符等。

六、过滤器和拦截器的执行顺序

综上所述可知,Filter的执行顺序在Interceptor之前。一图胜千言:假设自定义了2个过滤器TestFilter1TestFilter2,2个拦截器TestInterceptorBaseInterceptor,其执行流程可能如下:

执行顺序

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342