Spring系列之Filter and Interceptor

一、 Filter 过滤器

1. 什么是Filter 过滤器?
  • Filter 的含义
    Filter 过滤器是一个对象 ,它对请求资源(Servlet或静态内容),或者来自资源的响应,或两者执行过滤任务。
  • Filter 的过滤任务执行流程


    filter.png
  • Filter 的生命周期(来自google)


    Filter lifecycle.png
  • 过滤器特点
    1. Servlet过滤器可能检查和修改ServletRequest和ServletResponse对象
    2. 可以指定Servlet过滤器和特定的URL关联,只有当客户请求访问此URL时,才会触发该过滤器工作
    3. 多个Servlet过滤器可以被串联起来,形成管道效应,协同修改请求和响应对象
    4. 所有支持Java Servlet规范2.3的Servlet容器,都支持Servlet过滤器
  • Filter 的应用场景
    1. 认证过滤器
    2. 记录和审核过滤器
    3. 图像转换滤镜
    4. 数据压缩过滤器
    5. 加密过滤器
    6. 标记化过滤器
    7. 触发资源访问事件的过滤器
    8. Mime-type 过滤器
  • Filter 类型
    1. REQUEST:默认值,代表直接访问某个资源时执行filter
    2. FORWARD:转发时才执行filter
    3. INCLUDE: 包含资源时执行filter
    4. ERROR:发生错误时 进行跳转时执行filter
  • 接口定义
public interface Filter {

     /**
     * 这是Servlet过滤器的初始化方法,Servlet容器创建Servlet过滤器实例后就会调用这个方法。
     * 在这个方法中可以通过FilterConfig来读取web.xml文件中Servlet过滤器的初始化参数。
     * 注意:在Filter 被创建到销毁,只会执行 init 方法一次
     * @param FilterConfig : Filter 配置参数
     *                   public String getFilterName();//获取过滤器名称
     *                   public ServletContext getServletContext();//获取Servlet容器
     *                  public String getInitParameter(String name);//获取初始化参数
     *                  public Enumeration<String> getInitParameterNames();//获取全部的初始化参数
     */
    public default void init(FilterConfig filterConfig) throws ServletException {}


     /**
     * 这是完成实际的过滤操作的方法,当客户请求访问与过滤器关联的URL时,Servlet容器先调用该方法。
     * FilterChain参数用来访问后续的过滤器的doFilter()方法。
     * @param request  请求
     * @param response 响应
     * @param chain    Filter过滤链对象
     *
     */
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    /**
     * Servlet容器在销毁过滤器实例前调用该方法,在这个方法中,可以释放过滤器占用的资源。
     */
    public default void destroy() {}
}
2. 如何定义Filter
  • 第一步:定义Filter
public class HelloFilter implements Filter {
    private FilterConfig filterConfig;
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }
    public void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        System.out.println("HelloFilter 执行过滤");
        //执行下一个
        chain.doFilter(request, response);
    }
    public void destroy() {
    }
}
  • 第二步:注册到FilterChain中
    1. 在web.xml中注册
<filter>
        <filter-name>HelloFilter</filter-name>
        <filter-class>cn.hdj.filter.HelloFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HelloFilter</filter-name>
        <!--
            url 规则定义:
            /* 匹配所有请求链接
            /user/*  匹配/user/路径所有请求链接
            *.extension  匹配以extension为后缀的请求
        -->
        <url-pattern>/*</url-pattern>
        <!-- Filter 类型-->
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
  1. Servlet 3.0后新增了注解(@WebFilter)支持
    不用在web.xml中定义,Spring 项目需要添加注解@ServletComponentScan,用于扫描Servlet3.0 支持的@WebFilter, @WebServlet, @WebListener等注解,加入IOC容器
@WebFilter(
      //初始化参数
      //@WebInitParam
      initParams = {
              @WebInitParam(name = "name", value = "HelloWorld")
      },
      urlPatterns = "/*",
      dispatcherTypes = DispatcherType.REQUEST,
       //异步支持
      asyncSupported = true
)
public class AnnotationHelloFilter implements Filter {



  private FilterConfig filterConfig;

  public void init(FilterConfig filterConfig) throws ServletException {
      this.filterConfig = filterConfig;
  }

  public void doFilter(ServletRequest request,
                       ServletResponse response,
                       FilterChain chain) throws IOException, ServletException {

      System.out.println("AnnotationHelloFilter 执行过滤; ");
      System.out.println("初始化参数" + this.filterConfig.getInitParameter("name"));
      //执行下一个
      chain.doFilter(request, response);
  }

  public void destroy() {
  }
}
  1. 在Spring 中 使用 FilterRegistrationBean 注册
//把过滤器交给IOC 容器管理
@Bean
public OneFilter oneFilter() {
    return new OneFilter();
}
/**
 * 注册过滤器到Servlet FilterChain中
 */
@Bean
public FilterRegistrationBean FilterRegistrationBean(OneFilter oneFilter) {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    filterRegistrationBean.setFilter(oneFilter);
    //设置true 自动注册到Servlet 过滤链中,
    //false ,则该Filter 不会添加到过滤器链中,只是普通Bean
    //具体源码看(SpringBoot 2.1.8):(其中还有很多步骤,主要顺序是这三个步骤)
    //1. SpringApplication.run(Class<?> primarySource, String... args)
    //...
    //2. ServletWebServerApplicationContext.refresh()
    //...
    //3. RegistrationBean(FilterRegistrationBean 的父类) onStartup(ServletContext servletContext)
    filterRegistrationBean.setEnabled(true); 
    filterRegistrationBean.addUrlPatterns("/*");//拦截的请求
    filterRegistrationBean.setOrder(1);//执行顺序
    return filterRegistrationBean;
}

二、Interceptor 拦截器

1. 什么是Interceptor ?
  • 了解Interceptor 需要了解SpringMVC 是如何处理请求的? 看如下图 SpringMVC 处理请求流程(来源google)


    Interceptor.png
  • 拦截器的处理过程(包括多个)


    interceptor chain1.png
interceptor chain2.png
  • 从以上的图示中可以看出, Interceptor 拦截器用来在处理Controller 时进行相关拦截操作,如检查授权、参数验证等。
2. 如何使用Interceptor ?
  • 拦截器接口HandlerInterceptor
public interface HandlerInterceptor {
    //预处理,在HandlerMapping 找到相应处理的Controller 后执行,但在HandlerAdapter 执行Controller 前调用
   default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
         throws Exception {
      return true;
   }
   //在执行controller 之后,但在视图渲染前执行
   default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable ModelAndView modelAndView) throws Exception {
   }
   //在请求处理完成后,即视图渲染完后执行
//注意: 只有preHandle方法处理完成并返回true 才会执行afterCompletion方法
   default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
         @Nullable Exception ex) throws Exception {
   }
}
  • 实现HandlerInterceptor 接口,自定义拦截器
public class LogInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("LogInterceptor.preHandle ==> url:" + request.getRequestURI());
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("LogInterceptor.postHandle ==> handler:" + handler.toString());
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("LogInterceptor.afterCompletion");
    }
}
  • 注册
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

   @Bean
    public LogInterceptor logInterceptor(){
        return new LogInterceptor();
    }
    /**
     * 添加自定义拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logInterceptor())//添加拦截器, 这里实例化bean 可以由Spring 管理
                .addPathPatterns("/*") //拦截uri
                .order(1); //执行顺序
    }
}

三、Filter 和Interceptor 的区别

1. 使用范围不同:

Filter是Servlet规范规定的,只能用于Web程序中。而拦截器既可以用于Web程序,也可以用于Application、Swing程序中。

2. 规范不同:

Filter是在Servlet规范中定义的,是Servlet容器支持的。而拦截器是在Spring容器内的,是Spring框架支持的。

3. 操作资源不同:

Filter在过滤是只能对request和response进行操作,而interceptor可以对request、response、handler、modelAndView、exception进行操作。

4. 执行顺序不同:
Filter_Interceptor.png

四、参考

1.http://www.mkjava.com/tutorial/filter-vs-interceptor/
2.https://tomcat.apache.org/tomcat-8.0-doc/servletapi/javax/servlet/Filter.html
3.https://www.iteye.com/blog/jinnianshilongnian-1670856
4.https://o7planning.org/en/11229/spring-mvc-interceptors-tutorial#a4748118
5.https://ixyzero.com/blog/archives/3855.html
6.拦截器机制——《跟我学Shiro》

五、源码

https://github.com/h-dj/Spring-Learning

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

推荐阅读更多精彩内容