美文网首页
过滤器(Filter)和拦截器(Interceptor)

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

作者: 大鱼炖海棠 | 来源:发表于2019-08-12 20:34 被阅读0次

一、引言

在面向对象编程(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,其执行流程可能如下:

执行顺序

相关文章

网友评论

      本文标题:过滤器(Filter)和拦截器(Interceptor)

      本文链接:https://www.haomeiwen.com/subject/pwhpjctx.html