必威体育Betway必威体育官网
当前位置:首页 > IT技术

Java过滤器链原理解析

时间:2019-10-28 15:15:39来源:IT技术作者:seo实验室小编阅读:51次「手机版」
 

过滤器原理

在很多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

相关阅读

HTTP 代理原理及实现

Web 代理是一种存在于网络中间的实体,提供各式各样的功能。现代网络系统中,Web 代理无处不在。我之前有关 HTTP 的博文中,多次提到了

BP神经网络原理及matlab实例

转载:http://blog.csdn.net/u013007900/article/details/50118945 上一次我们讲了M-P模型,它实际上就是对单个神经元的一种建模,还

带你搞懂决策树算法原理

一、决策树是什么? 顾名思义,决策树是由一个个“决策”组成的树,学过数据结构的同学对树一定不陌生。决策树中,结点分为两种,放“决策

18位微信付款码原理是什么?如何防止被骗?

在大街小巷到处都能够看到微信支付和支付宝支付的二维码,而且现在人们已经开始渐渐习惯并喜爱这种支付方式,不需要带现金,也不需要找

Android Service启动(二) bindService()启动过程以及原

本篇文章主要讲解bindService()绑定Service的过程。(android的版本依旧是26) 通过bindService()绑定一个Service时,最终都是调用的Co

分享到:

栏目导航

推荐阅读

热门阅读