重新認(rèn)識(shí)javax.servlet.Filter過濾器

在web容器中, 最先加載的是 Listener, 其次是 Filter 最后是 Servlet.
Filter是一個(gè)特殊的 Servlet, 其生命周期分為以下三部分:

  • init(FilterConfig filterConfig) // 為Filter初始化 提供支持
  • doFilter(ServletRequest request, ServletResponse response, FilterChain chain) //這個(gè)是 Filter的核心方法被攔截請(qǐng)求在這里被處理
  • destroy() // Filter 實(shí)例銷毀前的準(zhǔn)備工作 //Called by the web container to indicate to a filter that it is being taken out of service.

通常我們將自定義的Filter配置在web容器的web.xml中, 大致分為兩個(gè)部分來配置這個(gè)Filter.

//聲明filter
<filter>
    <filter-name>filter-name</filter-name>
    <filter-class>com.xx.FilterClass</filter-class>
//初始化參數(shù)
    <init-param>
        <param-name>key</param-name>
        <param-value>value</param-value>
    </init-param>
</filter>

//filter 攔截規(guī)則
<filter-mapping>
  <filter-mapping>
  <filter-name>filter-name</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

和servlet不同的是, 如果配置多個(gè)filter, 則其聲明的先后順序是區(qū)別的, servlet在匹配到后流程至此終止, 而 filter不一定, 他是可以向下傳遞下去進(jìn)入下一個(gè)filter. 這里能夠使 Filter形成鏈?zhǔn)秸{(diào)用關(guān)鍵點(diǎn)在于 doFilter方法參數(shù)FilterChain {doFilter(ServletRequest request, ServletResponse response, FilterChain chain)}

FilterChain 是一個(gè)接口, 他只有一個(gè)方法 doFilter(ServletRequest var1, ServletResponse var2). 在我們希望將 #request和 #response傳遞給下一個(gè) Filter處理的時(shí)候就會(huì)使用 FilterChain. 如:

filterChain.doFilter(request, response) ;

這樣就會(huì)進(jìn)入下一個(gè)Filter的處理邏輯. 如此形成有名的Filter職責(zé)鏈. 類似鏈表結(jié)構(gòu). 鏈表是持有自己的引用, 但是FilterChain不是, 他有點(diǎn)類似代理和鏈表的結(jié)合體. 經(jīng)過查看 tomcat源碼, 才知道, 這個(gè)職責(zé)鏈?zhǔn)怯蓋eb容器管理的. 不同的容器實(shí)現(xiàn)FilterChain接口方法 #doFilter 就可以了. 例如在tomcat中, 實(shí)現(xiàn)類叫ApplicationFilterChain 這個(gè)類包含我們聲明的全部Filter, 并且有序, 然后記錄當(dāng)前Filter在Filter鏈中的索引值, 獲取該索引值的Filter然后調(diào)用doFilter同時(shí)將自己傳給這個(gè)Filter, 代碼如下:

filter.doFilter(request, response, this);

如果全部Filter執(zhí)行完畢, 則將流程交給對(duì)應(yīng)的servlet實(shí)例執(zhí)行具體的業(yè)務(wù).

servlet.service(request, response);

至此全部流程完畢.

我接觸到的這種職責(zé)鏈模式很多地方有使用, spring#CompositeFilter就是職責(zé)鏈很經(jīng)典的使用而且也重新實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的職責(zé)鏈管理器. 在 spring-web模塊中可以看見:

private static class VirtualFilterChain implements FilterChain {

   private final FilterChain originalChain;

   private final List<? extends Filter> additionalFilters;

   private int currentPosition = 0;

   public VirtualFilterChain(FilterChain chain, List<? extends Filter> additionalFilters) {
      this.originalChain = chain;
      this.additionalFilters = additionalFilters;
   }

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

      if (this.currentPosition == this.additionalFilters.size()) {
         this.originalChain.doFilter(request, response);
      }
      else {
         this.currentPosition++;
         Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
         nextFilter.doFilter(request, response, this);
      }
   }
}

根據(jù)描述, CompositeFilter通常需要結(jié)合 FIlter代理類#DelegatingFilterProxy

/**
 * A generic composite servlet {@link Filter} that just delegates its behavior
 * to a chain (list) of user-supplied filters, achieving the functionality of a
 * {@link FilterChain}, but conveniently using only {@link Filter} instances.
 *
 * <p>This is useful for filters that require dependency injection, and can
 * therefore be set up in a Spring application context. Typically, this
 * composite would be used in conjunction with {@link DelegatingFilterProxy},
 * so that it can be declared in Spring but applied to a servlet context.
 *
 * @author Dave Syer
 * @since 3.1
 */

這個(gè)類我們配置在web.xml文件中, 作為對(duì)外唯一可見的一個(gè)Filter. 如:

<filter>
    <filter-name>compositeFilter</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>compositeFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

查看 DelegatingFilterProxy源碼, DelegatingFilterProxy會(huì)在spring容器bean初始化完成之后初始化被代理的Filter.

public class DelegatingFilterProxy extends GenericFilterBean{

@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) {
            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);
         }
      }
   }
}
}

public abstract class GenericFilterBean implements
      Filter, BeanNameAware, EnvironmentAware, ServletContextAware, InitializingBean, DisposableBean {

/**
 * Calls the {@code initFilterBean()} method that might
 * contain custom initialization of a subclass.
 * <p>Only relevant in case of initialization as bean, where the
 * standard {@code init(FilterConfig)} method won't be called.
 * @see #initFilterBean()
 * @see #init(javax.servlet.FilterConfig)
 */
@Override
public void afterPropertiesSet() throws ServletException {
   initFilterBean(); //這是一個(gè)空方法, 需要子類實(shí)現(xiàn)
}
}

這樣就形成了Filter代理 和Filter職責(zé)鏈.
使用DelegatingFilterProxy的目的多用于我們?cè)谧远x業(yè)務(wù)FIlter的時(shí)候常常需要依賴spring管理的bean. 但是因?yàn)樵趙eb容器中, Filter首先被加載, 這之后spring容器才會(huì)被加載(這里還沒有代碼驗(yàn)證過),所以如果在Filter中依賴spring管理bean則會(huì)出現(xiàn)空指針引用, 無法使用. 當(dāng)使用DelegatingFilterProxy后, 這種情況就改變了. 根據(jù)這個(gè)類的實(shí)現(xiàn). 容器加載DelegatingFilterProxy這個(gè)Filter后, 會(huì)等待spring容器初始化全部bean完成后#afterPropertiesSet方法被調(diào)用的時(shí)候才會(huì)初始化DelegatingFilterProxy這個(gè)Filter代理的全部Filter, 這時(shí)候被代理Filter就能夠拿到spring容器里的全部bean.

上面的說法, 我覺得不對(duì), DelegatingFilterProxy 首先可以將多個(gè)Filter串聯(lián)在一起, 多個(gè)Filter可以以Spring Bean的形式的定義在spring配置文件中, 而不用全部聲明到web.xml文件中. 然后這些Filter初始化都委托給DelegatingFilterProxy. 我覺得這是這個(gè)類本來的職責(zé).
按照web容器規(guī)范, Listener/FIlter/Servlet他們?cè)趙eb容器中的的初始化順序是 Listener -> FIlter -> Servlet ; 如果引入spring框架, 一般, 我們都是使用 Spring#ContextLoaderListener來加載spring以及初始化Spring IOC容器, 所以 這個(gè)先后順序沒有問題, 只是Filter中如果依賴了spring bean拿不到bean. 無法注入, 因?yàn)閣eb.xml中無法注入spring bean, Filter也拿不到ApplicationContext, 所以沒有辦法拿到spring#bean, 只有把 Filter也納入spring管理這樣才可以拿到spring bean.

注意: 以下配置節(jié)點(diǎn)targetFilterLifecycle

<filter>
    <filter-name>compositeFilter</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>

targetFilterLifecycle為 true代表spring管理filter的初始化, 即 init和 destroy方法在適當(dāng)?shù)臅r(shí)候會(huì)被spring調(diào)用. 如果為false則這兩個(gè)方法無效.具體代碼如下:

/**
 * Initialize the Filter delegate, defined as bean the given Spring
 * application context.
 * <p>The default implementation fetches the bean from the application context
 * and calls the standard {@code Filter.init} method on it, passing
 * in the FilterConfig of this Filter proxy.
 * @param wac the root application context
 * @return the initialized delegate Filter
 * @throws ServletException if thrown by the Filter
 * @see #getTargetBeanName()
 * @see #isTargetFilterLifecycle()
 * @see #getFilterConfig()
 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
 */
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
   Filter delegate = wac.getBean(getTargetBeanName(), Filter.class);
   if (isTargetFilterLifecycle()) {
      delegate.init(getFilterConfig());
   }
   return delegate;
}
/**
 * Destroy the Filter delegate.
 * Default implementation simply calls {@code Filter.destroy} on it.
 * @param delegate the Filter delegate (never {@code null})
 * @see #isTargetFilterLifecycle()
 * @see javax.servlet.Filter#destroy()
 */
protected void destroyDelegate(Filter delegate) {
   if (isTargetFilterLifecycle()) {
      delegate.destroy();
   }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容