深度解析Spring Filter方法示例

 更新时间:2023年08月20日 10:28:47   作者:晴天哥_王志  
这篇文章主要为大家介绍了深度解析Spring Filter用法示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

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&lt;DispatcherType&gt; 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&lt;DispatcherType&gt; 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方法示例的详细内容,更多关于Spring Filter用法的资料请关注脚本之家其它相关文章!

相关文章

  • Solr通过特殊字符分词实现自定义分词器详解

    Solr通过特殊字符分词实现自定义分词器详解

    最近因为工作的需要,要做一个分词器,通过查找相关的资料最终用solr实现了,下面这篇文章主要给大家介绍了关于Solr通过特殊字符分词实现自定义分词器的相关资料,需要的朋友可以参考借鉴,下面随着小编来一起看看吧。
    2017-09-09
  • Spring常用注解及自定义Filter的实现

    Spring常用注解及自定义Filter的实现

    这篇文章主要介绍了Spring常用注解及自定义Filter的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • Java线程池详细解读

    Java线程池详细解读

    这篇文章主要给大家介绍了关于Java中方法使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • 基于Spring框架由ConditionalOnMissingBean注解引发的问题

    基于Spring框架由ConditionalOnMissingBean注解引发的问题

    这篇文章主要介绍了基于Spring框架由ConditionalOnMissingBean注解引发的问题,具有很好
    2023-11-11
  • SpringBoot微信消息接口配置详解

    SpringBoot微信消息接口配置详解

    这篇文章主要介绍了SpringBoot 微信消息接口配置详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-06-06
  • Java零基础教程之Windows下安装、启动Tomcat服务器方法图解(免安装版)

    Java零基础教程之Windows下安装、启动Tomcat服务器方法图解(免安装版)

    这篇文章主要介绍了Windows系统下安装、启动、注册服务、停止 Tomcat操作的所有方法,本文通过图文并茂的方式给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2016-09-09
  • Java SpringBoot自动装配原理详解及源码注释

    Java SpringBoot自动装配原理详解及源码注释

    SpringBoot的自动装配是拆箱即用的基础,也是微服务化的前提。其实它并不那么神秘,我在这之前已经写过最基本的实现了,大家可以参考这篇文章,来看看它是怎么样实现的,我们透过源代码来把握自动装配的来龙去脉
    2021-10-10
  • 浅谈java中HashMap键的比较方式

    浅谈java中HashMap键的比较方式

    今天带大家了解一下java中HashMap键的比较方式,文中有非常详细的解释说明及代码示例,对正在学习java的小伙伴们很有帮助,需要的朋友可以参考下
    2021-05-05
  • 使用Java应用程序添加或删除 PDF 中的附件

    使用Java应用程序添加或删除 PDF 中的附件

    当我们在制作PDF文件或者PPT演示文稿的时候,为了让自己的文件更全面详细,就会在文件中添加附件,那么如何添加或删除PDF中的附件呢,今天通过本文给大家详细讲解,需要的朋友参考下吧
    2023-01-01
  • 基于SpringBoot2.0默认使用Redis连接池的配置操作

    基于SpringBoot2.0默认使用Redis连接池的配置操作

    这篇文章主要介绍了基于SpringBoot2.0默认使用Redis连接池的配置操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12

最新评论