Spring Boot中filter的使用详解及原理

作者 | SUPERUSR
来源 | urlify.cn/nIrqyu
首先还是老生常谈 , 我先把SpringBoot中filter的使用示例写出来 , 然后再解释下代码、说一下运行的顺序 , 最后讲一下filter的原理(其实就是责任链设计模式 , 从马士兵老师那里偷来的 。。。 ) 。
【Spring Boot中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 {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic 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");}@Overridepublic 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 {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic 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");}@Overridepublic 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; @RestControllerpublic 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@ServletComponentScanpublic class ExecutorTestApplication {public static void main(String[] args) {ConfigurableApplicationContext applicationContext = SpringApplication.run(ExecutorTestApplication.class, args);}} 首先我们先来看一下执行结果 , 启动后访问127.0.0.1:8080/test1 , 在后台中打印如下信息:
Spring Boot中filter的使用详解及原理文章插图
我们可以看出代码执行的流程 , 首先请求被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中 。