在很多Java Web项目中我们会在web.xml中配置一些过滤器来拦截请求,比如下面解决乱码的编码过滤器:
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
encodingFilter
/*
但是让我一直困惑的是,这些过滤器是由谁来调用的?又是谁将它们组织起来,并确保他们的执行顺序的?
过滤器demo如下(为保护公司秘密,重要代码已删除,各位见谅哈):
@WebFilter(filterName="FilterDemo", urlPatterns={"/**"})
@Order(1) //当有多个filter时,指定filter的顺序
public class SecurityFilter implements Filter{
private static final Logger LOGGER = LoggerFactory.getLogger( FilterDemo.class );
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//TODO do some filter action;
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
}
本以为这样写了之后过滤器就能工作了,可是请求进来以后发现过滤器未执行。后来想到Spring的工作都是交给注册到beanFacotry中的一个个bean来完成的,所以可能需要把这个过滤器注册到beanFactory中,就像这样:
@Configuration //表明这是一个Spring配置文件
@EnableSwagger2 //swagger是一个restful接口的文档在线自动生成+功能测试功能框架
@RefreshScope //允许当Spring cloud config配置文件改动之后,依赖配置的bean自动更新而不用重启服务
public class GlobalBeanConfig {
//将filter加入到Spring配置中,否则只把filter类写出来,不加入配置还是无法起作用
@Bean //将xml形式的 采用代码来配置
public Filter filterDemo(){
return new FilterDemo();
}
@Bean(name = "loggerInteceptor")
public GlobalAspectInteceptor getLoggerInteceptor() {
return new GlobalAspectInteceptor();
}
@Bean
public ThreadPoolTaskExecutor globalTaskExecutor(
@Value("${global.thread.pool.corePoolSize}") Integer corePoolSize,
@Value("${global.thread.pool.maxPoolSize}") Integer maxPoolSize,
@Value("${global.thread.pool.queueCapacity}") Integer queueCapacity
) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize( corePoolSize );
executor.setMaxPoolSize( maxPoolSize );
executor.setQueueCapacity( queueCapacity );
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadGroupName("globalTaskExecutor");
executor.setRejectedExecutionHandler( new ThreadPoolExecutor.DiscardPolicy() );
executor.initialize();
return executor;
}
}
加了注解之后果然立马见效,看来我的直觉还是挺准的,嘻嘻~_~
这两个类是org.apache.catalina.core包下面的,可以肯定是tomcat容器来管理过滤器链了。
接下来看ApplicationFilterConfig,先上部分源码:
public final class ApplicationFilterConfig implements FilterConfig, Serializable { private static final long serialVersionUID = 1L; static final StringManager sm = StringManager.getManager("org.apache.catalina.core"); private static final Log log = LogFactory.getLog(ApplicationFilterConfig.class); 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 { this.context = context; this.filterDef = filterDef; if(filterDef.getFilter() == null) { this.getFilter(); } else { this.filter = filterDef.getFilter(); this.getInstanceManager().newInstance(this.filter); this.initFilter(); } } }通过分析可以发现,这个类是就是用来持有一个过滤器的,构造方法传入的context想必就是tomcat配置文件下的context.xml配置的应用环境,fliterDef就是过滤器的描述信息,应该是通过配置在web.xml中(或者其他什么地方)的filter参数来构造的。
好,重点来啦-ApplicationFilterChain,名字就暴露了它的作用,就是它将一个个分散的过滤器组织起来的。结合上面我猜测,它里面应该有一个数组或列表来保存ApplicationFilterConfig,还有一个过滤器游标,来记录当前过滤器走到哪儿了。源码(部分)如下:
public final class ApplicationFilterChain implements FilterChain { private static final ThreadLocal<ServletRequest> lastServicedRequest; private static final ThreadLocal<ServletResponse> lastServicedResponse; 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; private static final Class>[] classType; private static final Class>[] classTypeUsedInService; public ApplicationFilterChain() { } public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if(Globals.IS_SECURITY_ENABLED) { final ServletRequest req = request; final ServletResponse res = response; try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Void run() throws ServletException, IOException { ApplicationFilterChain.this.internalDoFilter(req, res); return null; } }); } catch (PrivilegedActionException var7) { Exception e = var7.getException(); if(e instanceof ServletException) { throw (ServletException)e; } if(e instanceof IOException) { throw (IOException)e; } if(e instanceof RuntimeException) { throw (RuntimeException)e; } throw new ServletException(e.getMessage(), e); } } else { this.internalDoFilter(request, response); } } private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if(this.pos < this.n) { ApplicationFilterConfig e1 = this.filters[this.pos++]; try { Filter res1 = e1.getFilter(); if(request.isAsyncSupported() && "false".equalsIgnoreCase(e1.getFilterDef().getAsyncSupported())) { request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE); } if(Globals.IS_SECURITY_ENABLED) { Principal principal1 = ((HttpServletRequest)request).getUserPrincipal(); Object[] args1 = new Object[]{request, response, this}; SecurityUtil.doAsPrivilege("doFilter", res1, classType, args1, principal1); } else { res1.doFilter(request, response, this); } } catch (ServletException | RuntimeException | IOException var15) { throw var15; } catch (Throwable var16) { Throwable res = ExceptionUtils.unwrapInvocationTargetException(var16); ExceptionUtils.handleThrowable(res); throw new ServletException(sm.getString("filterChain.filter"), res); } } else { try { if(ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(request); lastServicedResponse.set(response); } if(request.isAsyncSupported() && !this.servletSupportsAsync) { request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE); } if(request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) { Principal principal = ((HttpServletRequest)request).getUserPrincipal(); Object[] args = new Object[]{request, response}; SecurityUtil.doAsPrivilege("service", this.servlet, classTypeUsedInService, args, principal); } else { this.servlet.service(request, response); } } catch (ServletException | RuntimeException | IOException var17) { throw var17; } catch (Throwable var18) { Throwable e = ExceptionUtils.unwrapInvocationTargetException(var18); ExceptionUtils.handleThrowable(e); throw new ServletException(sm.getString("filterChain.servlet"), e); } finally { if(ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set((Object)null); lastServicedResponse.set((Object)null); } } } } ...... }果然,字段里面有一个ApplicationFilterConfig[]用来保存一系列过滤器,pos用来保存当前过滤器位置,还有其他字段就不深入下去了,有兴趣的小伙伴可以自己探索。
再来看两个重点方法:doFilter,internalDoFilter
doFilter的最终目的只有一个,调用internalDoFilter,中间可能会增加一些安全策略,估计Globals.IS_SECURITY_ENABLE与是否开启https服务有关,具体没仔细研究过。
internalDoFilter的最终目的也只有一个,就是调当前pos指向的过滤器链中的某一个filter的doFilter(request, response, this)方法,中间可能会增加一些安全策略,以及当所有过滤器调用完了,进行的一些收尾清理工作,包括调用servlet.service(request, response)方法,来处理真正的请求,以及清除threadLocal中保存的当前的request和response,为下一次请求做准备。
再把流程梳理一遍:
一个request请求进来了,先把自己交给filterChain;
filterChain启动过滤器链,从头开始,把request交给第一个filter,并把自己传给filter;
filter在doFilter里做完自己的过滤逻辑,再调用filterChain的doFilter,以启动下一个过滤器;
filterChain游标移动,启动下一个过滤器,如此循环下去......
过滤器游标走到链的末尾,filterChain执行收尾工作;
最后给一个简单的流程图: