1. 程式人生 > >DelegatingFilterProxy的作用與用法

DelegatingFilterProxy的作用與用法

開發十年,就只剩下這套架構體系了! >>>   

一、類結構

  DelegatingFilterProxy類存在與spring-web包中,其作用就是一個filter的代理,用這個類的好處是可以通過spring容器來管理filter的生命週期,還有就是,可以通過spring注入的形式,來代理一個filter執行,如shiro,下面會說到;有上圖我們可以看到,DelegatingFilterProxy類繼承GenericFilterBean,間接實現了Filter這個介面,故而該類屬於一個過濾器。那麼就會有實現Filter中init、doFilter、destroy三個方法。

二、代理具體實現

  首先我們看init方法,我們知道當filter初始化時會執行init方法,從原始碼中我們可以找到具體程式碼,該方法在GenericFilterBean類中實現,具體功能是,將該類封裝成spring特有形式的類,方便spring維護,並且呼叫initFilterBean方法,該方法放在子類(DelegatingFilterProxy)實現,該方法主要目的是,找到在spring中維護的目標filter,具體實現看下面程式碼:

複製程式碼

/**
 * Standard way of initializing this filter.
 * Map config parameters onto bean properties of this filter, and
 * invoke subclass initialization.
 * @param filterConfig the configuration for this filter
 * @throws ServletException if bean properties are invalid (or required
 * properties are missing), or if subclass initialization fails.
 * @see #initFilterBean
 */
@Override
public final void init(FilterConfig filterConfig) throws ServletException {
   Assert.notNull(filterConfig, "FilterConfig must not be null");
   if (logger.isDebugEnabled()) {
      logger.debug("Initializing filter '" + filterConfig.getFilterName() + "'");
   }
  
   this.filterConfig = filterConfig;

   // Set bean properties from init parameters.
   try {
    //將該類封裝成spring特有的bean形式,方便spring維護
      PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties);
      BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
      ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext());
      bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
      initBeanWrapper(bw);
      bw.setPropertyValues(pvs, true);
   }
   catch (BeansException ex) {
      String msg = "Failed to set bean properties on filter '" +
         filterConfig.getFilterName() + "': " + ex.getMessage();
      logger.error(msg, ex);
      throw new NestedServletException(msg, ex);
   }

   // 該方法在子類中實現,我們可以到DelegatingFilterPoxy中去看看,具體完成了那些工作?
  //1、找到要代理bean的id--》targetBeanName
  //2、在spring,bean容器中找到具體被代理的filter--》delegate

   initFilterBean();

   if (logger.isDebugEnabled()) {
      logger.debug("Filter '" + filterConfig.getFilterName() + "' configured successfully");
   }
}

複製程式碼

 initFilterBean()該方法主要完成兩個功能:

1、找到被代理類在spring中配置的id並賦值給targetBeanName。

2、使用找到的id從spring容器中找到具體被代理的類,並賦值給delegate

複製程式碼

@Override
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) {
       //找到要被代理的filter在spring中配置的id
            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) {
        //找到具體被代理的filter
            this.delegate = initDelegate(wac);
         }
      }
   }
}

複製程式碼

getFilterName()該方法的作用是,獲取被代理的filter在spring中配置的id

protected final String getFilterName() {
  //找到被代理filter在spring中配置的id
   return (this.filterConfig != null ? this.filterConfig.getFilterName() : this.beanName);
}

initDelegate()該方法的作用是,從spring容器中獲取到具體被代理的filter 

複製程式碼

//找到被代理的filter
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
   Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
   if (isTargetFilterLifecycle()) {
      delegate.init(getFilterConfig());
   }
   return delegate;
}

複製程式碼

到這裡我們可以看出來,我們要代理的filter其實就是我們配置filter中的filter-name標籤中的filterName了
<filter-name>filterName</filter-name>

我們在來看看doFilter方法具體實現,該方法主要是使用被代理的filter,並呼叫invokeDelegate方法,
執行被代理filter的doFilter方法,具體實現,請看下面原始碼:

複製程式碼

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

   // 得到被代理的filter
   Filter delegateToUse = this.delegate;
   if (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;
      }
   }

   // 執行被代理filter的doFilter方法
   invokeDelegate(delegateToUse, request, response, filterChain);
}

複製程式碼

invokeDelegate方法的作用就是執行被代理filter的doFilter方法

複製程式碼

protected void invokeDelegate(
      Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

   delegate.doFilter(request, response, filterChain);
}

複製程式碼

看到這裡我相信大家都明白DelegatingFilterPoxy是怎麼回事了吧。下面我們看看spring+shiro是如何運用這個類的

三、運用
  首先我們看web.xml具體配置,注意<filter-name>中配置的name,以name為id在spring的bean配置中找得到對應的bean

複製程式碼

<!-- Shiro Security filter-->
  <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>
      </init-param>
  </filter>
  <filter-mapping>
      <filter-name>shiroFilter</filter-name>
      <url-pattern>/*</url-pattern>
      <dispatcher>REQUEST</dispatcher>
      <dispatcher>ERROR</dispatcher>
  </filter-mapping>

複製程式碼

 spring對於代理filter配置
<bean id="shiroFilter" class="com.auth.Spri