美文网首页js css html
Spring Filter深度解析

Spring Filter深度解析

作者: 晴天哥_王志 | 来源:发表于2022-06-16 07:02 被阅读0次

    Filter的用法

    public interface Filter {
        //初始化方法,整个生命周期中只执行一次。
        //在init方法成功(失败如抛异常等)执行完前,不能提供过滤服务。
        //参数FilterConfig用于获取初始化参数
        public void init(FilterConfig filterConfig) throws ServletException;
    
        //执行过滤任务的方法,参数FilterChain表示过滤器链,doFilter方法中只有执行chain.doFilter()后才能调用下一个过滤器的doFilter方法
        //才能将请求交经下一个Filter或Servlet执行
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;
    
        //销毁方法,当移出服务时由web容器调用。整个生命周期中destroy方法只会执行一次
        //destroy方法可用于释放持有的资源,如内存、文件句柄等
        public void destroy();
    }
    
    • Filter的接口定义包含init、doFilter、destroy等接口。
    @Component
    public class TimeFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("time filter init");
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("time filter start");
            long startTime = System.currentTimeMillis();
    
            filterChain.doFilter(servletRequest, servletResponse);
    
            long endTime = System.currentTimeMillis();
            System.out.println("time filter consume " + (endTime - startTime) + " ms");
            System.out.println("time filter end");
        }
    
        @Override
        public void destroy() {
            System.out.println("time filter init");
        }
    }
    
    • 自定义 Filter对象需要实现Filter的接口并实现其中的方法。

    Filter的初始化

    public class StandardContext extends ContainerBase
            implements Context, NotificationEmitter {
    
        private HashMap<String, FilterDef> filterDefs = new HashMap<>();
        private HashMap<String, ApplicationFilterConfig> filterConfigs = new HashMap<>();
    
        @Override
        protected synchronized void startInternal() throws LifecycleException {
                // 省略其他代码
                if (ok) {
                    if (!filterStart()) {
                        log.error(sm.getString("standardContext.filterFail"));
                        ok = false;
                    }
                }
        }
    
        public boolean filterStart() {
            boolean ok = true;
            synchronized (filterConfigs) {
                filterConfigs.clear();
                // 遍历filterDefs的map初始化Filter对象
                for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
                    String name = entry.getKey();
                    try {
                        ApplicationFilterConfig filterConfig =
                                new ApplicationFilterConfig(this, entry.getValue());
                        filterConfigs.put(name, filterConfig);
                    } catch (Throwable t) {
                        ok = false;
                    }
                }
            }
    
            return ok;
        }
    }
    
    • StandardContext#filterStart负责遍历filterDefs并创建ApplicationFilterConfig对象。
    • ApplicationFilterConfig是包含 Filter 实例的对象,FilterDef是包含的过滤器的定义。
    • StandardContext的filterDefs保存 Filter 的定义,filterConfigs保存 Filter 的实例包装对象ApplicationFilterConfig

    Filter核心类定义

    public class FilterDef implements Serializable {
    
        private static final long serialVersionUID = 1L;
        private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME);
    
        private String description = null;
        private String displayName = null;
        private transient Filter filter = null;
        private String filterClass = null;
        private String filterName = null;
        private String largeIcon = null;
        private final Map<String, String> parameters = new HashMap<>();
        private String smallIcon = null;
        private String asyncSupported = null;
    }
    
    • FilterDef是Filter的定义类,filterClass表示过滤器的定义类。
    public final class ApplicationFilterConfig implements FilterConfig, Serializable {
    
        private static final long serialVersionUID = 1L;
        static final StringManager sm = StringManager.getManager(Constants.Package);
        private static final List<String> emptyString = Collections.emptyList();
        private final transient Context context;
        private transient Filter filter = null;
        private final FilterDef filterDef;
        private transient InstanceManager instanceManager;
        private ObjectName oname;
    
        ApplicationFilterConfig(Context context, FilterDef filterDef)
                throws ClassCastException, ClassNotFoundException, IllegalAccessException,
                InstantiationException, ServletException, InvocationTargetException, NamingException,
                IllegalArgumentException, NoSuchMethodException, SecurityException {
    
            super();
    
            this.context = context;
            this.filterDef = filterDef;
            if (filterDef.getFilter() == null) {
                getFilter();
            } else {
                this.filter = filterDef.getFilter();
                getInstanceManager().newInstance(filter);
                initFilter();
            }
        }
    
        Filter getFilter() throws ClassCastException, ClassNotFoundException, IllegalAccessException,
                InstantiationException, ServletException, InvocationTargetException, NamingException,
                IllegalArgumentException, NoSuchMethodException, SecurityException {
    
            if (this.filter != null)
                return (this.filter);
    
            // 创建 并初始化 Filter 对象
            String filterClass = filterDef.getFilterClass();
            this.filter = (Filter) getInstanceManager().newInstance(filterClass);
            initFilter();
    
            return (this.filter);
        }
    
    
        private void initFilter() throws ServletException {
            if (context instanceof StandardContext &&
                    context.getSwallowOutput()) {
                try {
                    SystemLogHandler.startCapture();
                    filter.init(this);
                } finally {
                    String capturedlog = SystemLogHandler.stopCapture();
                    if (capturedlog != null && capturedlog.length() > 0) {
                        getServletContext().log(capturedlog);
                    }
                }
            } else {
                filter.init(this);
            }
    
            registerJMX();
        }
    }
    
    • ApplicationFilterConfig的创建过程就是通过实例化FilterDef的 filterClass的类并调用 Filter 的 init 方法初始化 Filter 对象。
    • initFilter方法负责初始化 Filter 对象,也就是调用 filter 的 init 方法。
    • ApplicationFilterConfig的filter字段保存实例话后的 Filter 实例。

    FilterDef的加载

    FilterDef 的来源需要如果是 web.xml 定义那么就从 webxml 中获取,如果是springboot 工程,就通过ApplicationContextFacade类型进行获取。

    public class ContextConfig implements LifecycleListener {
    
        private void configureContext(WebXml webxml) {
            // 省略相关代码
            for (FilterDef filter : webxml.getFilters().values()) {
                if (filter.getAsyncSupported() == null) {
                    filter.setAsyncSupported("false");
                }
                context.addFilterDef(filter);
            }
        }
    }
    
    • 通过webxml.getFilters()获取过滤器的FilterDef并添加到StandardContext对象当中。
    • 上述方式一般在 spring MVC 项目在 web.xml 配置过滤器的时候使用。
    public class ApplicationContextFacade implements org.apache.catalina.servlet4preview.ServletContext {
    
        @Override
        public FilterRegistration.Dynamic addFilter(String filterName,
                Filter filter) {
            if (SecurityUtil.isPackageProtectionEnabled()) {
                return (FilterRegistration.Dynamic) doPrivileged("addFilter",
                        new Class[]{String.class, Filter.class},
                        new Object[]{filterName, filter});
            } else {
                return context.addFilter(filterName, filter);
            }
        }
    }
    
    
    public class ApplicationContext implements org.apache.catalina.servlet4preview.ServletContext {
    
        private FilterRegistration.Dynamic addFilter(String filterName,
                String filterClass, Filter filter) throws IllegalStateException {
    
            FilterDef filterDef = context.findFilterDef(filterName);
            // context是StandardContext对象
            if (filterDef == null) {
                filterDef = new FilterDef();
                filterDef.setFilterName(filterName);
                context.addFilterDef(filterDef);
            } else {
                if (filterDef.getFilterName() != null &&
                        filterDef.getFilterClass() != null) {
                    return null;
                }
            }
    
            if (filter == null) {
                filterDef.setFilterClass(filterClass);
            } else {
                filterDef.setFilterClass(filter.getClass().getName());
                filterDef.setFilter(filter);
            }
    
            return new ApplicationFilterRegistration(filterDef, context);
        }
    }
    
    • 通过ApplicationContextFacade的addFilter方法并最终调用ApplicationContext的addFilter方法将过滤器的FilterDef并添加到StandardContext对象当中。
    • 是上述方式一般在springboot 工程中的加载过程。

    Filter的加载流程

    • Filter的定义的加载顺序如上图所示,包括解析 web.xml 文件生成 FilterDef并保存到 StandardContext 当中,遍历 StandardContext 的 FilterDef 生成ApplicationFilterConfig并保存到StandardContext当中。
    • StandardContext负责保存核心的FilterDef和ApplicationFilterConfig。

    Filter执行

    Filter整体流程

    final class StandardWrapperValve
        extends ValveBase {
    
        @Override
        public final void invoke(Request request, Response response)
            throws IOException, ServletException {
    
            // 省略相关代码
            ApplicationFilterChain filterChain =
                    ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
    
            try {
                if ((servlet != null) && (filterChain != null)) {
                    if (context.getSwallowOutput()) {
                        try {
                            SystemLogHandler.startCapture();
                            if (request.isAsyncDispatching()) {
                                request.getAsyncContextInternal().doInternalDispatch();
                            } else {
                                filterChain.doFilter(request.getRequest(),
                                        response.getResponse());
                            }
                        } finally {
                        }
                    } else {
                        if (request.isAsyncDispatching()) {
                            request.getAsyncContextInternal().doInternalDispatch();
                        } else {
                            filterChain.doFilter
                                (request.getRequest(), response.getResponse());
                        }
                    }
    
                }
            } catch (Throwable e) {
    
            }
    
            // Release the filter chain (if any) for this request
            if (filterChain != null) {
                filterChain.release();
            }
        }
    }
    
    • Filter 执行流程在StandardWrapperValve#invoke 方法当中,核心流程包括创建 Filter 调用链和执行 Filter 调用链。
    • ApplicationFilterFactory.createFilterChain负责创建调用链对象ApplicationFilterChain。
    • filterChain.doFilter负责执行 Filter 调用链。

    filterChain的构建

    public final class ApplicationFilterChain implements FilterChain {
        public static final int INCREMENT = 10;
        private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
        private int pos = 0;
        private int n = 0;
        private Servlet servlet = null;
        private boolean servletSupportsAsync = false;
        private static final StringManager sm = StringManager.getManager(Constants.Package);
    
        void addFilter(ApplicationFilterConfig filterConfig) {
            for(ApplicationFilterConfig filter:filters)
                if(filter==filterConfig)
                    return;
    
            if (n == filters.length) {
                ApplicationFilterConfig[] newFilters =
                    new ApplicationFilterConfig[n + INCREMENT];
                System.arraycopy(filters, 0, newFilters, 0, n);
                filters = newFilters;
            }
            filters[n++] = filterConfig;
        }
    }
    
    • ApplicationFilterChain的内部维护ApplicationFilterConfig[] filters来保存 Filter 对象。
    public final class ApplicationFilterFactory {
    
        public static ApplicationFilterChain createFilterChain(ServletRequest request,
                Wrapper wrapper, Servlet servlet) {
    
            ApplicationFilterChain filterChain = null;
            if (request instanceof Request) {
                Request req = (Request) request;
                if (Globals.IS_SECURITY_ENABLED) {
                    filterChain = new ApplicationFilterChain();
                } else {
                    filterChain = (ApplicationFilterChain) req.getFilterChain();
                    if (filterChain == null) {
                        filterChain = new ApplicationFilterChain();
                        req.setFilterChain(filterChain);
                    }
                }
            } else {
                filterChain = new ApplicationFilterChain();
            }
    
            filterChain.setServlet(servlet);
            filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
    
            // 通过StandardContext#findFilterMaps获取所有的Filter对象
            StandardContext context = (StandardContext) wrapper.getParent();
            FilterMap filterMaps[] = context.findFilterMaps();
    
            if ((filterMaps == null) || (filterMaps.length == 0))
                return (filterChain);
    
            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;
    
                // 通过StandardContext#findFilterConfig获取Filter对象
                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMaps[i].getFilterName());
                if (filterConfig == null) {
                    continue;
                }
                filterChain.addFilter(filterConfig);
            }
    
            // 匹配servlet的名字 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;
        }
    }
    
    • 获取StandardContext的FilterMap[] 对象,遍历FilterMap[]后进行规则匹配,匹配后通过 StandardContext 获取ApplicationFilterConfig对象添加到ApplicationFilterChain当中。
    • StandardContext本身维护的ApplicationFilterConfig的加载流程已经分析,需要了解FilterMap的加载过程

    Filter 执行流程

    public final class ApplicationFilterChain implements FilterChain {
    
        public static final int INCREMENT = 10;
        private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
        private int pos = 0;
        private int n = 0;
        private Servlet servlet = null;
        private boolean servletSupportsAsync = false;
        private static final StringManager sm = StringManager.getManager(Constants.Package);
    
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {
    
            if( Globals.IS_SECURITY_ENABLED ) {
                // 省略相关代码
            } else {
                internalDoFilter(request,response);
            }
        }
    
        private void internalDoFilter(ServletRequest request,
                                      ServletResponse response)
            throws IOException, ServletException {
    
            if (pos < n) {
                ApplicationFilterConfig filterConfig = filters[pos++];
                try {
                    Filter filter = filterConfig.getFilter();
    
                    if( Globals.IS_SECURITY_ENABLED ) {
                        // 省略相关代码
                    } else {
                        filter.doFilter(request, response, this);
                    }
                } catch (IOException | ServletException | RuntimeException e) {
    
                } catch (Throwable e) {
    
                }
                return;
            }
    
            try {
                if ((request instanceof HttpServletRequest) &&
                        (response instanceof HttpServletResponse) &&
                        Globals.IS_SECURITY_ENABLED ) {
                    // 省略相关代码
                } else {
                    servlet.service(request, response);
                }
            } catch (IOException | ServletException | RuntimeException e) {
    
            } catch (Throwable e) {
    
            } finally {
    
            }
        }
    }
    
    • ApplicationFilterChain#internalDoFilter负责 Filter 调用链的执行,内部通过维护 Filter 的对象数组filters和下标pos依次执行 Filter。

    FilterMap介绍

    /**
     * 来看下这个类的官方解释:
     * Web应用程序的过滤器映射的表示形式,如部署描述符中<filter-mapping>元素中的所示
     * 每个过滤器映射都必须包含过滤器名称以及URL模式或servlet名称
     * 例如以下配置:
     * <filter-mapping>  
     *    <filter-name>MyFilter</filter-name>  
     *    <url-pattern>/my</url-pattern> 
     * </filter-mapping> 
     * 
     * 说白了,这个FilterMap就是封装了配置信息中<filter-mapping>标签中的元素
     * 其中还包含了两个重点属性:过滤器名、过滤器对应过滤的url
     */
    public class FilterMap extends XmlEncodingBase implements Serializable {
        private boolean matchAllUrlPatterns = false;
        private boolean matchAllServletNames = false;
    
        // serverlet的名字,对应多个
        private String[] servletNames = new String[0];
        // 过滤器名,对应的是<filter-name>中的内容
        private String filterName = null; 
        // 过滤url,对应的是<url-pattern>中的内容(可配置多个<filter-mapping>匹配不同的url,因此是数组形式)
        private String[] urlPatterns = new String[0]; 
    }
    
    • FilterMap的核心字段包括匹配的 Url格式,对应的 Filter 对象的filterName等。

    FilterMap的加载

    public class ContextConfig implements LifecycleListener {
    
        private void configureContext(WebXml webxml) {
            // 省略相关代码
    
            for (FilterMap filterMap : webxml.getFilterMappings()) {
                context.addFilterMap(filterMap);
            }
        }
    }
    
    • 通过webxml.getFilterMappings()获取过滤器的filterMap并添加到StandardContext中。
    • 上述方式一般在 spring MVC 项目在 web.xml 配置过滤器的时候使用。
    public class ApplicationFilterRegistration
            implements FilterRegistration.Dynamic {
    
        private static final StringManager sm =
          StringManager.getManager(Constants.Package);
    
        private final FilterDef filterDef;
        private final Context context;
    
        public ApplicationFilterRegistration(FilterDef filterDef,
                Context context) {
            this.filterDef = filterDef;
            this.context = context;
        }
    
        @Override
        public void addMappingForServletNames(
                EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
                String... servletNames) {
    
            FilterMap filterMap = new FilterMap();
            filterMap.setFilterName(filterDef.getFilterName());
    
            if (dispatcherTypes != null) {
                for (DispatcherType dispatcherType : dispatcherTypes) {
                    filterMap.setDispatcher(dispatcherType.name());
                }
            }
    
            if (servletNames != null) {
                for (String servletName : servletNames) {
                    filterMap.addServletName(servletName);
                }
    
                if (isMatchAfter) {
                    context.addFilterMap(filterMap);
                } else {
                    context.addFilterMapBefore(filterMap);
                }
            }
        }
    
        @Override
        public void addMappingForUrlPatterns(
                EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
                String... urlPatterns) {
    
            FilterMap filterMap = new FilterMap();
            filterMap.setFilterName(filterDef.getFilterName());
    
            if (dispatcherTypes != null) {
                for (DispatcherType dispatcherType : dispatcherTypes) {
                    filterMap.setDispatcher(dispatcherType.name());
                }
            }
    
            if (urlPatterns != null) {
                for (String urlPattern : urlPatterns) {
                    filterMap.addURLPattern(urlPattern);
                }
    
                if (isMatchAfter) {
                    context.addFilterMap(filterMap);
                } else {
                    context.addFilterMapBefore(filterMap);
                }
            }
        }
    }
    
    • addMappingForServletNames和addMappingForUrlPatterns负责获取过滤器的filterMap并添加到StandardContext中。
    • 上述方式一般在 spring boot 工程中加载FilterMap使用。

    Filter执行流程图

    • 通过解析 web.xml 文件生成 FilterMap并保存到 StandardContext 当中。
    • ApplicationFilterChaiFactory 负责创建 Filter 对象 ApplicationFilterChain,然后遍历FilterMap s并添加符合的 Filter 包装对象 ApplicationFilterConfig。
    • 执行ApplicationFilterChain的doFilter方法调用过滤器。

    相关文章

      网友评论

        本文标题:Spring Filter深度解析

        本文链接:https://www.haomeiwen.com/subject/bleimrtx.html