这个类最早是在基于Spring的安全框架Acegi中提出来的,一个非常不错的创意性的代理类:将Servlet Filter代理成Spring容器中的Bean,这样可以让Filter也能享受到Spring容器的依赖注入。
需求分析
我们通常在配置过滤器(比如编码设置,比如XSS过滤等)时,都是在 web.xml 中通过<filter>节点来完成,这个是基于Tomcat与J2EE规范的,并不在Spring容器的管理范围内。如果我想要在Filter的执行过程中利用到Spring容器里面依赖管理的话该怎么处理呢?比如,我们想要在Filter中获取某个SpringBean该怎么办呢?其方法之一就是通过WebApplicationContextUtils的工具方法来获取到ApplicationContext,再通过ApplicationContext来获取相应的SpringBean,如下所示:
1 | ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext()); |
这个过程虽然能达到我们获取容器组件的目标,但如果我们所依赖的组件较多,同时还想要其它有关Spring容器的功能时会比较繁琐。
使用方法
FilterToBeanProxy是Acegi(现在已经成为SpringSecurity框架的一部分)专门为了解决这类问题,它在内部维护了一个delegate对象,并将Filter的操作都代理到delegate上,而这个对象delegate是Spring容器中的组件,这样一来,既能够满足我们Filter的功能,也能利用到Spring容器带来的增强。
它的使用方法跟普通的Filter一样,是在web.xml配置FilterToBeanProxy,只不过它的filter-name是有特殊含义的,如下所示:
1 | <filter> |
它的特殊性就在于,这个Filter最终所有的操作都是代理到FilterChainProxy类型的SpringBean上面去的。
初始化过程
我们来看看它的源码(省略非关注点代码):
1 | public class FilterToBeanProxy implements Filter { |
可以看到,在 doInit的时候就是根据配置查找对应的Spring容器组件的过程,步骤如下:
- 判定初始化策略,如果是 lazy 则是在第一次请求过滤的时候执行初始化,而非容器启动时;
- 查找
targetBean属性,如果存在,则beanName就是该配置值; - 检查
lifecycle配置,如果是servlet-container-managed则标记该组件是一个Servlet管控的组件。 - 如果
targetBean忏悔存在,但是Spring容器中没有这个bean,则直接抛出异常。 - 查找
targetClass属性,如果不存在则报错(该属性和targetBean必须选配一个),如果存在则在Spring容器中查找该类型的组件。- 如果找到了,则
beanName为该类型第一个组件的beanName; - 如果没找到,则抛出异常;
- 如果找到了,则
- 判定找到的组件是否是一个
Servlet Filter,如果不是则抛出异常; - 如果是Servlet管控的组件,则调用组件的
init方法。
执行过程
执行过程就比较简单了,就是将doFilter 方法代理给Spring组件,如下所示:
1 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |