Spring Boot - Filter执行链路

文章目录

      • FilterChain源码链路分析
        • Servlet执行细节
      • 设置Filter执行顺序

在SpringBoot工程中使用filter进行过滤需要进行的步骤

  1. 自定义一个Filter类,实现Filter接口
  2. 在FilterConfiguration类(加了@Configuration的注解)中注册Filter,加上@Bean注解并在FilterRegistrationBean中设置Filter的优先级
  3. Filter接口中有三个方法,doFilter()是真正实现过滤的地方;其中方法参数之一的filterChain是工程Filter的一个执行链路代理,在进行过滤操作之后执行的filterChain.doFilter()方法会把request请求发往后续的Filter或者是servlet。

注:Filter可以实现的功能部分也可由Interceptor来实现

Filter和Interceptor区别

Filter能够顺序执行就是因为FilterChain的存在,每一个filter执行filterChain.doFilter()的时候都会把filterChain对象传入,然后交给下一个filter,目的就是保证每一个filter都可以持有filterChain,直到把所有的filter都执行完。在执行完servlet之后还会倒序返回到各个filter中。Filter的生命周期没有特别声明时同servlet一样。
Spring Boot - Filter执行链路_第1张图片

FilterChain源码链路分析

首先debug查看工程的chain,包含的有

  YouCan-filter
->httpmonitor-filter
->SetCharacterEncoding
->YouCanCommonFilter
->YouCanApp-filter
->log4j2-filter
->rhinoControl-filter
->set-proxy-filter
->applicationContextIdFilter
->Jetty_WebSocketUpgradeFilter
->dispatcherServlet@7ef5559e==org.springframework.web.servlet.DispatcherServlet,jsp=null,order=-1,inst=true

工程使用的是jetty容器,分析一下jetty中对于Filter的执行处理,

首先是javax.servlet-api中的Filter接口和FilterChain接口

public interface Filter {

    public void init(FilterConfig filterConfig) throws ServletException;
	
   /** 
   * 1.doFilter中都是使用chain.doFilter把执行流交给FilterChain来管理;doFilter之前的部分都是处理之前的过滤操作,之后的部分都是处理之后的过滤操作
   * 2.自定义Filter中重点关注的部分就是doFilter方法,关乎了过滤的意义
   */
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
            throws IOException, ServletException;
  
    public void destroy();
}

public interface FilterChain {
	
    public void doFilter ( ServletRequest request, ServletResponse response ) throws IOException, ServletException;

}

接着是Jetty中的CacheChain,springBoot使用了jetty容器(默认tomcat),因此FilterChain是由它来实现的

protected class CachedChain implements FilterChain {
  	// 当前filter的持有者
    FilterHolder _filterHolder;
  	// 指向下一个filter,没有则为null
    ServletHandler.CachedChain _next;
    // servlet处理的持有者
    ServletHolder _servletHolder;

    protected CachedChain(List<FilterHolder> filters, ServletHolder servletHolder) {
      // 初始化的时候就递归确认各filter的holder
      if (filters.size() > 0) {
        this._filterHolder = (FilterHolder)filters.get(0);
        filters.remove(0); 
        this._next = ServletHandler.this.new CachedChain(filters, servletHolder);
      } else {
        // 初始化时没有filter则直接把执行流交给相应的servlet
        this._servletHolder = servletHolder;
      }

    }

    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
      Request baseRequest = Request.getBaseRequest(request);
      // filterHolder是在Filterchain中每一个filter被轮询时的所有者,当filterHolder为null时,表示chain中的filter都已经被轮询了
      if (this._filterHolder != null) {
        if (ServletHandler.LOG.isDebugEnabled()) {
          ServletHandler.LOG.debug("call filter {}", new Object[]{this._filterHolder});
        }

        Filter filter = this._filterHolder.getFilter();
        // 判断baseRequest是否加锁以及当前filter的执行者是否未加锁
        if (baseRequest.isAsyncSupported() && !this._filterHolder.isAsyncSupported()) {
          try {
            baseRequest.setAsyncSupported(false, this._filterHolder.toString());
            // 执行流交给下一个filter执行者
            filter.doFilter(request, response, this._next);
          } finally {
            baseRequest.setAsyncSupported(true, (String)null);
          }
        } else {
          // 执行流交给下一个filter执行者
          filter.doFilter(request, response, this._next);
        }

      } else {
        // 执行到此表示所有的filter都已经执行完了,将句柄交给Servlet持有者
        HttpServletRequest srequest = (HttpServletRequest)request;
        if (this._servletHolder == null) {
          ServletHandler.this.notFound(baseRequest, srequest, (HttpServletResponse)response);
        } else {
          if (ServletHandler.LOG.isDebugEnabled()) {
            ServletHandler.LOG.debug("call servlet " + this._servletHolder, new Object[0]);
          }
					// serletk执行者开始处理请求
          this._servletHolder.handle(baseRequest, request, response);
        }

      }
    }

Servlet执行细节

执行方法:

ServletHolder.handle() ->

FrameworkServlet.service() -> FrameworkServlet.processRequest()

DispatcherServlet.doService() -> DispatcherServlet.doDispatch()

执行涉及的类:

Dispatcherervlet -> HandlerExecutionChain -> RequestMappingHandlerAdapter -> ServletInvocableHandlerMethod -> InvocableHandlerMethod

  • Dispatcherervlet.getHandler()从handlerMappings中遍历找到RequestMappingHandlerMapping,接着在AbstractHandlerMethodMapping根据请求request中的url到合适的HandlerMethod;其中通过获取请求url比对在mappingRegistry存储的工程中相关的url映射找到最满足对比结果的Controller类。后续会获取InterceptorList以便于执行Interceptor中的过滤逻辑。

  • 真正执行Controller中的方法是在InvocableHandlerMethod.invokeForRequest()中的RequestMappingHandlerMapping.doInvoke()中的Bean.invoke()使用反射去执行。

  • 执行的结果放在returnValue中,在返回Handler中找到RequestResponseBodyMethodProcessor,接着一层一层的回调到Interceptor和FilterChain上一层中,直到走完所有的Filter。

设置Filter执行顺序

在工程中有显示的定义filter的执行优先级,保证想要实现的过滤效果可控的。在加了@Configuration的注解的FilterConfiguration中进行设置。

  • 自定义的方法需要加上@Bean注解,返回值需要是FilterRegistrationBean对象,并且把自定义的Filter类当做入参传入FilterRegistrationBean的构造方法中进行Filter注册。
private static final int TRACE_FILTER_ORDER = 1;@Bean
  public FilterRegistrationBean traceFilter() {
    TraceFilter traceFilter = new TraceFilter();
    FilterRegistrationBean registration = new FilterRegistrationBean(traceFilter);
    registration.addUrlPatterns("/*");
    registration.setName("traceFilter");
    // 这里设置Filter执行的优先级,数字越小,优先级越高
    registration.setOrder(TRACE_FILTER_ORDER);
    return registration;
  }

但是需要注意的是这里注册的filter优先级并不是真正意义上定义的顺序,debug可以发现在执行traceFilter之前还有SpringBoot自带的一些Filter,它们是Springboot启动依赖的starter自带的一些filter,执行固定配置中的一些默认筛选规则。

如何自定义的Filter执行的优先级要先于框架自带的Filter呢?

在设置filter执行优先级的时候将其设置为

// int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; -2147483648
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);

这样做理论上可以使自定义的Filter是所有Filter中第一个被执行的。为什么是理论上的,是因为这个参数有可能已经被其他Filter设置过了,进行了覆盖。

具体原理是在web容器启动的时候,会调用一个匿名类去加载整个容器内的ServletContetxInitializerBeans,此时所有的filter也会进行加载,接着在for循环中进行排序,其中getOrderedBeansOfType()这个方法就获取到了各个Bean的order,像filter这种Bean就会依赖order去排序加载执行。

private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
    for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(
            beanFactory, ServletContextInitializer.class)) {
        addServletContextInitializerBean(initializerBean.getKey(),
                initializerBean.getValue(), beanFactory);
    }
}

你可能感兴趣的:(SpringBoot,Filter,Java,SpringBoot,Filter)