一.引言
昨天写了拦截器实现的源码解析,代码并不简单,抓住关键实现的话其实也就还好理解了,不要太抠代码细节。后来看到网上的一些介绍,感觉这过滤器好像挺简单的,本来想着过滤器和拦截器一块写的,不过看拦截器源码耽误的有点晚了,才没写成。今天看到时才发现,原来也没那么简单啊。
过滤器的作用是拦截前段发给后端的请求,一般用于权限过滤、日志记录、图片转换、加密、数据压缩等操作。
二. 使用
public class InferenceFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
// log.info("请求url:{}",((RequestFacade) request).getRequestURL());
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
三. 底层源码解析
Filter的执行流程主要分为两个环节:
- 初始化部分:对于定义好的过滤器,会首先创建过滤器对象,并保存到容器中,并调用init方法进行初始化
- 执行部分:当匹配到相应的请求路径时,首先会对该请求进行拦截,执行doFIlter中的逻辑,若不通过则该请求到此为止;若通过则继续执行下一个拦截器中的doFilter方法,直到指定的过滤器的doFilter方法执行完后,便执行Servlet中的业务逻辑。
a.初始化部分
-
初始化Filter的入口时StandardContext类的startInternal方法
@Override protected synchronized void startInternal() throws LifecycleException { ... // 省略不必要代码 fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); // 读取web.xml配置文件或者注解配置信息,创建并添加Filter ... if (ok) { if (!filterStart()) { // 初始化Filter。若初始化成功则继续往下执行;若初始化失败则抛出异常,终止程序 log.error(sm.getString("standardContext.filterFail")); ok = false; } } ... }
-
一些重要属性
// filterConfigs是一个HashMap,以键值对的形式保存数据(key :value = 过滤器名 :过滤器配置信息对象) private HashMap<String, ApplicationFilterConfig> filterConfigs = new HashMap<>(); // filterDefs同时也是一个HashMap,其中保存的数据是(过滤器名 :过滤器定义对象) private HashMap<String, FilterDef> filterDefs = new HashMap<>();
-
ApplicationFilterConfig
/** * ApplicationFilterConfig类主要用于保存自定义Filter的一些配置信息,例如过滤器名、初始化参数、过滤器定义对象等等 */ public final class ApplicationFilterConfig implements FilterConfig, Serializable { ... /** * 构造方法 */ ApplicationFilterConfig(Context context, FilterDef filterDef) throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException, InvocationTargetException, NamingException, IllegalArgumentException, NoSuchMethodException, SecurityException { super(); this.context = context; this.filterDef = filterDef; // 由此可知,ApplicationFilterConfig类中其实定义了过滤器定义对象 // 初始化真正的过滤器对象 if (filterDef.getFilter() == null) { getFilter(); } else { this.filter = filterDef.getFilter(); getInstanceManager().newInstance(filter); initFilter(); } } ... }
-
FilterDef
/** * 来看下官方解释: * Web应用程序的过滤器定义的表示形式,如部署描述符中<filter>元素中的所示。 * 例如: * <filter> * <filter-name>MyFilter</filter-name> * <filter-class>com.filter.MyFilter</filter-class> * </filter> * * 说白了,这个FilterDef其实就是封装了配置信息中<filter>标签当中的元素 * 其中就有三个重点属性:filterName、filterClass、filter */ public class FilterDef implements Serializable { ... private String filterName = null; // 过滤器名,对应的是<filter-name>中的内容 private String filterClass = null; // 过滤器全限定类名,对应的是<filter-class>中的内容(用于反射创建过滤器对象) private transient Filter filter = null; // 真正的过滤器对象(如:MyFilter实例对象) ... }
-
-
继续看调用过程,在startInternal方法中调用了filterStart方法
public boolean filterStart() { if (getLogger().isDebugEnabled()) { getLogger().debug("Starting filters"); } // Instantiate and record a FilterConfig for each defined filter boolean ok = true; synchronized (filterConfigs) { filterConfigs.clear(); //这里是通过filterDefs获取所有的过滤器对象进行遍历,那么filterDefs是在哪里被初始化的呢? for (Entry<String,FilterDef> entry : filterDefs.entrySet()) { String name = entry.getKey(); if (getLogger().isDebugEnabled()) { getLogger().debug(" Starting filter '" + name + "'"); } try { ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue()); filterConfigs.put(name, filterConfig); } catch (Throwable t) { t = ExceptionUtils.unwrapInvocationTargetException(t); ExceptionUtils.handleThrowable(t); getLogger().error(sm.getString( "standardContext.filterStart", name), t); ok = false; } } } return ok; }
-
filterDefs是在哪里被初始化的呢?其实在执行真正过滤器初始化方法之前,还有一步。在ContextConfig的webConfig方法中调用了configureContext方法
startInternal()->fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null)-> ContextConfig.lifecycleEvent()(事件监听)->configureStart()->webConfig()->configureContext()for (FilterDef filter : webxml.getFilters().values()) { if (filter.getAsyncSupported() == null) { filter.setAsyncSupported("false"); } context.addFilterDef(filter); } for (FilterMap filterMap : webxml.getFilterMappings()) { context.addFilterMap(filterMap); }
-
接着上文filterStart方法中创建过滤器配置对象
ApplicationFilterConfig(Context context, FilterDef filterDef) throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException, InvocationTargetException, NamingException, IllegalArgumentException, NoSuchMethodException, SecurityException { super(); this.context = context; this.filterDef = filterDef; // 现在重点来关注以下部分的代码 // 从过滤器定义对象中获取真正的过滤器对象(正常来说,应该都是空。因为从之前filterDefs的添加元素逻辑来看,添加的过滤器定义对象中的过滤器对象都是空的) if (filterDef.getFilter() == null) { // 过滤器对象为空 getFilter(); // 获取并初始化过滤器(此处没有进行接收结果,说明此处只是进行初始化) } else { this.filter = filterDef.getFilter(); // 绑定过滤器对象 getInstanceManager().newInstance(filter); initFilter(); // 调用过滤器的init方法,初始化过滤器 } }
-
获取并初始化过滤器
Filter getFilter() throws ClassCastException, ReflectiveOperationException, ServletException, NamingException, IllegalArgumentException, SecurityException { // Return the existing filter instance, if any if (this.filter != null) { return this.filter; } // Identify the class loader we will be using String filterClass = filterDef.getFilterClass(); this.filter = (Filter) context.getInstanceManager().newInstance(filterClass); initFilter(); return this.filter; }
-
调用的Filter对象的InitFilter方法
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); } // Expose filter via JMX registerJMX(); }
初始化过程就是根据配置信息创建过滤器配置对象,并初始化它的过滤器定义和真正的过滤对象,最后将它加入到过滤器映配置映射表中去。
b.执行部分
-
Filter方法的调用入口在StandardWrapperValve类的invoker方法。
@Override public final void invoke(Request request, Response response) throws IOException, ServletException { // Create the filter chain for this request ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); .... //调用过滤器的执行链 filterChain.doFilter(request.getRequest(), response.getResponse()); }
-
首先看调用的过滤器链的创建过程
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(); 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 (FilterMap filterMap : filterMaps) { if (!matchDispatcher(filterMap, dispatcher)) { continue; } if (!matchFiltersURL(filterMap, requestPath)) { continue; } ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMap.getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } filterChain.addFilter(filterConfig); } // Add filters that match on servlet name second for (FilterMap filterMap : filterMaps) { if (!matchDispatcher(filterMap, dispatcher)) { continue; } if (!matchFiltersServlet(filterMap, servletName)) { continue; } ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMap.getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } filterChain.addFilter(filterConfig); } // Return the completed filter chain return filterChain; }
初始化ApplicationFilterChain,然后获取当前context的filterMaps对象,根据url路径匹配规则进行过滤后,添加满足条件的到过滤器链中
-
FilterMap对象是什么?在哪里初始化的?
FilterMap filterMaps[] = context.findFilterMaps(); // 从createFilterChain方法中的这句就可以知道,filterMap是从context容器来的 /** * 这个context容器其实就是我们一直说的StandardContext * 然后点进来到findFilterMaps方法,发现它只是返回了filterMaps属性对象的asArray方法的结果 */ @Override public FilterMap[] findFilterMaps() { return filterMaps.asArray(); } // 因此,再来看这个filterMaps属性是个什么东西 // 原来又是ContextFilterMaps类的对象 private final ContextFilterMaps filterMaps = new ContextFilterMaps(); /** * 再点进来这个ContextFilterMaps类,发现原来它是StandardContext类中的一个静态final内部类 * 重点是其内部维护了一个FilterMap[]类型的数组,而且它的asArray方法也只是返回了这个数组 */ private static final class ContextFilterMaps { ... private FilterMap[] array = new FilterMap[0]; ... public FilterMap[] asArray() { synchronized (lock) { return array; } } ... } }
filterMaps是从standardContext中来的,findFilterMaps方法返回的是其内部类中维护的一个ContextFilterMaps[]对象数组。
-
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 String filterName = null; // 过滤器名,对应的是<filter-name>中的内容 private String[] urlPatterns = new String[0]; // 过滤url,对应的是<url-pattern>中的内容(可配置多个<filter-mapping>匹配不同的url,因此是数组形式) ... }
可以看出FilterMap是封装了配置信息中 过滤名 和 对应url参数的对象数组
-
filterMaps是怎么初始化的?在初始化阶段
// 大家对这个方法还有印象吧,它就是初始化过滤器的入口方法(StandardContext类中的startInterval中调用) // 针对于以下代码,我们之前的说法是:读取web.xml配置文件或者注解配置信息,创建并添加FilterDef过滤器定义对象 // 那么到了现在,其实全面一点的说法应该是: // 1. 读取web.xml配置文件或者注解配置信息,创建并添加FilterDef过滤器定义对象 // 2. 同时也创建并添加FilterMap过滤器映射对象 fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); // 以下同样是调用了ContextConfig类的configureContext方法,以下是关键代码 for (FilterMap filterMap : webxml.getFilterMappings()) { // 循环配置信息中的过滤器映射对象 context.addFilterMap(filterMap); // 将过滤器映射对象添加到容器中 } /** * 最终调用了StandardContext类中的addFilterMap方法 */ @Override public void addFilterMap(FilterMap filterMap) { validateFilterMap(filterMap); // 验证过滤器映射对象中的信息是否正确 filterMaps.add(filterMap); // 添加到filterMaps中去 fireContainerEvent("addFilterMap", filterMap); }
-
重新回到invoke方法中,invoke初始化获取filterChain后,调用了filterChain的doFilter方法,在其中又调用了internalDoFilter方法
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; try { Filter filter = filterConfig.getFilter(); if (request.isAsyncSupported() && "false".equalsIgnoreCase( filterConfig.getFilterDef().getAsyncSupported())) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal); } else { filter.doFilter(request, response, this); } } catch (IOException | ServletException | RuntimeException e) { throw e; } catch (Throwable e) { e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("filterChain.filter"), e); } return; } // We fell off the end of the chain -- call the servlet instance try { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(request); lastServicedResponse.set(response); } if (request.isAsyncSupported() && !servletSupportsAsync) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } // Use potentially wrapped request from this point if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) && Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res}; SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal); } else { servlet.service(request, response); } } catch (IOException | ServletException | RuntimeException e) { throw e; } catch (Throwable e) { e = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("filterChain.servlet"), e); } finally { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(null); lastServicedResponse.set(null); } } }
到这里就真正调用我们自定Filter的doFilter方法了,可以看到后面就开始调用Servlet的service方法了,拦截器就在其中生效。