欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

DelegatingFilterProxy的使用

程序员文章站 2022-03-05 14:15:48
...
在Spring集成第三方时,通常会提供以下方式的配置来作为第三方引入的入口:
<filter>  
    < filter-name>shiroFilter</filter-name>  
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
<init-param>
    <param-name>targetFilterLifecycle</param-name>
    <param-value>true</param-value>  <!-- 默认是false -->
</init-param> 
</filter>  
<filter-mapping>  
    < filter-name>shiroFilter</filter-name>  
    < url-pattern>/*</url-pattern>  
</filter-mapping> 
从配置上来看,只是增加了一个filter,与引入的第三方框架没有什么关系。实际上DelegatingFilterProxy是org.springframework.web.filter中的一个特殊类,对于servlet filter进行代理,其好处在于可以通过spring容器来管理servlet filter对象及其生命周期。既然由spring容器进行管理,那和普通的bean就没有区别,也可以使用依赖注入机制、属性文件的操作等特性。

那么DelegatingFilterProxy是如何实现filter的代理?
protected void initFilterBean() throws ServletException {
  synchronized (this.delegateMonitor) {
    if(this.delegate == null){
      // If no target bean name specified, use filter name.
      if (this.targetBeanName == null) {
        this.targetBeanName = getFilterName();
      }
      // Fetch Spring root application context and initialize the delegate early,
      // if possible. If the root application context will be started after this
      // filter proxy, we'll have to resort to lazy initialization.
      WebApplicationContext wac = findWebApplicationContext();
      if (wac != null) {
        this.delegate = initDelegate(wac);
      }
    }
  }
}
从上述代码可以看出,如果没有提供初始化参数targetBeanName,就用filterName替代(必须与spring中定义的bean name对应),然后在spring IOC容器中获取该bean,下面看一下获取bean的代码initDelegate:
protected Filter initDelegate(WebApplicationContext wac) throws ServletException{
  Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
  if (isTargetFilterLifecycle()) {
    delegate.init(getFilterConfig());
  }
  return delegate;
}
从容器中获取bean对象,通过判断isTargetFilterLifecycle决定是否调用init(),默认为false。从代码中Filter.class看出,该类必须实现Filter接口。在Shiro对应的bean为ShiroFilterFactoryBean,该类是一个工厂类,并没有实现Filter接口,表面上看与上面的原则相违背。实际情况ShiroFilterFactoryBean是一个Filter工厂,其有一个内部类SpringShiroFilter,继承于AbstractShiroFilter,实现了Filter接口。getObject()、getObjectType()两个方法返回的也都是SpringShiroFilter类型,因此getBean实际返回的是SpringShiroFilter。Shiro源码分析可以参考:
引用

获取Filter对象获取并执行init后,就需要关注doFilter,代码如下:
public void doFilter(ServletRequest request, ServletResponse response, 
  FilterChain filterChain) throws ServletException, IOException {
 
        // Lazily initialize the delegate if necessary.
        Filter delegateToUse = null;
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                WebApplicationContext wac = findWebApplicationContext();
                if (wac == null) {
                    throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
                }
                this.delegate = initDelegate(wac);
            }
            delegateToUse = this.delegate;
        }
 
        // Let the delegate perform the actual doFilter operation.
        invokeDelegate(delegateToUse, request, response, filterChain);
}
以上代码,需要重点关注invokeDelegate,Lazily initialize只有在init未调用delegate未初始化时执行。invokeDelegate代码:
protected void invokeDelegate(
            Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
     delegate.doFilter(request, response, filterChain);
}
delegate为从spring容器中获取到的Filter,执行了doFilter方法。从上述代码可以看出DelegatingFilterProxy就是一个代理模式的应用,把servlet容器的filter与spring容器中bean有效的结合,综合了两者的特性。
相关标签: spring filter