Spring Boot 環境での主要なトラフィック制御インタフェースの選定指針

Spring Boot におけるインプレメント層別アプローチの比較

Spring Boot 開発において、横断的関心事をどのように扱うかは重要な設計課題となります。単純に HandlerInterceptor を実装するだけでは、複雑な要件に対応できない場合があります。本記事では、フレームワークが提供する異なる境界レベルの 6 つの制御メカニズムを技術的な観点から整理し、それぞれの適用範囲と注意点について解説します。

1. Servlet Filter レベル

最も外部に位置するコンポーネントであり、サーブレットコンテナのライフサイクルに直接組み込まれます。主にセキュリティチェックやロギングなどのグローバルな処理に適しています。

@WebFilter("/*")
public class LatencyLoggingFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(LatencyLoggingFilter.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        long startTime = System.currentTimeMillis();
        try {
            chain.doFilter(request, response);
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            logger.info("API Response Time: {} ms", duration);
        }
    }
}

運用上の注意

  • この段階では Spring コンテナのスコープ外にあるため、依存注入(DI)を利用するには WebApplicationContextUtils を経由する必要があります。
  • エラーハンドリングが発生しても必ず finally ブロックでリソース解放やログ出力を行うよう設計すべきです。

2. HandlerInterceptor

Spring MVC の DispatcherServlet によって制御される層です。コントローラー実行前後のアプローチが可能です。

public class ApiAccessGuard implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) {
        String authHeader = req.getHeader("X-Api-Key");
        if (!validateKey(authHeader)) {
            res.setStatus(HttpStatus.FORBIDDEN.value());
            return false;
        }
        // メソッド呼び出し前
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest req, HttpServletResponse res, Object handler, ModelAndView modelAndView) {
        if (res != null && !res.isCommitted()) {
             // レスポンス書き込み後の修正が可能
        }
    }
}

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ApiAccessGuard())
                .addPathPatterns("/secure/**")
                .excludePathPatterns("/public/**");
    }
}

実装時の制約

  • postHandleafterCompletion では、すでにレスポンスがコミットされている可能性があるため、状態確認が必要です。
  • 静的リソースへのアクセスも対象となるため、除外パスの定義は必須です。
  • 複数のインタセプターが存在する場合、登録順が実行順序に影響します。

3. AOP アスペクト

ビジネスロジックの実装クラスに対して作用します。トランザクション管理やキャッシュ制御など、ドメイン固有の関心事に適しています。

@Aspect
@Component
public class MethodExecutionMonitor {

    @Around("@annotation(com.example.cache.EnableCache)")
    public Object executeWithCaching(ProceedingJoinPoint joinPoint) throws Throwable {
        String signature = joinPoint.getSignature().toShortString();
        
        // キー生成ロジック
        String cacheId = "cache:" + signature;
        Object cachedData = getFromRedis(cacheId);

        if (cachedData != null) {
            return cachedData;
        }

        Object result = joinPoint.proceed();
        
        // 結果保存
        saveToRedis(cacheId, result, Duration.ofMinutes(10));
        return result;
    }
}

機能制限

  • Spring が管理している Bean にのみ有効です。new キーワードで作成されたオブジェクトには作用しません。
  • @Transactional との組み合わせでは、トランザクションアスペクトより先に実行させる必要がある場合、優先度を調整する必要があります。

4. RestTemplate インタセプター

外部システムへのリクエストを送信する際、共通ヘッダーの付与や追跡 ID の追加を実現するためのクライアントサイド機構です。

public class CorrelationIdInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) {
        HttpRequest wrappedRequest = new HttpHeadersDecorator(request, headers -> {
            headers.set("X-Correlation-ID", UUID.randomUUID().toString());
        });
        return execution.execute(wrappedRequest, body);
    }
}

設定方法

@Bean
public RestTemplate restTemplate(ClientHttpRequestInterceptor correlationInterceptor) {
    RestTemplate template = new RestTemplate();
    template.setInterceptors(Collections.singletonList(correlationInterceptor));
    return template;
}

留意点: 文字列データを送信する際はエンコード形式の確認が必要となり、HTTPS通信の場合、SSLContextの設定が別途必要になることがあります。

5. OpenFeign インタセプター

マイクロサービス間通信において、デクレーティブなHTTP クライアントとして動作する Feign クライアント向けの拡張点です。

public class TenantContextRequestInterceptor implements RequestInterceptor {
    
    @Override
    public void apply(RequestTemplate template) {
        SecurityContext ctx = SecurityContextHolder.getContext();
        if (ctx.getAuthentication() != null) {
            template.header("Authorization", "Bearer " + ctx.getAuthentication().getToken());
            template.header("X-Tenant-ID", ctx.getTenantIdentifier());
        }
    }
}

構成例:

@Configuration
@FeignClient(name = "user-service")
public class FeignClientsConfig {
    @Bean
    public RequestInterceptor tenantInterceptor() {
        return new TenantContextRequestInterceptor();
    }
}

GET リクエストボディの扱いや、フォームデータ送信時には特別なライブラリの併用を検討してください。

6. WebFilter (Spring WebFlux)

リアクティブスタックを用いる WebFlux アプリケーションで使用される非ブロック型のフィルタリング機構です。

@Component
public class GlobalSecurityFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().set("Access-Control-Allow-Origin", "*");
        
        return chain.filter(exchange).doOnTerminate(() -> {
            // 完了後のクリーンアップ
        });
    }
}

要件: 従来のサーブレットベースではなく、リアクティブフロー(Mono/Flux)に基づいた処理が前提となります。

各機構の特性比較表

種別 実行タイミング オーバーヘッド 主な用途
Servlet Filter 最上位(リクエスト到着時) ログ記録、認証基盤、圧縮処理
HandlerInterceptor MVC フロー内(コントローラー直前) ユーザー権限判定、パラメータ変換
AOP メソッド実行時 キャッシュ、トランザクション、監査
RestTemplate 送信要求時 他システムへのヘッダー付与
Feign RPC 呼び出し時 分散トレーシング、サービス間認証
WebFilter リアクティブ全経路 極めて高 CORS、非同期セキュリティ処理

パフォーマンスと最適化の原則

  1. チェーン順序の考慮: 基本的には Filter → Interceptor → AOP の順で実行されます。重い処理は可能な限り手前のレイヤー、または必要最小限の層で行うことが推奨されます。
  2. 適切なツールの選定: 簡易的な認可は Interceptor で、ビジネスロジックの横断管理は AOP で、マイクロサービス連携は Feign で担当するとアーキテクチャが明確になります。
  3. 監視の強化: インタセプター自体がボトルネックになる場合があります。Arthas 等のツールを使用して、特定のインタセプターの処理時間を特定しましょう。
# HandlerInterceptor のメソッドトリミング
trace com.example.interceptor.* 'returnObj' 

# AOP アスペクトの監視
watch com.example.aspect.*Aspect '#cost>5' 

タグ: spring-boot Java servlet-filter handler-interceptor AOP

7月15日 23:06 投稿