天天看点

SpringBoot2.X学习之Filter过滤器

1.spring应用中存在两种过滤用法,一种是拦截器,另一种是过滤器,两者的作用其实相差并不大

2.filter功能:它使用户可以改变一个request和修改一个response.Filter不是一个servlet,它不能产生一个response,它能够在

一个request到达servlet之前预处理request,也可以在离开servlet时处理response.

3.过滤器与拦截器的区别:

       Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。

  Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理的方式来执行。

  Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。

这里我们就对SpringBoot的过滤器进行实战讲解:

一.SpringBoot自带的Filter以及加载顺序

SpringBoot启动默认会加载下面几个filter,我们可以在启动日志里面看到:

SpringBoot2.X学习之Filter过滤器

characterEncodingFilter

hiddenHttpMethodFilter

httpPutFormContentFilter

requestContextFilter

这几个过滤器都是SpringBoot启动之后进行加载的,我们自己添加的Filter也会跟在这几个后面。过滤器加载是有优先级的,毕竟他有那么多Filter,到底哪个排在前面呢,SpringBoot是根据order这个字段进行判断过滤器优先级的,我们可以查看源码:

SpringBoot2.X学习之Filter过滤器

那么过滤器的一个优先级范围就是在 -2147483648与 2147483647之间,低位值意味着更高的优先级:Higher values are interpreted as lower priority。

二.自定义Filter

自定义实现filter有两种方式,一个是实现FilterRegistrationBean,另外一个是使用

Servlet3.0

新增的注解实现过滤器

1.使用FilterRegistrationBean实现过滤器

首先新建一个过滤器类Filter1实现Filter接口,这里我们就打印了一下请求的路径:

package com.qzsun.springbootdemo.filter;

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

public class Filter1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String requestURI = request.getRequestURI();
        System.out.println("-----------------"+requestURI+"----------------");
        filterChain.doFilter(request, servletResponse);
    }

    @Override
    public void destroy() {

    }
}
           

然后在启动类里面配置一个bean,把刚刚新建的一个过滤器加进去并设置优先级,这样一个自定义filter就实现了

package com.qzsun.springbootdemo;

import com.qzsun.springbootdemo.filter.Filter1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SpringbootdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootdemoApplication.class, args);
    }

    @Bean
    public FilterRegistrationBean testFilterRegistration() {

        FilterRegistrationBean registration = new FilterRegistrationBean();
        //当过滤器有注入其他bean类时,可直接通过@bean的方式进行实体类过滤器,这样不可自动注入过滤器使用的其他bean类。
        //当然,若无其他bean需要获取时,可直接new Filter1(),也可使用getBean的方式。
        registration.setFilter(new Filter1());
        //拦截路径
        registration.addUrlPatterns("/*");
        registration.addInitParameter("paramName", "paramValue");
        //过滤器名称
        registration.setName("filter1");
        //设置顺序
        registration.setOrder(10);
        return registration;
    }
}

           

在indexController中加一个访问方法:

@RequestMapping("/index")
    public String index(){
        System.out.println("热部署1");
        System.out.println("热部署2");
        System.out.println("热部署3");
        System.out.println("测试配置文件的值"+webTest);
        return "index";
    }
           

我们启动并访问发现过滤器生效,如果我们需要配置多个过滤器,就注册多个FilterRegistrationBean就行了。

SpringBoot2.X学习之Filter过滤器
SpringBoot2.X学习之Filter过滤器

2.使用Servlet3.0的注解进行配置 (推荐)

2.1新建一个Filter2类实现Filter接口,实现三个方法init(),doFilter(),destroy(),进行登录的验证,这里要加上@WebFilter注解,目的是将该FIlter加入Spring容器的管理,这里可以通过urlPatterns 进行路径的拦截,filterName配置filter的名字,这样的话就比上面一种方法方便多了,不需要配置bean了,@Order注解就是设置过滤器的优先级

package com.qzsun.springbootdemo.filter;

import org.springframework.core.annotation.Order;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Order(9)
@WebFilter(urlPatterns = "/api/*",filterName = "Filter2")
public class Filter2 implements Filter {
    /**
     * 容器加载的时候调用
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("-------------过滤器初始化-------------");
    }

    /**
     * 请求被拦截时调用
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        String requestURI = request.getRequestURI();
        String username = request.getParameter("username");
        int status = response.getStatus();
        System.out.println("-----------请求URL"+requestURI+"用户名"+username);
        System.out.println("-----------返回状态"+status);
        if (username!=null){
            filterChain.doFilter(request, servletResponse);
        }else {
            return;
        }
    }

    /**
     * 容器被销毁时调用
     */
    @Override
    public void destroy() {
        System.out.println("-------------过滤器销毁-------------");
    }
}
           

2.1.1:init()是filter初始化,在容器启动的时候会调用这个方法,

2.1.2:doFilter()是一个最重要的方法,当请求经过filter的时候进行业务,比如判断一个人时候登录,登陆的话就调用filterChain.doFilter(request, servletResponse)放行,没有登录就重定向或者跳转,在这里我们可以拿到请求过来的request,也可以拿到返回数据时候的response,还是非常有用的

2.1.3:destroy()是在容器销毁的时候进行回调,一般关掉容器的时候是看不到这个方法的打印的,只有热部署的时候能看到,因为热部署的时候要销毁这个filter进行重新创建(下面会进行验证)(不了解热部署的可以看我之前的博文https://blog.csdn.net/qq_33355821/article/details/86616794)

2.2在启动类上加上@ServletComponentScan注解,这个主要作用是Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。

SpringBoot2.X学习之Filter过滤器

2.3在indexController中加上一个方法进行测试:

@RequestMapping("/api/index")
    public String apiIndex(){
        return "index";
    }
           

启动发现过滤器已经初始化了:

SpringBoot2.X学习之Filter过滤器

 访问http://localhost:8081/api/index,不加username,过滤器也是起作用的,走了return跳出,什么都没有返回

SpringBoot2.X学习之Filter过滤器
SpringBoot2.X学习之Filter过滤器

下面我们修改刚刚的apiIndex()方法,返回一个json数据,然后在访问链接上加上?username=zhangsan,然后我们改动trigger.txt的版本号进行热部署,发现走了destroy()方法,销毁了Filter,并且filter也放行了,正常返回数据!

@RequestMapping("/api/index")
    @ResponseBody
    public Map<String, Object> apiIndex(){
        Map<String,Object> map = new HashMap<>();
        map.put("name","zhangsan");
        map.put("age","111");
        return map;
    }
           
SpringBoot2.X学习之Filter过滤器
SpringBoot2.X学习之Filter过滤器

filter场景:权限控制、用户登录等,主要适用于非前后端分离的场景

源码地址:https://gitee.com/xuxinsunqizheng/SpringBoot2.0.git   

继续阅读