SpingBoot FilterRegistrationBean registered components | FilterChain source code analysis of the chain of responsibility

SUMMARY
SpringBoot abandoned while cumbersome xml configuration register provides several components: ServletRegistrationBean,
FilterRegistrationBean, ServletListenerRegistrationBean, DelegatingFilterProxyRegistrationBean, for registration from the corresponding components, such as filters, etc. listener.

Benpian to analyze the filter registration component FilterRegistrationBean, understand that principle, usually contribute to the development of corresponding problems encountered, can quickly analyze and positioning.
Covering the following points:

FilterRegistrationBean loading mechanism
FilterChain responsibility chain configuration mode
custom FilterChain
a loading mechanism FilterRegistrationBean
first look at the class uml:

Springboot 1.x version:

 

 

 

Springboot 2.x version:

 

 

 

 

First, ServletContextInitializer is initialized when the initialization of the interface Servlet container, provided. FilterRegistrationBean finally realized ServletContextInitializer, therefore, Servlet container and trigger the initialization will get all FilterRegistrationBean instantiated.
Two versions change is not great, but SpringBoot version 2.x, the AbstractFilterRegistrationBean the registration logic to extract DynamicRegistrationBean abstract class.

Look at the source code.
Spring refresh container performs onRefresh:

 

 

 Follow this method:

private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        if (webServer == null && servletContext == null) {
            //获取指定的 Servlet类型
            ServletWebServerFactory factory = getWebServerFactory();
            //指定 ServletContextInitializer 触发逻辑
            this.webServer = factory.getWebServer(getSelfInitializer());
        }
        else if (servletContext != null) {
            try {
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context",
                        ex);
            }
        }
        initPropertySources();
    }

First of all the above to get the current Servlet container type, this part to Jetty example for analysis.
There is a more important parameter above:
this.webServer = factory.getWebServer (getSelfInitializer ());
here passed a callback function getSelfInitializer ():

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
        return this::selfInitialize;
    }

This is used to get all ServletContextInitializerand instantiate callback function, when triggered it?

 

 

 When the container starts, execution callInitializers, by onStartuptrigger callback function. The callback function is defined in ServletWebServerApplicationContextthe selfInitializemethod, follow this method:

private void selfInitialize(ServletContext servletContext) throws ServletException {
        prepareWebApplicationContext(servletContext);
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
                beanFactory);
        WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
                getServletContext());
        existingScopes.restore();
        WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
                getServletContext());
        //Here is an overview of all of the implementation class ServletContextInitializer, gets all of the components registered 
        for (ServletContextInitializer Beans: getServletContextInitializerBeans ()) { 
            beans.onStartup (where servletContext); 
        } 
    }

Follow the above getServletContextInitializerBeansmethod:

protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
        return new ServletContextInitializerBeans(getBeanFactory());
    }

ServletContextInitializerBeansThe object is ServletContextInitializera package, constructor as follows:

public ServletContextInitializerBeans (ListableBeanFactory beanFactory) {
         the this .initializers = new new LinkedMultiValueMap <> ();
         // get all ServletContextInitializer 
        addServletContextInitializerBeans (beanFactory); 
        addAdaptableBeans (beanFactory); 
        List <ServletContextInitializer> = sortedInitializers new new the ArrayList <> ();
         // Listener , filter, and servlet logic sorting 
        the this .initializers.values () forEach ((contextInitializers) ->. {  
            AnnotationAwareOrderComparator.sort (contextInitializers);
            sortedInitializers.addAll (contextInitializers); 
        }); 
        the this .sortedList = Collections.unmodifiableList(sortedInitializers);
    }

Can see the constructor is performed addServletContextInitializerBeans, the method was introduced to beanFactory, all that is acquired from the container ServletContextInitializer, and instantiated, and then sorted. Look at how specific is acquired?

private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
        //指定ServletContextInitializer.class 类型
        for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(
                beanFactory, ServletContextInitializer.class)) {
            //添加到具体的集合中
            addServletContextInitializerBean(initializerBean.getKey(),
                    initializerBean.getValue(), beanFactory);
        }
    }

addServletContextInitializerBeanThe method determines the type of the specific implementation class, i.e. several registered components mentioned in the introduction:

private void addServletContextInitializerBean(String beanName,
            ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
        // Servlet注册组件
        if (initializer instanceof ServletRegistrationBean) {
            Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
            addServletContextInitializerBean(Servlet.class, beanName, initializer,
                    beanFactory, source);
        }
        //过滤器注册组件
        else if (initializer instanceof FilterRegistrationBean) {
            Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
            addServletContextInitializerBean(Filter.class, beanName, initializer,
                    beanFactory, source);
        }
        else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
            String source = ((DelegatingFilterProxyRegistrationBean) initializer)
                    .getTargetBeanName();
            addServletContextInitializerBean(Filter.class, beanName, initializer,
                    beanFactory, source);
        }
        else if (initializer instanceof ServletListenerRegistrationBean) {
            EventListener source = ((ServletListenerRegistrationBean<?>) initializer)
                    .getListener();
            addServletContextInitializerBean(EventListener.class, beanName, initializer,
                    beanFactory, source);
        }
        else {
            addServletContextInitializerBean(ServletContextInitializer.class, beanName,
                    initializer, beanFactory, initializer);
        }
    }

Said logic is mainly acquired in container ServletContextInitializerimplementation class classifying them into a corresponding set of components. In order to realize the function of each component.

Continue the main flow, look at the registration logic filter. As shown below:

 

 

 Get all of the above methods ServletContextInitializer, circulated registration, follow-up onStartupmethods:

 

 

 RegistrationBeanClass provides a template method: registercorresponding register component to perform respective logical register. Here's look at the components implement filters registration:

@Override
    protected Dynamic addRegistration(String description, ServletContext servletContext) {
        Filter filter = getFilter();
        return servletContext.addFilter(getOrDeduceName(filter), filter);
    }

The method of obtaining the above-described filter, by ServletContextinjecting into the Servletcontainer, to follow up addFiltermethods:

@Override
         public FilterRegistration.Dynamic addFilter (<? FilterName String, Class the extends the Filter> filterClass) 
        { 
            // ...... 
            Final ServletHandler Handler = ServletContextHandler. The this .getServletHandler ();
             // determine whether the filter has been registered 
            FilterHolder holder = handler.getFilter (filterName);
             IF (Holder == null ) 
            { 
                // new new filter
                 // Create a new holder, is injected into the ServletHandler 
                Holder = handler.newFilterHolder (Source.JAVAX_API);
                holder.setName(filterName);
                //将filter设置到holder中
                holder.setFilter(filter);
                handler.addFilter(holder);
                return holder.getRegistration();
            }
            if (holder.getClassName()==null && holder.getHeldClass()==null)
            {
                //preliminary filter registration completion
                holder.setHeldClass(filterClass);
                return holder.getRegistration();
            }
            else
                return null; //existing filter
        }

Thus, custom Filter are injected into the Servlet container.

Note: ServletWebServerApplicationContext is SpringBoot 2.x version of the name, corresponding to the version 1.x EmbeddedWebApplicationContext.

Two FilterChain responsibility chain configuration mode

FilterChain using a chain of responsibility pattern, is a typical use chain of responsibility pattern. Similar to the Pipeline mode.

FilterChain objects Jetty is the default form of lazy loading, only the first request came in before initialization, as shown below:

 

 

 Request comes, will first determine _filterMappingswhether it is empty, not empty the acquisition FilterChaintarget.
Continuing with getFilterChainmethod:

protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder)
    {
        String key=pathInContext==null?servletHolder.getName():pathInContext;
        int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType());
        
           //通过 url,从缓存中获取 FilterChain
        if (_filterChainsCached && _chainCache!=null)
        {
            FilterChain chain = (FilterChain)_chainCache[dispatch].get(key);
            if (chain!=null)
                return chain;
        } 

       // if it is not acquired, the object is constructed a FilterChain 
        FilterChain catena alberghiera = null ;
         // determines whether to open the cache 
        IF (_filterChainsCached) 
        { 
            IF (filters.size ()> 0 ) 
                catena alberghiera = new new CachedChain (Filters, servletHolder); 

            Final the Map <String, FilterChain> cache = _chainCache [dispatch];
             Final Queue <String> the LRU = _chainLRU [dispatch]; 

                // ? have have the Do WE tOO mANY cached chains
                 // determine whether there was too much cache FilterChain, if greater than the maximum length, it is deleted. 
                while (_maxFilterChainsCacheSize>0 && cache.size()>=_maxFilterChainsCacheSize)
                {
                    // The LRU list is not atomic with the cache map, so be prepared to invalidate if
                    // a key is not found to delete.
                    // Delete by LRU (where U==created)
                    String k=lru.poll();
                    if (k==null)
                    {
                        cache.clear();
                        break;
                    }
                    cache.remove(k);
                }

                cache.put(key,chain);
                lru.add(key);
        }
        else if (filters.size() > 0)
            chain = new Chain(baseRequest,filters, servletHolder);

        return chain;
    }

Jetty FilterChain implements a cache function, a URL for the key, each time a request comes in to obtain the corresponding filter chain according to URL.
Further implements an LRU algorithm, the cache when the length exceeds the maximum, to clean out the unused first key-pair.

But many requests, get a different URL filter chain is the same, so there is no need to open the cache. Providing Jetty _filterChainsCached set, the above code is judged by this variable.
Default is true, use the default cache.

Point to note: only open FilterChain cache, create CachedChainobjects using only chain of responsibility pattern.
If you are creating Chainobjects directly through all the filter processing.

Look at the CachedChainconstruction method, the chain of responsibility relevant code:

CachedChain(List<FilterHolder> filters, ServletHolder servletHolder)
        {
            if (filters.size()>0)
            {
                _filterHolder=filters.get(0);
                filters.remove(0);
                //递归处理
                _next=new CachedChain(filters,servletHolder);
            }
            else
                _servletHolder=servletHolder;
        }

Code is relatively simple, the method is configured to perform a recursive process, creating CachedChaina linked list, to generate the final object of the form:

characterEncodingFilter->hiddenHttpMethodFilter->httpPutFormContentFilter->requestContextFilter->webRequestLoggingFilter->authenticationFilter->traceFilter->applicationContextIdFilter->Jetty_WebSocketUpgradeFilter

Three custom FilterChain

The following example code provides two ways to create FilterChain, construct and implement a common method recursive recursive implementation.

Define a Filter interface:

public interface MyFilter {

    String getName();
    void execute(FilterChain filterChain);
}

Defines two implementation classes:

public class MyFilters{

    /**
     * 定义两个个Myfilter
     *
     */
    public static class MyFilter1 implements  MyFilter{
        @Override
        public String getName() {
            return "myFilter1";
        }

        @Override
        public void execute(FilterChain filterChain) {
            System.out.println(getName()+"before...");
            if (null != filterChain) {
                filterChain.doFilter(filterChain);
            }
            System.out.println(getName()+"after...");
        }
    }


    public static class MyFilter2 implements  MyFilter{
        @Override
        public String getName() {
            return "myFilter2";
        }

        @Override
        public void execute(FilterChain filterChain) {
            System.out.println(getName()+"before...");
            if (null != filterChain) {
                filterChain.doFilter(filterChain);
            }
            System.out.println(getName()+"after...");
        }
    }
}

FilterChain objects:

@Data
 public  class the FilterChain { 

    Private the MyFilter currentFilter;
     Private the FilterChain Next;
     Private List <the MyFilter> Filters; 


    / ** 
     * 
     * recursive chain of responsibility 
     * / 
    public the FilterChain (the MyFilter myFilter) {
         the this .currentFilter = myFilter; 
    } 


    / ** 
     * 
     * fiterChain chain of responsibility mechanism implemented in analog SpringBoot Jetty 
     * / 
    public the FilterChain (List <the MyFilter> Filters) {
         IF (filters.size ()> 0 ) {
             the this .currentFilter filters.get = (0);
            filters.remove(0);
            this.next = new FilterChain(filters);
        }
    }

    public void doFilter(FilterChain filterChain) {
        MyFilter currentFilter = filterChain.getCurrentFilter();
        if (null != currentFilter) {
            currentFilter.execute(filterChain.next);
        }
    }
}

By a recursive construction method:

public class FilterChainBuilder {

    static List<MyFilter> filters;

    public static FilterChain buildFilterChainBuild(List<MyFilter> myFilters){
        filters = myFilters;
        return FilterChainInstanceFactory.FILTER_CHAIN;
    }

    private  static class FilterChainInstanceFactory{
        final static FilterChain FILTER_CHAIN = new FilterChain(filters);
    }
}

Recursive by ordinary methods:

public class FilterChainBuilder2 {

    public static FilterChain buildFilterChain(List<MyFilter> filters) {

        if (CollectionUtils.isEmpty(filters)) {
            return null;
        }
        MyFilter currentFilter = filters.get(0);
        FilterChain filterChain2 = new FilterChain(currentFilter);
        filters.remove(0);

        if (filters.size() > 0) {
            filterChain2.setNext(buildFilterChain(filters));
        }
        return filterChain2;
    }
}

具体代码 Github:
https://github.com/admin801122/springboot2-spring5-studying/tree/master/filterChain

 

 

Summary
SpringBoot when loading Servlet container implementation class gets all the expansion interface ServletContextInitializer. Filter, etc. listener registration component of the interface is achieved, thereby completing the registration of the respective corresponding mechanisms. Further filter chain uses a LRU algorithm to achieve a caching mechanism, and the chain of responsibility achieved by recursively FilterChain constructor mechanism.
Original link: https://blog.csdn.net/woshilijiuyi/article/details/85014183

Guess you like

Origin www.cnblogs.com/nizuimeiabc1/p/12542490.html