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>の記述順(上から優先)- アノテーション使用時はクラス名の辞書順(例:
AuthFilter→LogFilter)
この順序制御は責任チェーンパターン(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/*:パスプレフィックス一致/*:すべてのパスにマッチ