Spring-webソース解析のFilter-OnecePerRequest Filter:

3353 ワード

4.1.70.RELEASEに基づく
まずfilter-mappingの配置を見ます. 

   encodingFilter
   /*
   REQUEST
   ASYNC
ここでは、ASYNCの構成を指定して、フィルタリング非同期要求を示しています.このASYNCは、エニュメレーション型DispartTypeの一つの要素であり、Servlet 3.0において、一つの要求がDispactcher Type.ASYNCタイプであれば、一つの要求のプロセスにおいて、filterは複数のスレッドを呼び出すことができます.これは明らかに問題がありますが、springはどうやってこの問題を避けますか?これは今日話したいOcePerRequest Filterです.
直接dofilterの方法を見ます.
@Override
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

   if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
      throw new ServletException("OncePerRequestFilter just supports HTTP requests");
   }
   HttpServletRequest httpRequest = (HttpServletRequest) request;
   HttpServletResponse httpResponse = (HttpServletResponse) response;

   String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
   boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;

   if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {

      // Proceed without invoking this filter...
      filterChain.doFilter(request, response);
   }
   else {
      // Do invoke this filter...
      request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
      try {
         doFilterInternal(httpRequest, httpResponse, filterChain);
      }
      finally {
         // Remove the "already filtered" request attribute for this request.
         request.removeAttribute(alreadyFilteredAttributeName);
      }
   }
}
ソースからは、springはフィルタリングされたrequestにatributeをセットします.filterチェーンと目標方法の実行が終わったら、このatributeをリリースします.  get AlreadyFilteredAttributeName()メソッドが得られ、デフォルトではfilterの名前にサフィックスが付けられていますが、filterが完全に初期化されていない場合は、クラス名にサフィックスが付けられ、後に「.FILTEED」が付けられます.
protected String getAlreadyFilteredAttributeName() {
   String name = getFilterName();
   if (name == null) {
      name = getClass().getName();
   }
   return name + ALREADY_FILTERED_SUFFIX;
}
名前を取得したら、このfilterを実行したかどうかを判断する必要があります.判定条件は3つあります.
1 has AlreadyFilteredAttributeがありますか? 
2 skyipDisplatchかどうか
3フィルタをかけないか
私たちは2と3を直接見て、ステップ3で、ショルドノートファイトによってフィルタリングを行うかどうか判断します.その実現はサブクラスに依存します.
2には判定条件が二つあります.
private boolean skipDispatch(HttpServletRequest request) {
   if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) {
      return true;
   }
   if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) {
      return true;
   }
   return false;
}
1は非同期であり、非同期をフィルタするべきでない場合、skypetchはtrueであり、つまりフィルタリングを行わない.
2はERROR要求であり、ERRORをフィルタするべきではなく、同じくtrueに戻る.
上記の全ての判断条件が完了したら、実行するかどうかを決定することができます.
doFilterInternal(httpRequest, httpResponse, filterChain);
この方法はサブクラスの具体的な実現方法です.その一つは前の記事で述べたCharcterEncocdingFilterです.
もう一つ注意しなければならない方法は? 
protected boolean isAsyncStarted(HttpServletRequest request) {
   return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted();
}
これがtrueに戻ると、現在のスレッドが終了している時はレスポンスは返されません.