首先還是老生常談,我先把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類:
-
package filterchain_pattern;
-
-
import java.util.ArrayList;
-
import java.util.List;
-
-
public class FilterChain implements Filter {
-
-
private List<Filter> filters = new ArrayList<>();
-
int index = 0;
-
-
public FilterChain addFilter(Filter filter) {
-
filters.add(filter);
-
return this;
-
}
-
-
-
public void doFilter(Request request, Response response, FilterChain chain) {
-
-
if(index == filters.size()) {
-
return;
-
}
-
Filter filter = filters.get(index);
-
index++;
-
filter.doFilter(request, response, chain);
-
}
-
-
}
接下來模擬Request類和Response類:
-
package filterchain_pattern;
-
-
public class Request {
-
-
public String requestStr;
-
-
}
-
package filterchain_pattern;
-
-
public class Response {
-
-
public String responseStr;
-
-
}
然后我們下一個Filter接口的實現類HTMLFilter類,該類會將requestStr中的<>替換成[],並給responseStr添加------------HTML response filter字符串,並在控制台打印出來:
-
package filterchain_pattern;
-
-
public class HTMLFilter implements Filter {
-
-
-
public void doFilter(Request request, Response response, FilterChain chain) {
-
request.requestStr = request.requestStr.replace( "<", "[").replace(">", "]") + "--------HTML Request Filter";
-
System.out.println( "HTML Filter request Str:" + request.requestStr);
-
chain.doFilter(request, response, chain);
-
response.responseStr = response.responseStr + "-------------HTML response filter";
-
System.out.println( "HTML Filter response Str:" + response.responseStr);
-
}
-
}
然后是另外一個Filter接口的實現類SensitiveFilter類, 該類會給requestStr添加一段字符串,給responseStr添加一段字符串,並在控制台打印出來:
-
package filterchain_pattern;
-
-
public class SensitiveFilter implements Filter {
-
-
-
public void doFilter(Request request, Response response, FilterChain chain) {
-
request.requestStr = request.requestStr + "---------------Sensitive request Filter";
-
System.out.println( "sensitiveFilter request str:" + request.requestStr);
-
chain.doFilter(request, response, chain);
-
response.responseStr = response.responseStr + "---------------------sensitive response filter";
-
System.out.println( "sensitiveFilter response str:" + response.responseStr);
-
}
-
-
}
最后使我們的Main方法類:
-
package filterchain_pattern;
-
-
public class MainTest {
-
-
public static void main(String[] args) {
-
-
String msg = "<html>testMsg</html>";
-
Request request = new Request();
-
request.requestStr = msg;
-
Response response = new Response();
-
response.responseStr = "responseStr";
-
-
FilterChain fc = new FilterChain();
-
fc.addFilter( new HTMLFilter()).addFilter(new SensitiveFilter());
-
fc.doFilter(request, response, fc);
-
-
}
-
-
}
打印結果如下,這就是責任鏈模式的實際應用了: