天天看點

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   

繼續閱讀