这个类最早是在基于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) |