过滤器Filter的使用

前言

我们学习Servlet的时候,过滤器Filter是绕不过,而且较为重要的一个知识点。Filter随web应用启动而启动的,只初始化一次,以后就可以拦截相关的请求,只有当你的web应用停止或重新部署的时候才能销毁。在实际项目中,我们常常使用过滤器来对过滤字符编码或者做一些业务逻辑判断。

这篇文章,将对如何应用Filter,并且对过滤器的执行流程进行讲解,希望对过滤器不了解的学习者一个参考。

一、什么是过滤器

过滤器 Filter,是在 Servlet 规范中定义的,是 Servlet 容器支持的,该接口定义在 javax.servlet包下,主要是在客户端请求(HttpServletRequest)进行预处理,以及对服务器响应(HttpServletResponse)进行后处理。
我们要自定义过滤器,需要实现Filter 接口。



该接口一共有三个方法

  • init 方法,在自定义Filter初始化的时候调用
  • doFilter,每次匹配到用户请求,都会先经过这个方法,执行doFilter放行后,客户端的请求才能到servlet上。
  • destroy 自定义Filter被销毁的时候调用

二、自定义过滤器

定义过滤器有两种方式,第一种可以通过注解定义,第二种需要在创建过滤器后,在web.xml文件上配置过滤器。为了更好地对过滤器的拦截顺序进行讲解,这里的话会创建两个过滤器。

package com.xiaoming.util;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
@Order(1)
@WebFilter(urlPatterns = "/user/*",filterName = "filter1")
public class MyFilter1 implements Filter {

    //  在Filter初始化的时候调用
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter1 has been initialized ...");
    }


    /** 每个用户请求都会调用到这个方法,校验通过则doFilter放行到下一个过滤器
     *  等到请求通过所有过滤链上的校验后,才能到达servlet
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("doFilter1开始执行,对"+((HttpServletRequest)servletRequest).getRequestURL().toString()+" 进行过滤 ");
        System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
        System.out.println("doFilter1执行结束");
    }

    // Filter对象被销毁的时候调用,注意,执行该方法后会在调用一次dofilter
    public void destroy() {
        System.out.println("MyFilter1 has been destroyed...");
    }
}

过滤器1

package com.xiaoming.util;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
@Order(2)
@WebFilter(filterName = "filter2",urlPatterns = "/user/*")
public class MyFilter2 implements Filter {

    //  在Filter初始化的时候调用
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter2 has been initialized ...");
    }


    /** 每个用户请求都会调用到这个方法,校验通过则doFilter放行到下一个过滤器
     *  等到请求通过所有过滤链上的校验后,才能到达servlet
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("doFilter2开始执行,对"+((HttpServletRequest)servletRequest).getRequestURL().toString()+" 进行过滤 ");
        System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("检验接口是否被调用,尝试获取contentType如下: " + servletResponse.getContentType());
        System.out.println("doFilter2执行结束");
    }

    // Filter对象被销毁的时候调用,注意,执行该方法后会在调用一次dofilter
    public void destroy() {
        System.out.println("MyFilter2 has been destroyed...");
    }
}

过滤器2
这里对使用到的注解进行一个简单的介绍

  • @Component 让spring接管这个bean
  • @Order 执行顺序,数字越小,越早执行
  • @WebFilter
    • filterName 过滤器的名称
    • urlPatterns 过滤的url规则
@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping(value = "/save",method = RequestMethod.GET)
    public String save(){
        System.out.println("user save ...");
    return "success";
    }
}

将项目部署到tomcat后,访问http://localhost:8088/user/save,得到结果如下


我们可以发现,过滤器的执行顺序是
过滤器1.doFilter() -> 过滤器2.doFilter() -> controller方法调用 -> 过滤器2.doFilter()结束 -> 过滤器2.doFilter()结束。我们可以用下图来更好的理解这个过程

Servlet在Filter的里层,所有到servlet的请求都必须先经过过滤器Filter,而过滤器往往有多个,组成了过滤链,Filter执行doFilter()的话就将请求放行到下一个过滤器。
本案例的话,Filter1比过滤器的级别要高,调用顺序在Filter2之前,所以请求过来的时候,会先经过Filter1,再经过Filter2,请求结束后,根据堆栈的执行顺序,先执行Filter2.doFilter后面的代码,再执行Filter1剩余的代码。

另外,如果是采用配置文件的方式配置过滤器的话,需要在web.xml文件进行如下配置

    <filter>
        <filter-name>filter2</filter-name>
        <filter-class>com.xiaoming.util.MyFilter2</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>filter2</filter-name>
        <url-pattern>/user/*</url-pattern>
    </filter-mapping>
    <filter>
        <filter-name>filter1</filter-name>
        <filter-class>com.xiaoming.util.MyFilter1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>filter1</filter-name>
        <url-pattern>/user/*</url-pattern>
    </filter-mapping>

过滤器的执行顺序依照配置文件中定义的过滤器顺序来执行

参考文章:
https://zhuanlan.zhihu.com/p/69060111
https://zhuanlan.zhihu.com/p/161740475

posted @ 2020-10-25 15:00  moutory  阅读(12)  评论(0编辑  收藏  举报  来源