オタクタイム-デザインパターンの美しさ責任の連鎖パターン(パート2):フレームワークで一般的に使用されるフィルターとインターセプターはどのように実装されていますか?

以前に責任の連鎖パターンの原理と実装を学び、機密性の高い単語フィルタリングフレームワークの例を通じて責任の連鎖パターンの設計意図を示しました。本質的には、ほとんどの設計パターン同様に、コードを分離し、コードの複雑さに対処し、コードをオープンとクローズの原則に適合させ、コードのスケーラビリティを向上させることです

さらに、責任連鎖モデルは、フレームワークの拡張ポイントを提供するためにフレームワーク開発でよく使用され、フレームワークのユーザーがフレームワークのソースコードを変更せずに拡張ポイントに基づいて新しい機能を追加できるようにすることにも言及しました。実際、より具体的には、責任連鎖モデルは、フレームワークのフィルターとインターセプターを開発するために最も一般的に使用されます。今日は、Java開発で一般的に使用される2つのコンポーネントであるServletFilterとSpringInterceptorを使用して、フレームワーク開発でのそのアプリケーションについて具体的に説明します。

サーブレットフィルタ

Servlet Filterは、Java Servlet仕様で定義されているコンポーネントであり、中国語に翻訳されたフィルターです。認証、電流制限、ロギング、検証パラメーターなど、HTTP要求のフィルタリング機能を実装できます。これはServlet仕様の一部であるため、ServletをサポートするすべてのWebコンテナー(Tomcat、Jettyなど)はフィルター機能をサポートします。理解しやすいように、以下に示すように、それがどのように機能するかを説明するための概略図を描きました。

ここに写真の説明を挿入
実際のプロジェクトでは、サーブレットフィルターをどのように使用する必要がありますか?以下に示すような簡単なサンプルコードを作成しました。フィルタを追加するには、javax.servlet.Filterインターフェイスを実装するフィルタクラスを定義し、web.xml構成ファイルで構成するだけです。Webコンテナが起動すると、web.xmlの構成が読み取られ、フィルタオブジェクトが作成されます。リクエストが来ると、サーブレットによって処理される前にフィルターを通過します。


public class LogFilter implements Filter {
    
    
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    
    
    // 在创建Filter时自动调用,
    // 其中filterConfig包含这个Filter的配置参数,比如name之类的(从配置文件中读取的)
  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    
    System.out.println("拦截客户端发送来的请求.");
    chain.doFilter(request, response);
    System.out.println("拦截发送给客户端的响应.");
  }

  @Override
  public void destroy() {
    
    
    // 在销毁Filter时自动调用
  }
}

// 在web.xml配置文件中如下配置:
<filter>
  <filter-name>logFilter</filter-name>
  <filter-class>com.xzg.cd.LogFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>logFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

今のサンプルコードから、フィルターを追加すると非常に便利であることがわかりました。コードを変更する必要はありません。javax.servlet.Filterを実装するクラスを定義してから、構成を変更します。これは、開閉の原則に完全に準拠しています。では、サーブレットフィルターはどのようにしてこのような優れたスケーラビリティを実現するのでしょうか。ご想像のとおり、責任連鎖モデルを使用しています。ここで、そのソースコードを分析することにより、下部にどのように実装されているかを詳しく見ていきます。

前のレッスンでは、責任チェーンモデルの実現には、プロセッサインターフェイス(IHandler)または抽象クラス(Handler)、およびプロセッサチェーン(HandlerChain)が含まれることを説明しました。サーブレットフィルターに対応して、javax.servlet.Filterはプロセッサーインターフェースであり、FilterChainはプロセッサーチェーンです。次に、FilterChainの実装方法に焦点を当てます。

ただし、前述したように、Servletは単なる仕様であり、特定の実装は含まれていません。したがって、ServletのFilterChainは単なるインターフェイス定義です。特定の実装クラスは、Servlet仕様に準拠するWebコンテナによって提供されます。たとえば、ApplicationFilterChainクラスは、Tomcatによって提供されるFilterChainの実装クラスです。ソースコードを以下に示します。

コードを読みやすくするために、コードを簡略化し、デザインのアイデアに関連するコードスニペットのみを保持しました。Tomcatにアクセスして、完全なコードを表示できます。


public final class ApplicationFilterChain implements FilterChain {
    
    
  private int pos = 0; //当前执行到了哪个filter
  private int n; //filter的个数
  private ApplicationFilterConfig[] filters;
  private Servlet servlet;
  
  @Override
  public void doFilter(ServletRequest request, ServletResponse response) {
    
    
    if (pos < n) {
    
    
      ApplicationFilterConfig filterConfig = filters[pos++];
      Filter filter = filterConfig.getFilter();
      filter.doFilter(request, response, this);
    } else {
    
    
      // filter都处理完毕后,执行servlet
      servlet.service(request, response);
    }
  }
  
  public void addFilter(ApplicationFilterConfig filterConfig) {
    
    
    for (ApplicationFilterConfig filter:filters)
      if (filter==filterConfig)
         return;

    if (n == filters.length) {
    
    //扩容
      ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT];
      System.arraycopy(filters, 0, newFilters, 0, n);
      filters = newFilters;
    }
    filters[n++] = filterConfig;
  }
}

ApplicationFilterChainのdoFilter()関数のコード実装はより巧妙であり、実際には再帰呼び出しです。各フィルター(LogFilterなど)のdoFilter()のコードを使用して、ApplicationFilterChainコードの12行目を直接置き換えることができ、一目で再帰呼び出しであることがわかります。以下のように交換しました。


  @Override
  public void doFilter(ServletRequest request, ServletResponse response) {
    
    
    if (pos < n) {
    
    
      ApplicationFilterConfig filterConfig = filters[pos++];
      Filter filter = filterConfig.getFilter();
      //filter.doFilter(request, response, this);
      //把filter.doFilter的代码实现展开替换到这里
      System.out.println("拦截客户端发送来的请求.");
      chain.doFilter(request, response); // chain就是this
      System.out.println("拦截发送给客户端的响应.")
    } else {
    
    
      // filter都处理完毕后,执行servlet
      servlet.service(request, response);
    }
  }

この実装は主に、doFilter()メソッドで双方向のインターセプトをサポートすることを目的としています。このメソッドは、クライアントから送信された要求とクライアントに送信された応答の両方をインターセプトできます。LogFilterの例を組み合わせて、後で比較できます。スプリングインターセプターを理解するようになります。ただし、前のレッスンで示した2つの実装では、ビジネスロジックの実行の前後に処理コードを追加することはできません。

スプリングインターセプター

サーブレットフィルターについて話しましたが、今は機能が非常に似ているものについて話します。SpringInterceptorは、中国語に翻訳されたものです。英語の単語と中国語の翻訳は異なりますが、この2つは基本的に概念と見なすことができ、どちらもHTTP要求を傍受するために使用されます。

それらの違いは、Servlet FilterがServlet仕様の一部であり、実装がWebコンテナに依存することです。SpringInterceptorはSpringMVCフレームワークの一部であり、実装はSpringMVCフレームワークによって提供されます。クライアントから送信されたリクエストは、最初にサーブレットフィルタを通過し、次にSpring Interceptorを通過し、最後に特定のビジネスコードに到達します。以下に示すように、リクエストの処理フローを説明するために絵を描きました。

ここに写真の説明を挿入
プロジェクトでは、Spring Interceptorをどのように使用する必要がありますか?以下に示すような簡単なサンプルコードを作成しました。LogInterceptorによって実装された関数は、現在LogFilterとまったく同じですが、実装が少し異なります。LogFilterは関数doFilter()で要求と応答をインターセプトし、LogInterceptorはpreHandle()で要求をインターセプトし、postHandle()で応答をインターセプトします。


public class LogInterceptor implements HandlerInterceptor {
    
    

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    
    System.out.println("拦截客户端发送来的请求.");
    return true; // 继续后续的处理
  }

  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
    
    System.out.println("拦截发送给客户端的响应.");
  }

  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
    
    System.out.println("这里总是被执行.");
  }
}

//在Spring MVC配置文件中配置interceptors
<mvc:interceptors>
   <mvc:interceptor>
       <mvc:mapping path="/*"/>
       <bean class="com.xzg.cd.LogInterceptor" />
   </mvc:interceptor>
</mvc:interceptors>

同様に、基盤となるSpringInterceptorがどのように実装されているかを分析してみましょう。

もちろん、責任連鎖モデルに基づいて実装されています。その中で、HandlerExecutionChainクラスは、責任連鎖モデルのプロセッサチェーンです。TomcatのApplicationFilterChainと比較すると、その実装はより明確なロジックを備えており、主に要求と応答のインターセプト作業を2つの関数に分割するため、再帰的に実装する必要はありません。HandlerExecutionChainのソースコードを以下に示します。同様に、コードを簡略化し、キーコードのみを保持しました。


public class HandlerExecutionChain {
    
    
 private final Object handler;
 private HandlerInterceptor[] interceptors;
 
 public void addInterceptor(HandlerInterceptor interceptor) {
    
    
  initInterceptorList().add(interceptor);
 }

 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    
  HandlerInterceptor[] interceptors = getInterceptors();
  if (!ObjectUtils.isEmpty(interceptors)) {
    
    
   for (int i = 0; i < interceptors.length; i++) {
    
    
    HandlerInterceptor interceptor = interceptors[i];
    if (!interceptor.preHandle(request, response, this.handler)) {
    
    
     triggerAfterCompletion(request, response, null);
     return false;
    }
   }
  }
  return true;
 }

 void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
    
    
  HandlerInterceptor[] interceptors = getInterceptors();
  if (!ObjectUtils.isEmpty(interceptors)) {
    
    
   for (int i = interceptors.length - 1; i >= 0; i--) {
    
    
    HandlerInterceptor interceptor = interceptors[i];
    interceptor.postHandle(request, response, this.handler, mv);
   }
  }
 }

 void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
   throws Exception {
    
    
  HandlerInterceptor[] interceptors = getInterceptors();
  if (!ObjectUtils.isEmpty(interceptors)) {
    
    
   for (int i = this.interceptorIndex; i >= 0; i--) {
    
    
    HandlerInterceptor interceptor = interceptors[i];
    try {
    
    
     interceptor.afterCompletion(request, response, this.handler, ex);
    } catch (Throwable ex2) {
    
    
     logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
    }
   }
  }
 }
}

Springフレームワークでは、DispatcherServletのdoDispatch()メソッドを使用してリクエストを配信し、実際のビジネスロジックの実行の前後にHandlerExecutionChainのapplyPreHandle()関数とapplyPostHandle()関数を実行して、インターセプト関数を実装します。特定のコードの実装は非常に単純であり、自分で作成できるはずなので、ここではリストしません。興味のある方は、ご自身でご確認ください。

おすすめ

転載: blog.csdn.net/zhujiangtaotaise/article/details/110486470