JavaWebにおけるFilterの活用と実装方法

Filterの役割と必要性

Webアプリケーションにおいて、複数のServletで共通の処理(例:ユーザー認証チェックや文字エンコーディング設定)を個別に実装すると、コードの重複が発生し保守性が低下します。このような課題を解決するために、Jakarta Servlet仕様で提供されるFilter(フィルター)を使用します。

Filterの基本概念

Filterは、対象のServletが実行される前後に任意の処理を挿入できるコンポーネントです。典型的な用途として以下が挙げられます:

  • ユーザー認証の統一処理
  • リクエスト/レスポンスのエンコーディング設定
  • アクセスログの記録
  • 不正リクエストの遮断

Filterの実装手順

Filterを実装するには以下の2ステップが必要です。

1. Filterクラスの作成

jakarta.servlet.Filterインターフェースを実装し、以下のメソッドをオーバーライドします:

  • init():Filterインスタンス生成時に1回だけ実行(初期化処理)
  • doFilter():各リクエストごとに実行(メインロジック)
  • destroy():Filter破棄前に1回だけ実行(クリーンアップ)

2. 設定によるマッピング

Filterを有効にするには、web.xmlでの宣言的設定または@WebFilterアノテーションによるアノテーションベース設定のいずれかを行います。

<filter>
    <filter-name>authFilter</filter-name>
    <filter-class>com.example.AuthFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>authFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

または:

@WebFilter("/*")
public class AuthFilter implements Filter { ... }

実行順序とライフサイクル

Filterはデフォルトでアプリケーション起動時にインスタンス化され、シングルトンとして動作します。複数のFilterが存在する場合、実行順序は以下の通りです:

  • web.xmlでは<filter-mapping>の記述順(上から優先)
  • アノテーション使用時はクラス名の辞書順(例:AuthFilterLogFilter

この順序制御は責任チェーンパターン(Chain of Responsibility Pattern)に基づいており、実行フローを実行時に動的に構成できます。

実装例:認証チェックFilter

以下は、ログイン状態を確認し未認証ユーザーをログインページへリダイレクトするFilterの実装例です。

package com.example.web.filter;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;

public class AuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        HttpSession session = req.getSession(false);
        String path = req.getServletPath();

        // 認証不要パスまたはログイン済みなら許可
        if ("/welcome".equals(path) || "/user/login".equals(path) || 
            (session != null && session.getAttribute("username") != null)) {
            chain.doFilter(request, response);
        } else {
            res.sendRedirect(req.getContextPath() + "/welcome");
        }
    }
}

Servlet側の変更

認証ロジックをFilterに移管した後、Servletは純粋な業務ロジックのみを担当します:

@WebServlet({"/dept/list", "/dept/delete", "/dept/detail", "/dept/save", "/dept/modify"})
public class DepartmentServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        String path = req.getServletPath();
        switch (path) {
            case "/dept/list" -> showList(req, resp);
            case "/dept/delete" -> delete(req, resp);
            case "/dept/detail" -> showDetail(req, resp);
            case "/dept/save" -> save(req, resp);
            case "/dept/modify" -> update(req, resp);
        }
    }
}

URLパターンのマッチングルール

  • /exact/path:完全一致
  • *.do:拡張子マッチ(先頭に/不要)
  • /prefix/*:パスプレフィックス一致
  • /*:すべてのパスにマッチ

タグ: JavaWeb Servlet filter 責任チェーンパターン jakarta.servlet

6月18日 00:40 投稿