• 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.  
      }

    打印结果如下,这就是责任链模式的实际应用了:

  • 相关阅读:
    三种按键处理函数
    enum与typedef enum的用法
    PIC18F中断定时器
    .net core Ocelot+Consul实现网关及服务注册和服务发现
    wpf的优点
    ASP.NET Core Web API下事件驱动型架构的实现
    2020个人计划
    图解C#的值类型,引用类型,栈,堆,ref,out
    .NET Core中的一个接口多种实现的依赖注入与动态选择
    redis分布式锁深度剖析
  • 原文地址:https://www.cnblogs.com/huoqm/p/13877267.html
Copyright © 2020-2023  润新知