过滤器原理
在很多java Web项目中我们会在web.xml中配置一些过滤器来拦截请求,比如下面解决乱码的编码过滤器:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.Springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mAPPing>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
但是让我一直困惑的是,这些过滤器是由谁来调用的?又是谁将它们组织起来,并确保他们的执行顺序的?今天使用Spring cloud用代码的方法配置过滤器的时候(不再是配到web.xml中,Spring clound自带web容器,提供开箱即用的服务,所以以前很多需要配到web容器的配置都交给Spring来管理了,在学习Spring cloud的时候发现这货真的是想统一全世界呀-_-),发现自己定义的一个过滤器死活不执行,就打算将这个问题深挖到底。
过滤器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形式的<bean></bean>采用代码来配置
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;
}
}
加了注解之后果然立马见效,看来我的直觉还是挺准的,嘻嘻~_~好了,既然我们知道怎么用工具了,下面就来拆工具,看看how it works。通过断点调试,发现过滤器中两个关键的类在起作用:ApplicationFilterConfig,ApplicationFilterChain。
这两个类是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执行收尾工作;
最后给一个简单的流程图:
文章最后发布于: 2018-01-05 15:09:53
相关阅读
Web 代理是一种存在于网络中间的实体,提供各式各样的功能。现代网络系统中,Web 代理无处不在。我之前有关 HTTP 的博文中,多次提到了
转载:http://blog.csdn.net/u013007900/article/details/50118945 上一次我们讲了M-P模型,它实际上就是对单个神经元的一种建模,还
一、决策树是什么? 顾名思义,决策树是由一个个“决策”组成的树,学过数据结构的同学对树一定不陌生。决策树中,结点分为两种,放“决策
在大街小巷到处都能够看到微信支付和支付宝支付的二维码,而且现在人们已经开始渐渐习惯并喜爱这种支付方式,不需要带现金,也不需要找
Android Service启动(二) bindService()启动过程以及原
本篇文章主要讲解bindService()绑定Service的过程。(android的版本依旧是26) 通过bindService()绑定一个Service时,最终都是调用的Co