banner
田野放空

田野放空

认真扮演一个擅长白日做梦的普通人

What is the execution order of filters in Java?

Questions:#

  1. When is the filterChain executed?
  2. Where do the filters in the filterChain come from?
  3. When does the standardContext start collecting the filter collection?

1. When is the filterChain executed?#

In the org.apache.catalina.core.StandardWrapperValve class, there is a method as follows:

public final void invoke(Request request, Response response)
        throws IOException, ServletException {

				// Omitted large amounts of unrelated code
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
				// Omitted large amounts of unrelated code
        try {
            if (!unavailable) {
              	// 1. create servlet
                servlet = wrapper.allocate();
            }
        } catch(){
          ...
        }
        // 2. Create the filter chain for this request
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

        Container container = this.container;
        try {
            if ((servlet != null) && (filterChain != null)) {
                // Swallow output if needed
                if (context.getSwallowOutput()) {
                    try {
                        SystemLogHandler.startCapture();
                        if (request.isAsyncDispatching()) {
                            request.getAsyncContextInternal().doInternalDispatch();
                        } else {
                          	// execute doFilter method of the FilterChain object.
                            filterChain.doFilter(request.getRequest(),
                                    response.getResponse());
                        }
                    } finally {
                      
                    }
                } else {
                    if (request.isAsyncDispatching()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else {
                        filterChain.doFilter
                            (request.getRequest(), response.getResponse());
                    }
                }

            }
        } catch(){
        } finally {
            // Release the filter chain (if any) for this request
            if (filterChain != null) {
                filterChain.release();
            }

            // Deallocate the allocated servlet instance
            try {
                if (servlet != null) {
                    wrapper.deallocate(servlet);
                }
            }
        }
    }

We will look back at how it creates the filter chain object later; first, let's see how it executes the filter chain. The execution flow of the filter chain object is implemented as follows:

public final class ApplicationFilterChain implements FilterChain {
   // Current executing filter index
   private int pos = 0;
   // Matched filter count 
   private int n = 0;
   // Servlet 
   private Servlet servlet = null;
   // Matched filter arrays
   private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];

   @Override
   public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

    		// Security has been turned on
        if( Globals.IS_SECURITY_ENABLED ) {
          // ... security bean filter
        } else {
          	// Execute filter logic 
            internalDoFilter(request,response);
        }
    }

    private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {
      
      	// Loop and call the next filter if there is one
        if (pos < n) {
          	// pos index ++
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();
                if( Globals.IS_SECURITY_ENABLED ) {
                } else {
                  	// Execute the doFilter() of the current filter in the filter chain
                    filter.doFilter(request, response, this);
                }
            } catch(){
              ...
            }
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
        try {
            if ((request instanceof HttpServletRequest) &&
                    (response instanceof HttpServletResponse) &&
                    Globals.IS_SECURITY_ENABLED ) {
            } else {
              	// If there are no filters to be executed, the servlet's service method will be executed, which contains the business logic that we have written.
                servlet.service(request, response);
            }
        } catch(){
          ...
        } finally{
          ...
        }
    }
}

From the source code of the filterChain class, it can be seen that the underlying structure contains the matched filter array, meaning that the added matching filter objects are ordered, which is determined at the time of addition.

So when is it added?

2. Where do the filters in the filterChain come from?#

In fact, in the org.apache.catalina.core.StandardWrapperValve class's invoke method:

ApplicationFilterChain filterChain =
               ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
    public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {

        // If there is no servlet to execute, return null
        if (servlet == null)
            return null;

        // Create and initialize a filter chain object
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                // Security: Do not recycle
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // Request dispatcher in use
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

        // Acquire the filter mappings for this Context
        StandardContext context = (StandardContext) wrapper.getParent();
      	// Get an array of all the filters registered in the ServletContext object
        FilterMap filterMaps[] = context.findFilterMaps();

        // If there are no filter mappings, we are done
        if ((filterMaps == null) || (filterMaps.length == 0))
            return filterChain;

        // Acquire the information we will need to match filter mappings
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }

        String servletName = wrapper.getName();

        // Add the relevant path-mapped filters to this filter chain
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // Add filters that match on servlet name second
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // Return the completed filter chain
        return filterChain;
    }

It can be seen that when creating the filterChain object, it retrieves all registered filters from the ServletContext and adds the necessary ones to the filterChain object created for this request. Moreover, the registered filters in the servletContext object are themselves an array (which is inherently ordered), so the matching during traversal is also ordered.

3. When does the ServletContext start collecting the array?#

The filterMaps are in the StandardContext class:

public class StandardContext extends ContainerBase
        implements Context, NotificationEmitter {

  private final ContextFilterMaps filterMaps = new ContextFilterMaps();
  
  //
  @Override
    public void addFilterMap(FilterMap filterMap) {
        validateFilterMap(filterMap);
        // Add this filter mapping to our registered set
        filterMaps.add(filterMap);
        fireContainerEvent("addFilterMap", filterMap);
    }

}

When starting Tomcat, we can check the call order through the stack:

StandardContext.startInternal() --> fireLifecycleEvent() --> ContextConfig.lifecycleEvent() --> ContextConfig.lifecycleEventcon --> ContextConfig.con.figureStart() --> ContextConfig.webConfig()

Looking at the method:

protected void webConfig() {
      if (ok) {
          configureContext(webXml);
      }
}

private void configureContext(WebXml webxml) {
      for (FilterMap filterMap : webxml.getFilterMappings()) {
          context.addFilterMap(filterMap);
      }
}

Here, the web.xml is used to collect all filter mappings into a set, and then the collected filter set is converted into an array and set to the StandardContext object.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.