SpringBoot中filter的使用詳解及原理


  首先還是老生常談,我先把SpringBoot中filter的使用示例寫出來,然后再解釋下代碼、說一下運行的順序,最后講一下filter的原理(其實就是責任鏈設計模式,從馬士兵老師那里偷來的。。。)。

        要想使用filter,需要寫一個方法繼承Filter類,我們寫如下兩個自己的Filter類,首先是FirstFilter類,其中@Order里邊的數字越小代表越先被該Filter過濾,@WebFilter代表這是個Filter類並把這個類注入到容器中:

 

package com.example.executor_test.filter;
 
import java.io.IOException;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
 
import org.springframework.core.annotation.Order;
 
@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 1");
        chain.doFilter(request, response);
        System.out.println("first filter 2");
    }
 
    @Override
    public void destroy() {
        
    }
}

 然后是第二個Filter,SecondFilter類:

package com.example.executor_test.filter;
 
import java.io.IOException;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
 
import org.springframework.core.annotation.Order;
 
@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() {
        
    }
}

然后我們把Controller類也寫出來吧:

package com.example.executor_test.controller;
 
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
 
import javax.annotation.Resource;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
import com.example.executor_test.task.OldTask;
import com.example.executor_test.task.OldTaskThread;
 
@RestController
public class TestController {
    
    @GetMapping("/test1")
    public String test1() {
        System.out.println("method in controller");
        return "test1";
    }
 
}

    最后是springboot的主方法入口,注意,由於我們使用注解注入的Filter,所以要在下邊這個Application類中加入@ServletComponentScan注解:

package com.example.executor_test;
 
import org.omg.CORBA.PRIVATE_MEMBER;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.ConfigurableApplicationContext;
 
import com.example.executor_test.task.OldTaskThread;
 
@SpringBootApplication
@ServletComponentScan
public class ExecutorTestApplication {
 
    public static void main(String[] args) {    
        ConfigurableApplicationContext applicationContext = SpringApplication.run(ExecutorTestApplication.class, args);
    }
}

  首先我們先來看一下執行結果,啟動后訪問127.0.0.1:8080/test1,在后台中打印如下信息:

 

 

 我們可以看出代碼執行的流程,首先請求被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)之后寫你的邏輯。

        接下來講一下這個Filter和FilterChain都是怎么用責任鏈模式實現的,如果不太需要了解原理的話,往下的部分就可以不看了。。。好了,我們來模擬一下簡單的實現SpringBoot中的Filter接口和FilterChain類:

首先是我們自己寫的Filter接口,里邊就一個doFilter方法:

 

package filterchain_pattern;

public interface Filter {

public void doFilter(Request request, Response response, FilterChain chain);

}

接下來是我們自己寫的FilterChain類:

  1.  
    package filterchain_pattern;
  2.  
     
  3.  
    import java.util.ArrayList;
  4.  
    import java.util.List;
  5.  
     
  6.  
    public class FilterChain implements Filter {
  7.  
     
  8.  
    private List<Filter> filters = new ArrayList<>();
  9.  
    int index = 0;
  10.  
     
  11.  
    public FilterChain addFilter(Filter filter) {
  12.  
    filters.add(filter);
  13.  
    return this;
  14.  
    }
  15.  
     
  16.  
    @Override
  17.  
    public void doFilter(Request request, Response response, FilterChain chain) {
  18.  
     
  19.  
    if(index == filters.size()) {
  20.  
    return;
  21.  
    }
  22.  
    Filter filter = filters.get(index);
  23.  
    index++;
  24.  
    filter.doFilter(request, response, chain);
  25.  
    }
  26.  
     
  27.  
    }

接下來模擬Request類和Response類:

  1.  
    package filterchain_pattern;
  2.  
     
  3.  
    public class Request {
  4.  
     
  5.  
    public String requestStr;
  6.  
     
  7.  
    }
  1.  
    package filterchain_pattern;
  2.  
     
  3.  
    public class Response {
  4.  
     
  5.  
    public String responseStr;
  6.  
     
  7.  
    }

然后我們下一個Filter接口的實現類HTMLFilter類,該類會將requestStr中的<>替換成[],並給responseStr添加------------HTML response filter字符串,並在控制台打印出來:

  1.  
    package filterchain_pattern;
  2.  
     
  3.  
    public class HTMLFilter implements Filter {
  4.  
     
  5.  
    @Override
  6.  
    public void doFilter(Request request, Response response, FilterChain chain) {
  7.  
    request.requestStr = request.requestStr.replace( "<", "[").replace(">", "]") + "--------HTML Request Filter";
  8.  
    System.out.println( "HTML Filter request Str:" + request.requestStr);
  9.  
    chain.doFilter(request, response, chain);
  10.  
    response.responseStr = response.responseStr + "-------------HTML response filter";
  11.  
    System.out.println( "HTML Filter response Str:" + response.responseStr);
  12.  
    }
  13.  
    }

然后是另外一個Filter接口的實現類SensitiveFilter類, 該類會給requestStr添加一段字符串,給responseStr添加一段字符串,並在控制台打印出來:

  1.  
    package filterchain_pattern;
  2.  
     
  3.  
    public class SensitiveFilter implements Filter {
  4.  
     
  5.  
    @Override
  6.  
    public void doFilter(Request request, Response response, FilterChain chain) {
  7.  
    request.requestStr = request.requestStr + "---------------Sensitive request Filter";
  8.  
    System.out.println( "sensitiveFilter request str:" + request.requestStr);
  9.  
    chain.doFilter(request, response, chain);
  10.  
    response.responseStr = response.responseStr + "---------------------sensitive response filter";
  11.  
    System.out.println( "sensitiveFilter response str:" + response.responseStr);
  12.  
    }
  13.  
     
  14.  
    }

最后使我們的Main方法類:

  1.  
    package filterchain_pattern;
  2.  
     
  3.  
    public class MainTest {
  4.  
     
  5.  
    public static void main(String[] args) {
  6.  
     
  7.  
    String msg = "<html>testMsg</html>";
  8.  
    Request request = new Request();
  9.  
    request.requestStr = msg;
  10.  
    Response response = new Response();
  11.  
    response.responseStr = "responseStr";
  12.  
     
  13.  
    FilterChain fc = new FilterChain();
  14.  
    fc.addFilter( new HTMLFilter()).addFilter(new SensitiveFilter());
  15.  
    fc.doFilter(request, response, fc);
  16.  
     
  17.  
    }
  18.  
     
  19.  
    }

打印結果如下,這就是責任鏈模式的實際應用了:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM