banner
田野放空

田野放空

认真扮演一个擅长白日做梦的普通人

javaにおけるフィルターの実行順序はどのようになっていますか

問題:#

  1. filterChain はいつ実行されるのですか?
  2. filterChain の filter はどこから来るのですか?
  3. standardContext はいつフィルタのコレクションを収集し始めるのですか?

1. filterChain はいつ実行されるのですか?#

org.apache.catalina.core.StandardWrapperValve クラスには以下のようなメソッドがあります。

public final void invoke(Request request, Response response)
        throws IOException, ServletException {

				//大部分の無関係なコードを省略
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
				//大部分の無関係なコードを省略
        try {
            if (!unavailable) {
              	//1. サーブレットを作成
                servlet = wrapper.allocate();
            }
        } catch(){
          ...
        }
        // 2. このリクエストのためのフィルターチェーンを作成
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

        Container container = this.container;
        try {
            if ((servlet != null) && (filterChain != null)) {
                // 必要に応じて出力を飲み込む
                if (context.getSwallowOutput()) {
                    try {
                        SystemLogHandler.startCapture();
                        if (request.isAsyncDispatching()) {
                            request.getAsyncContextInternal().doInternalDispatch();
                        } else {
                          	//FilterChainオブジェクトのdoFilterメソッドを実行します。
                            filterChain.doFilter(request.getRequest(),
                                    response.getResponse());
                        }
                    } finally {
                      
                    }
                } else {
                    if (request.isAsyncDispatching()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else {
                        filterChain.doFilter
                            (request.getRequest(), response.getResponse());
                    }
                }

            }
        } catch(){
        } finally {
            // このリクエストのためのフィルターチェーンを解放します(あれば)
            if (filterChain != null) {
                filterChain.release();
            }

            // 割り当てられたサーブレットインスタンスを解放します
            try {
                if (servlet != null) {
                    wrapper.deallocate(servlet);
                }
            }
        }
    }

後でフィルターチェーンオブジェクトを作成するコードを見ますが、まずはフィルターチェーンを実行する方法を見てみましょう。フィルターチェーンオブジェクトの実行フローの実装:

public final class ApplicationFilterChain implements FilterChain {
   //現在実行中のフィルタのインデックス
   private int pos = 0;
  //一致したフィルタの数 
   private int n = 0;
  //サーブレット 
   private Servlet servlet = null;
  //一致したフィルタの配列
   private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];

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

    		//セキュリティが有効になっている場合
        if( Globals.IS_SECURITY_ENABLED ) {
          //... セキュリティビーンフィルタ
        } else {
          	//フィルタロジックを実行 
            internalDoFilter(request,response);
        }
    }

    private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {
      
      	//ループして次のフィルタを呼び出します
        if (pos < n) {
          	//posインデックスを++
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();
                if( Globals.IS_SECURITY_ENABLED ) {
                } else {
                  	//フィルターチェーン内の現在のフィルタのdoFilter()を実行
                    filter.doFilter(request, response, this);
                }
            } catch(){
              ...
            }
            return;
        }

        // チェーンの終わりに落ちました -- サーブレットインスタンスを呼び出します
        try {
            if ((request instanceof HttpServletRequest) &&
                    (response instanceof HttpServletResponse) &&
                    Globals.IS_SECURITY_ENABLED ) {
            } else {
              	//実行するフィルタがない場合、サーブレットのserviceメソッドが実行され、私たちが書いたビジネスロジックが含まれます。
                servlet.service(request, response);
            }
        }catch(){
          ...
        }finally{
          ...
        }
    }
}

filterChain クラスのソースコードから、底層には一致したフィルタの配列が含まれており、追加されたフィルタオブジェクトは順序付けられています。追加する際に決定されます。

では、いつ追加されるのでしょうか?

2. filterChain の filter はどこから来るのですか?#

実際には、org.apache.catalina.core.StandardWrapperValve クラスの invoke メソッド内で、

ApplicationFilterChain filterChain =
               ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
    public static ApplicationFilterChain createFilterChain(ServletRequest request,
            Wrapper wrapper, Servlet servlet) {

        // 実行するサーブレットがない場合、nullを返します
        if (servlet == null)
            return null;

        // フィルターチェーンオブジェクトを作成して初期化します
        ApplicationFilterChain filterChain = null;
        if (request instanceof Request) {
            Request req = (Request) request;
            if (Globals.IS_SECURITY_ENABLED) {
                // セキュリティ: リサイクルしない
                filterChain = new ApplicationFilterChain();
            } else {
                filterChain = (ApplicationFilterChain) req.getFilterChain();
                if (filterChain == null) {
                    filterChain = new ApplicationFilterChain();
                    req.setFilterChain(filterChain);
                }
            }
        } else {
            // リクエストディスパッチャーが使用されています
            filterChain = new ApplicationFilterChain();
        }

        filterChain.setServlet(servlet);
        filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

        // このコンテキストのフィルターマッピングを取得します
        StandardContext context = (StandardContext) wrapper.getParent();
      	//ServletContextオブジェクトに登録されたすべてのフィルタの配列を取得します
        FilterMap filterMaps[] = context.findFilterMaps();

        // フィルターマッピングがない場合、終了します
        if ((filterMaps == null) || (filterMaps.length == 0))
            return filterChain;

        // フィルターマッピングを一致させるために必要な情報を取得します
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null){
            requestPath = attribute.toString();
        }

        String servletName = wrapper.getName();

        // このフィルターチェーンに関連するパスマッピングされたフィルタを追加します
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMaps[i], requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - 設定の問題をログに記録
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // サーブレット名に一致するフィルタを2番目に追加します
        for (int i = 0; i < filterMaps.length; i++) {
            if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
                continue;
            }
            if (!matchFiltersServlet(filterMaps[i], servletName))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMaps[i].getFilterName());
            if (filterConfig == null) {
                // FIXME - 設定の問題をログに記録
                continue;
            }
            filterChain.addFilter(filterConfig);
        }

        // 完成したフィルターチェーンを返します
        return filterChain;
    }

フィルターチェーンオブジェクトを作成する際に、ServletContext から登録されたすべてのフィルタの配列を取得し、必要なものをこのリクエストで作成されたフィルターチェーンオブジェクトに追加することがわかります。また、servletContext オブジェクトに登録されたすべてのフィルタは元々配列であり(順序付けられています)、したがってマッチングを遍歴する際も順序付けられています。

3. ServletContext はいつから配列を収集し始めるのですか?#

filterMaps は StandardContext クラス内にあります。

public class StandardContext extends ContainerBase
        implements Context, NotificationEmitter {

  private final ContextFilterMaps filterMaps = new ContextFilterMaps();
  
  //
  @Override
    public void addFilterMap(FilterMap filterMap) {
        validateFilterMap(filterMap);
        // このフィルターマッピングを登録されたセットに追加します
        filterMaps.add(filterMap);
        fireContainerEvent("addFilterMap", filterMap);
    }

}

Tomcat を起動すると、スタックを確認して呼び出し順序を確認できます。

StandardContext.startInternal()-->fireLifecycleEvent()-->ContextConfig.lifecycleEvent()-->ContextConfig.lifecycleEventcon-->ContextConfig.con.figureStart()-->ContextConfig.webConfig()

メソッドを確認します。

protected void webConfig() {
      if (ok) {
          configureContext(webXml);
      }
}

private void configureContext(WebXml webxml) {
      for (FilterMap filterMap : webxml.getFilterMappings()) {
          context.addFilterMap(filterMap);
      }
}

ここで web.xml からすべてのフィルタのセットを収集し、収集したフィルタのセットを配列に変換して StandardContext オブジェクトに設定します。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。