SpringBoot_Filter的详解

SpringBoot&Filter的详解

过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理 通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理

大概流程图如下

应用场景

  • 自动登录

  • 统一设置编码格式

  • 访问权限控制

  • 敏感字符过滤等

  • ......


1、Filter的使用

  要想使用filter,需要写一个方法继承Filter类,我们写如下两个自己的Filter类,首先是FirstFilter类,其中@Order里边的数字越小代表越先被该Filter过滤,@WebFilter代表这是个Filter类并把这个类注入到容器中。

注意:

    SpringBoot 实现 Filter 两种方式:

  1. 自定义Filter通过FilterRegistrationBean 类来注入;(不推荐)

  2. 自定义Filter通过 实现接口 Filter方法,@WebFilter注入。

FirstFilter

@Order(1)
@WebFilter(filterName="firstFilter", urlPatterns="/*")
public class FirstFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("first filter 1111111111111111111");
        chain.doFilter(request, response);
        System.out.println("first filter 2222222222222222222");
    }

    @Override
    public void destroy() {

    }
}

SecondFilter

@Order(2)
@WebFilter(filterName="secondFilter", urlPatterns="/*")
public class SecondFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("second filter 1=============================");
        System.out.println("before:" + response);
        chain.doFilter(request, response);
        System.out.println("after:" + response);
        System.out.println("second filter 2=============================");

    }

    @Override
    public void destroy() {

    }
}

TestController

@RestController
public class TestController {

    @GetMapping("/test1")
    public String test1() {
        System.out.println("method in controller");
        return "test1";
    }

}

postman测试:http://localhost:8080/test1

输出结果:

first filter 1111111111111111111
second filter 1=============================
before:org.apache.catalina.connector.ResponseFacade@1d2aafd8
method in controller
after:org.apache.catalina.connector.ResponseFacade@1d2aafd8
second filter 2=============================
first filter 2222222222222222222

过程:

总结:

  我们可以看出代码执行的流程,首先请求被firstfilter截获,打印出first filter 1,然后去执行chain.doFilter(request, response),这句话代表着请求会转发给过滤器链上下一个对象,也就是secondfilter,所以打印出secondfilter里的second filter 1,接下来再执行secondfilter里的chain.dofilter()方法,请求再转发给下一个对象,由于没有其他的filter了,所以会转发给controller,打印出了controller类中的method in controller,接下来再去内存栈里调用secondfilter的print("second filter 2"),然后再去内存栈里调用firstfilter的print("first filter 1")。所以如果在自己实现的Filter类的doFilter方法里不加chain.doFilter(req, rep)是万万不行的,那样会导致请求到了这个filter里就不再往下走了,永远进不了controller中。

  我们也可以在print("before:" + response)和print("after:" + response)这两个地方打上断点,然后调试一下,你会发现在before那里的response里是什么都么有的,而在after那里的response里则是已经有了test1字符串,也就是说controller类test1方法的返回值已经添加进了response,所以如果你想对请求的response做一下过滤处理,那么一定要在chain.doFilter(res, rep)之后写你的逻辑。


2、@WebFilter注解说明

  @WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 )

@WebFilter 的常用属性

属性名类型说 明
filterName String 指定过滤器的 name 属性,等价与 <filter-name> 标签
value String[] 该属性等价于 <url-pattern> 标签,但是两者不应该同时使用
urlPatterns String[] 指定一组过滤器的 URL 匹配模式。等价于 <url-pattern> 标签。
servletNames String[] 用于指定过滤的servlet。取值是 @WebServlet 中的 name 属性的取值,或者是 web.xml 中 <servlet-name> 的取值。
dispatcherTypes DispatcherType[] 指定过滤器的转发模式。具体取值包括:ASYNC、ERROR、FORWARD、INCLUDE、REQUEST
initParams WebInitParam[] 用于设置该过滤器类的一些初始化参数,等同于<init-param> 标签
asyncSupported Boolean 声明过滤器是否支持异步操作模式,等价于 <async supported> 标签
description String 对该过滤器的描述信息,等价于 <description> 标签
displayName String 过滤器显示名,通常配合工具使用,等同与<display-name> 标签

3、责任链模式的实际应用

Filter和FilterChain都是怎么用责任链模式实现的,自定义实现一下, 使用Filter模式来模拟

Filter接口

public interface Filter {
    /**
     * 过滤器
     * @param request 请求对象
     * @param response 返回对象
     */
    public void doFilter(Request request, Response response, FilterChain chain);

}

FilterChain类 { 过滤器链 }

/**
 * @Desc: TODO 过滤器链【执行责任链模式的主要成员】
 */
public class FilterChain implements Filter {

    /**
     * 过滤器列表
     */
    private List<Filter> filters = new ArrayList<>();

    int index = 0;

    /**
     * 添加过滤器
     * @param filter
     */
    public FilterChain addFilter(Filter filter) {
        filters.add(filter);
        return this;
    }

    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        if(index == filters.size()) {
            /**
             * 真正处理请求(此时开始处理 controller业务逻辑)
             */
            return;
        }
        Filter filter = filters.get(index);
        index++;
        filter.doFilter(request, response, chain);
    }

}

Request类和Response类

public class Request {
    public String requestStr;
}
public class Response {
    public String responseStr;
}

实现类(业务中需要的过滤器,以下举例)

EncodeFilter{ 设置统一编码 }

public class EncodeFilter implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        request.requestStr = request.requestStr + "  设置编码 UTF-8";
        System.out.println("EncodeFilter request Str:" + request.requestStr);
        chain.doFilter(request, response, chain);
        response.responseStr = response.responseStr + "-------------设置编码 UTF-8";
        System.out.println("EncodeFilter response Str:" + response.responseStr);
    }
}

VerifyParameterFilter{ 参数校验 }

public class VerifyParameterFilter  implements Filter {
    @Override
    public void doFilter(Request request, Response response, FilterChain chain) {
        request.requestStr = request.requestStr + "  参数验证 成功";
        System.out.println("VerifyParameterFilter request str:" + request.requestStr);
        chain.doFilter(request, response, chain);
        response.responseStr = response.responseStr + "======================";
        System.out.println("VerifyParameterFilter response str:" + response.responseStr);
    }
}

测试

public class FilterTest {
    public static void main(String[] args) {
        String msg = "张三";
        Request request = new Request();
        request.requestStr = msg;
        Response response = new Response();
        response.responseStr = "success *****";

        FilterChain fc = new FilterChain();
        fc.addFilter(new EncodeFilter())
                .addFilter(new VerifyParameterFilter())
                .doFilter(request, response, fc);
    }
}

结果:

EncodeFilter request Str:张三  设置编码 UTF-8
VerifyParameterFilter request str:张三  设置编码 UTF-8  参数验证 成功
VerifyParameterFilter response str:success *****======================
EncodeFilter response Str:success *****======================-------------设置编码 UTF-8

总结

  1. Filter是针对请求进行拦截、意在请求前进行一些过滤、权限校验、日志记录、统一编码等

  2. 所有Filter统一在FilterChain中组成一个链条,然后调用也统一由FilterChain来协调,确保以链条的模式执行

  3. 责任链模式是一种重要的设计模式,如Servlet中的Filter模式、Mybatis中的Plugin模式等都是责任链模式的体现

posted @ 2021-10-28 14:50  梅子猪  阅读(357)  评论(0编辑  收藏  举报