zoukankan      html  css  js  c++  java
  • SpingBoot FilterRegistrationBean 注册组件 | FilterChain 责任链源码分析

    概述
    SpringBoot 摒弃了繁琐的 xml 配置的同时,提供了几种注册组件:ServletRegistrationBean,
    FilterRegistrationBean,ServletListenerRegistrationBean,DelegatingFilterProxyRegistrationBean,用于注册自对应的组件,如过滤器,监听器等。

    本篇来分析过滤器注册组件FilterRegistrationBean,理解实现原理,有助于平时开发遇到对应的问题,能够快速的分析和定位。
    内容涉及以下几点:

    FilterRegistrationBean加载机制
    FilterChain责任链构造方式
    自定义FilterChain
    一 FilterRegistrationBean 加载机制
    先来看一下该类 uml:

    Springboot 1.x 版本:

    Springboot 2.x 版本:

     

    首先,ServletContextInitializer是 Servlet 容器初始化的时候,提供的初始化接口。FilterRegistrationBean最终实现了ServletContextInitializer,所以,Servlet 容器初始化会获取并触发所有的FilterRegistrationBean实例化。
    两个版本中变化不是很大,只是SpringBoot 2.x版本中,将AbstractFilterRegistrationBean中的注册逻辑提取到DynamicRegistrationBean抽象类中。

    来看一下源码。
    Spring 刷新容器会执行onRefresh:

     跟进该方法:

    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();
        }

    上述首先获取当前 Servlet 容器类型,本篇以 Jetty 为例进行分析。
    上面有一个参数比较重要:
    this.webServer = factory.getWebServer(getSelfInitializer());
    这里传入了一个回调函数 getSelfInitializer():

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

    这是用来获取所有的ServletContextInitializer并实例化的回调函数,什么时候触发呢?

     当容器启动时,会执行callInitializers,通过onStartup会触发回调函数。回调函数是定义在ServletWebServerApplicationContext中的selfInitialize方法,跟进该方法:

    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());
            //这里便是获取所有的 ServletContextInitializer 实现类,会获取所有的注册组件
            for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                beans.onStartup(servletContext);
            }
        }

    跟进上面的getServletContextInitializerBeans方法:

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

    ServletContextInitializerBeans对象是对ServletContextInitializer的一种包装,构造函数如下:

    public ServletContextInitializerBeans(ListableBeanFactory beanFactory) {
            this.initializers = new LinkedMultiValueMap<>();
            //获取所有的 ServletContextInitializer
            addServletContextInitializerBeans(beanFactory);
            addAdaptableBeans(beanFactory);
            List<ServletContextInitializer> sortedInitializers = new ArrayList<>();
            //监听器,过滤器,以及 servlet的排序逻辑
            this.initializers.values().forEach((contextInitializers) -> {
                AnnotationAwareOrderComparator.sort(contextInitializers);
                sortedInitializers.addAll(contextInitializers);
            });
            this.sortedList = Collections.unmodifiableList(sortedInitializers);
        }

    可以看到其构造函数中执行了addServletContextInitializerBeans方法,该方法传入了 beanFactory,也就是从容器中获取所有的ServletContextInitializer,并进行实例化,然后进行排序。来看看具体是如何获取的?

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

    addServletContextInitializerBean方法会判断具体实现类的类型,也就是开头提到的几种注册组件:

    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);
            }
        }

    上述逻辑主要是对容器中获取的ServletContextInitializer实现类进行分类,存入对应的组件集合当中。以此实现各自组件的功能。

    继续主流程,来看一下过滤器的注册逻辑。如下图:

     上述方法获取所有的ServletContextInitializer,进行循环注册,跟进onStartup方法:

     RegistrationBean类提供了一个模板方法:register,对应的注册组件执行各自的注册逻辑。这里来看一下过滤器注册组件的实现:

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

    上述方法获取过滤器,并通过ServletContext注入到Servlet容器中,继续跟进addFilter方法:

    @Override
            public FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass)
            {
                //......
                final ServletHandler handler = ServletContextHandler.this.getServletHandler();
                //判断filter是否已注册
                FilterHolder holder = handler.getFilter(filterName);
                if (holder == null)
                {
                    //new filter
                    //创建一个新的holder,注入到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
            }

    至此,自定义的 Filter 就注入到了 Servlet 容器中。

    注意:ServletWebServerApplicationContext 是SpringBoot 2.x版本中的命名,对应的是1.x版本中的EmbeddedWebApplicationContext。

    二 FilterChain责任链构造方式

    FilterChain 采用了责任链模式,也是责任链模式的一种典型使用方式。类似于 Pipeline 模式。

    Jetty中的 FilterChain 对象默认是懒加载的形式,只有第一次请求进来的时候才会初始化,如下图:

     请求进来,首先会判断_filterMappings是否为空,不为空则获取FilterChain对象。
    继续来看getFilterChain方法:

    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;
            }
    
           //如果未获取到,则构造一个FilterChain对象
            FilterChain chain = null;
            //判断是否开启了缓存
            if (_filterChainsCached)
            {
                if (filters.size() > 0)
                    chain= new CachedChain(filters, servletHolder);
    
                final Map<String,FilterChain> cache=_chainCache[dispatch];
                final Queue<String> lru=_chainLRU[dispatch];
    
                    // Do we have too many cached chains?
                    //判断缓存中是否有了太多的FilterChain,如果大于最大长度,进行删除。
                    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 缓存的功能,以 URL为key,每次请求进来,根据 URL 获取对应的过滤器链。
    另外实现了 LRU 算法,当缓存长度超过最大限度时,清理掉最早未使用的键值对。

    但是很多请求下,不同的 URL 获取的过滤器链是一样的,所以这里没必要开启缓存。Jetty提供了_filterChainsCached进行设置,上述代码也是通过此变量进行判断。
    默认为 true,默认使用了缓存。

    需要注意一点:只有开启 FilterChain 缓存,创建CachedChain对象,才会采用责任链模式。
    如果创建的是Chain对象,则直接遍历所有过滤器处理。

    来看一下CachedChain构造方法,责任链相关代码:

    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;
            }

    代码比较简洁,对构造方法进行递归处理,创建CachedChain链表,最终生成的对象如下形式:

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

    三 自定义 FilterChain

    以下实例代码提供了两种方式创建FilterChain,构造方法递归实现和普通方法递归实现。

    定义一个 Filter接口:

    public interface MyFilter {
    
        String getName();
        void execute(FilterChain filterChain);
    }

    定义两个实现类:

    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 对象:

    @Data
    public class FilterChain {
    
        private MyFilter currentFilter;
        private FilterChain next;
        private List<MyFilter> filters;
    
    
        /**
         *
         * 递归实现责任链
         */
        public FilterChain(MyFilter myFilter) {
            this.currentFilter = myFilter;
        }
    
    
        /**
         *
         * 模拟 SpringBoot Jetty 中的 fiterChain 责任链实现机制
         */
        public FilterChain(List<MyFilter> filters) {
            if (filters.size() > 0) {
                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);
            }
        }
    }

    通过构造方法递归实现:

    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);
        }
    }

    通过普通方法递归实现:

    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

    总结
    SpringBoot 在加载 Servlet 容器时,会获取扩展接口ServletContextInitializer的所有实现类。过滤器,监听器等注册组件正是实现了该接口,从而完成了对应各自注册的机制。另外过滤器链采用了 LRU 算法实现了缓存机制,并通过在 FilterChain 构造方法中递归实现了责任链机制。
    原文链接:https://blog.csdn.net/woshilijiuyi/article/details/85014183

  • 相关阅读:
    simian 查找项目中的重复代码
    idea 启动 ShardingProxy
    kafka 加密通信,python-kafka 访问加密服务器
    apt-get 修改源
    短轮询、长轮询、SSE 和 WebSocket
    前端模块化:CommonJS,AMD,CMD,ES6
    Set、Weak Set、Map、Weak Map学习笔记
    博客园应该如何运营
    Vue中Route的对象参数和常用props传参
    Dapper的新实践,Dapper.Contrib的使用与扩展
  • 原文地址:https://www.cnblogs.com/nizuimeiabc1/p/12542490.html
Copyright © 2011-2022 走看看