- Spring
Spring Filter・Interceptor・AOPの違い|比較表と実行順
Filter・Interceptor・AOP の違いは「どこで動き、何が見えるか」です。Filter は DispatcherServlet の手前で HTTP のリクエストとレスポンスそのものを扱い、HandlerInterceptor は DispatcherServlet の中で「どのコントローラーのメソッドが呼ばれるか」を知ったうえで前後に入り、AOP は Spring の Bean のメソッド呼び出しに入って引数と戻り値を扱います。
この記事では、同じ「処理時間をログに出す」処理を3通りで書いたコード全文、@ControllerAdvice と Spring Security のフィルターも加えた比較表、リクエストが通る順番の図、用途別の選び方を1ページにまとめました。
確認環境:Spring Boot 4.1.1/Spring Framework 7.0.9/Spring Security 7.1.1 の公式リファレンスと Javadoc で確認(確認日 2026年10月7日)。実行順は公式ドキュメントの記述に基づいて示しています。
Filter・Interceptor・AOP の早見表
| 仕組み | 動く位置 | 見えるもの | 向いている用途 |
|---|---|---|---|
| Filter | DispatcherServlet の手前 | HTTP のリクエスト・レスポンス | 文字コード、CORS、全リクエストのログ |
| Spring Security のフィルター | Filter の並びの中 | リクエストと認証情報 | 認証、URL 単位の認可 |
| HandlerInterceptor | DispatcherServlet の中、ハンドラーの前後 | リクエストとハンドラー(メソッド情報) | コントローラー単位の前処理・計測 |
@Controller |
コントローラーの周辺 | 例外、モデル、バインダー | 例外を共通のレスポンスに変換 |
AOP(@Aspect) |
Bean のメソッド呼び出し | 引数、戻り値、例外 | サービス層のログ、監査、計測 |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Spring Filter・Interceptor・AOPの違い|比較表と実行順」
同じ「処理時間のログ」を3通りで書く
題材をそろえると、それぞれが受け取れる情報の違いがはっきりします。パッケージは com.example.demo としています。
依存関係(Spring Boot 4.x・Maven)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<!-- @Aspect を使う場合 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aspectj</artifactId>
</dependency>
</dependencies>
Spring Boot 4.0 の移行ガイドでは、spring-boot-starter-web は spring-boot-starter-webmvc に、spring-boot-starter-aop は spring-boot-starter-aspectj に名前が変わったと案内されています。3.x の場合は変更前の名前を使います。
1. Filter(OncePerRequestFilter)
package com.example.demo.web;
import java.io.IOException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@Component
public class TimingFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(TimingFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
long start = System.nanoTime();
try {
filterChain.doFilter(request, response); // 次のフィルター、最後は DispatcherServlet へ
} finally {
long ms = (System.nanoTime() - start) / 1_000_000;
log.info("[Filter] {} {} status={} {}ms",
request.getMethod(), request.getRequestURI(), response.getStatus(), ms);
}
}
/** 対象から外したいパスをここで指定する */
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
return request.getRequestURI().startsWith("/assets/");
}
}
OncePerRequestFilter は、Javadoc によると「1回のリクエストのディスパッチにつき1度だけ実行されることを保証するためのフィルターの基底クラス」です。shouldNotFilter の既定は false(すべて対象)で、非同期ディスパッチとエラーディスパッチでは既定で実行されません(shouldNotFilterAsyncDispatch・shouldNotFilterErrorDispatch の既定が true)。
Filter から見えるのは URL・ヘッダー・ステータスなど HTTP の情報で、どのコントローラーが呼ばれたかはわかりません。
Filter の登録方法
Spring Boot では、Filter を Bean として宣言すれば Boot が組み込みコンテナに登録します。上のように @Component を付けるのがいちばん短い方法です。URL パターンや順序を細かく決めたいときは、@Component を外して FilterRegistrationBean で登録します。
package com.example.demo.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import com.example.demo.web.TimingFilter;
@Configuration(proxyBeanMethods = false)
public class FilterConfig {
@Bean
public FilterRegistrationBean<TimingFilter> timingFilterRegistration() {
FilterRegistrationBean<TimingFilter> registration =
new FilterRegistrationBean<>(new TimingFilter());
registration.addUrlPatterns("/api/*");
registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 10);
return registration;
}
}
2. HandlerInterceptor + WebMvcConfigurer
package com.example.demo.web;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
public class TimingInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory.getLogger(TimingInterceptor.class);
private static final String START_ATTR = TimingInterceptor.class.getName() + ".start";
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
request.setAttribute(START_ATTR, System.nanoTime());
return true; // false を返すと、ここで処理を打ち切る
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
Object start = request.getAttribute(START_ATTR);
if (start == null) {
return;
}
long ms = (System.nanoTime() - (Long) start) / 1_000_000;
String target = (handler instanceof HandlerMethod hm)
? hm.getBeanType().getSimpleName() + "#" + hm.getMethod().getName()
: handler.getClass().getSimpleName();
log.info("[Interceptor] {} status={} {}ms", target, response.getStatus(), ms);
}
}
登録は WebMvcConfigurer の addInterceptors で行います。
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.example.demo.web.TimingInterceptor;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TimingInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/health")
.order(0);
}
}
Interceptor は handler 引数から「どのクラスのどのメソッドか」を取り出せます。 HandlerMethod を使うと、メソッドに付いたアノテーションも調べられます。一方、コントローラーのメソッドに渡る引数(@RequestBody で変換されたオブジェクトなど)と戻り値は受け取れません。
3. AOP(@Aspect)
package com.example.demo.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TimingAspect {
private static final Logger log = LoggerFactory.getLogger(TimingAspect.class);
@Around("within(com.example.demo.service..*)")
public Object measure(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
try {
return pjp.proceed(); // 本体のメソッドを実行
} finally {
long ms = (System.nanoTime() - start) / 1_000_000;
log.info("[AOP] {} args={} {}ms",
pjp.getSignature().toShortString(), pjp.getArgs().length, ms);
}
}
}
@Aspect だけではコンポーネントスキャンの対象にならないため、@Component も付けます(Spring Framework リファレンス)。AOP は HTTP とは関係なく、サービスやリポジトリなど Spring の Bean のメソッドならどこにでも入れられます。 引数は pjp.getArgs()、戻り値は pjp.proceed() の結果として受け取れます。
3つのコードで取れる情報の違い
| 取れる情報 | Filter | Interceptor | AOP |
|---|---|---|---|
| URL・HTTP メソッド・ヘッダー | 取れる | 取れる | 引数で受け取れば可 |
| レスポンスのステータス | 取れる | 取れる | 対象外 |
| 呼ばれるクラスとメソッド | 対象外 | 取れる | 取れる |
| メソッドの引数と戻り値 | 対象外 | 対象外 | 取れる |
5つの仕組みの比較表
@ControllerAdvice と Spring Security のフィルターも加えて比べます。表は2つに分けています。
管理する主体・絞り方・順序
| 仕組み | 管理する主体 | 対象の絞り方 | 順序の指定 |
|---|---|---|---|
| Filter | Servlet コンテナ | URL パターン、should |
@Order、set |
| Security のフィルター | Filter |
Security の対象 |
add/add |
| HandlerInterceptor | Spring MVC(DispatcherServlet) | add/exclude |
order((既定 0) |
@Controller |
Spring MVC | annotations・パッケージ・assignable |
ローカルの後にグローバル |
AOP(@Aspect) |
Spring コンテナ(プロキシ) | ポイントカット式 | @Order(小さいほど外側) |
補足です。
- Filter:Servlet の仕様の部品です。Spring Boot では Bean として宣言すると組み込みコンテナに登録されます。
- Spring Security のフィルター:
DelegatingFilterProxyが Servlet コンテナと Spring の橋渡しをし、FilterChainProxyがSecurityFilterChainに並んだフィルターを順に呼びます(Spring Security リファレンス)。 @ControllerAdvice:コントローラー内の@ExceptionHandlerが先に適用され、@ControllerAdviceのものはその後です。@ModelAttributeと@InitBinderは逆で、グローバルが先です。- AOP:
@Orderの値が小さいアスペクトほど優先度が高く、入るときは先、出るときは後に動きます。
触れられる情報と例外の行き先
| 仕組み | リクエスト本文 | ハンドラー | 引数・戻り値 |
|---|---|---|---|
| Filter | 触れられる(差し替えも可) | わからない | 触れられない |
| Security のフィルター | 触れられる | わからない | 触れられない |
| HandlerInterceptor | 差し替えは想定外 | わかる | 触れられない |
@Controller |
書き込み前の本文に入れる | 対象を絞れる | 例外・モデルを扱う |
AOP(@Aspect) |
引数として受け取る | メソッド情報がわかる | 触れられる |
HandlerInterceptor の Javadoc には、「Filter のほうが強力で、チェーンに渡すリクエストとレスポンスのオブジェクトを差し替えられる」とあります。リクエスト本文を読み直せるように包む、レスポンスを圧縮するといった処理は Filter の役割です。
投げた例外が @ControllerAdvice の @ExceptionHandler に届くかどうかも、選ぶときの大事な基準です。
| 例外を投げた場所 | @Exception |
行き先 |
|---|---|---|
| Filter | 届かない | コンテナのエラー処理(/ へのディスパッチ) |
| Security のフィルター | 届かない | Exception が認証開始または拒否の応答に変換 |
| HandlerInterceptor | 届く | DispatcherServlet の例外解決の対象 |
| コントローラーに掛けた AOP | 届く | コントローラーが投げた例外と同じ扱い |
| サービスに掛けた AOP | 呼び出し元次第 | コントローラーまで伝われば届く |
Spring Framework リファレンスは、@ExceptionHandler を呼び出す仕組み(HandlerExceptionResolver)の対象を「リクエストのマッピング中、またはリクエストハンドラー(@Controller など)から投げられた例外」と説明しています。Filter は DispatcherServlet の外側にあるため、この対象に入りません。解決されなかった例外はコンテナまで伝わり、コンテナがエラー用の URL(例:/error)へ ERROR ディスパッチを行います。
Filter の中でエラー応答を返したいときは、例外を投げずにその場でレスポンスを書きます。
package com.example.demo.web;
import java.io.IOException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
public class RequestIdRequiredFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
String requestId = request.getHeader("X-Request-Id");
if (requestId == null || requestId.isBlank()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"error\":\"X-Request-Id ヘッダーが必要です\"}");
return; // 次へ渡さずに応答する
}
filterChain.doFilter(request, response);
}
}
リクエストが通る順番
正常時
sequenceDiagram
participant C as クライアント
participant F as Filter
participant S as Securityのフィルター
participant D as DispatcherServlet
participant I as Interceptor
participant A as AOP
participant H as コントローラー
C->>F: リクエスト
F->>S: chain.doFilter
S->>D: 認証・認可を通過
D->>I: preHandle
I-->>D: true
D->>A: ハンドラーを呼び出す
A->>H: proceed
H-->>A: 戻り値
A-->>D: 戻り値
D->>I: postHandle
D->>I: afterCompletion
D-->>S: 戻る
S-->>F: 戻る
F-->>C: レスポンス
入るときは外側から、出るときは内側から順に戻ります。図の AOP は「コントローラーにアスペクトを掛けた場合」で、サービスに掛けた場合はコントローラーの内側(サービスを呼ぶ手前)に入ります。Filter どうしの前後は @Order などの設定で決まり、図は自作の Filter を Spring Security より前に置いた場合です。
コントローラーで例外が起きたとき
sequenceDiagram
participant C as クライアント
participant F as Filter
participant D as DispatcherServlet
participant I as Interceptor
participant H as コントローラー
participant E as ControllerAdvice
C->>F: リクエスト
F->>D: chain.doFilter
D->>I: preHandle
I-->>D: true
D->>H: ハンドラーを呼び出す
H-->>D: 例外
Note over D,I: postHandle は呼ばれない
D->>E: ExceptionHandler で応答を作る
E-->>D: エラー応答
D->>I: afterCompletion
D-->>F: 戻る
F-->>C: エラー応答
@ExceptionHandler で応答に変換されると、Filter から見たときは通常どおり chain.doFilter から戻ってきます。前掲の TimingFilter が finally でログを出しているのは、正常時も例外時も同じ場所で計測を終えられるためです。
preHandle・postHandle・afterCompletion が呼ばれる条件
| 状況 | preHandle | postHandle | afterCompletion |
|---|---|---|---|
| 正常に完了 | 呼ばれる | 呼ばれる | 呼ばれる |
preHandle が false を返した |
呼ばれる | 呼ばれない | そのインターセプターでは呼ばれない |
| ハンドラーが例外を投げた | 呼ばれる | 呼ばれない | 呼ばれる |
@Response/Response |
呼ばれる | 呼ばれるが応答は確定済み | 呼ばれる |
それぞれの根拠は HandlerInterceptor の Javadoc と Spring Framework リファレンス(7.0.9)です。
- preHandle:
HandlerMappingがハンドラーを決めた後、HandlerAdapterがハンドラーを呼ぶ前。falseを返すと残りのチェーンとハンドラーは実行されません。 - postHandle:ハンドラーが正常に実行された後、ビューの描画前。複数ある場合は登録の逆順です。
- afterCompletion:ビューの描画後。ハンドラーの結果にかかわらず呼ばれますが、そのインターセプターの preHandle が正常に完了して
trueを返した場合だけです。こちらも逆順です。 @ResponseBodyとResponseEntity:レスポンスはpostHandleより前にHandlerAdapterの中で書き込まれ確定します。postHandleでヘッダーを足しても間に合わないため、公式はResponseBodyAdviceを実装して@ControllerAdviceの Bean として宣言する方法を案内しています。
後始末(計測の終了、リソースの解放)は afterCompletion に書くと、例外時にも動きます。
用途別の選び方の早見表
| 用途 | 選ぶ仕組み | 理由 |
|---|---|---|
| 認証(ログイン状態の確認) | Spring Security | フィルターの並びの早い段階で判定できる |
| URL 単位の認可 | Spring Security(authorize) |
公式が Interceptor より推奨 |
| メソッド単位の認可 | Spring Security(@Pre) |
Spring AOP の上に作られた仕組み |
| CORS | Cors または MVC の CORS 設定 |
Security 併用時は Cors を前段に |
| 全リクエストのアクセスログ | Filter | 静的ファイルやエラー応答も含めて拾える |
| リクエスト ID の採番 | Filter | 以降のすべての処理で使える |
| リクエスト本文の加工・包み直し | Filter | オブジェクトを差し替えられる |
| コントローラー別の処理時間 | HandlerInterceptor | ハンドラーの情報が取れる |
| アノテーションに応じた前処理 | HandlerInterceptor | Handler で判定できる |
| レスポンス本文への共通項目の追加 | Response |
書き込み前に本文へ入れる |
| 例外を共通の形式で返す | @Controller |
例外の種類ごとに応答を決められる |
| サービス層のログ・計測 | AOP | 引数と戻り値が取れる |
| 操作の記録(監査ログ) | AOP | 業務メソッド単位で記録できる |
| トランザクション | @Transactional |
Spring が用意した AOP の仕組み |
迷ったときは次の順で考えると決めやすくなります。
- HTTP そのものの話(ヘッダー、文字コード、本文の包み直し)なら Filter
- 認証・認可なら Spring Security
- 「どのコントローラーか」で動きを変えるなら HandlerInterceptor
- 例外をレスポンスに変えるなら
@ControllerAdvice - Web 以外(バッチやメッセージ受信)からも通る業務メソッドに掛けるなら AOP
認可を Interceptor から Spring Security に移す例
Spring Framework リファレンスの Interceptors のページには、次の注意書きがあります(日本語訳)。
インターセプターは、アノテーション付きコントローラーのパスマッチングと食い違う可能性があるため、セキュリティ層としては理想的ではありません。一般的には、Spring Security を使うか、Servlet フィルターチェーンに統合された同様の方法で、できるだけ早い段階で適用することを推奨します。
インターセプターの対象は addPathPatterns に書いたパターンで決まり、コントローラーのマッピングとは別に管理されます。2か所の書き方がずれると、意図した URL に掛からないことがあります。
移す前(Interceptor で管理者かどうかを判定)
package com.example.demo.web;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
public class AdminOnlyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpSession session = request.getSession(false);
Object role = (session != null) ? session.getAttribute("role") : null;
if (!"ADMIN".equals(role)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
return true;
}
}
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.example.demo.web.AdminOnlyInterceptor;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AdminOnlyInterceptor())
.addPathPatterns("/admin/**");
}
}
移した後(SecurityFilterChain で URL 単位に指定)
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/assets/**", "/signup").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
// 認証方式(ログインの方法)の設定をここに加える
return http.build();
}
}
Spring Security リファレンスによると、認可を担当する AuthorizationFilter は既定で Spring Security のフィルターチェーンの最後に置かれ、その後に DispatcherServlet が動きます。コントローラーに届く前に判定が終わるので、Interceptor・AOP・コントローラーはすべて認可済みの前提で書けます。
メソッド単位で指定する場合
URL ではなく業務メソッドに対して権限を決めたいときは、メソッドセキュリティを使います。
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
}
package com.example.demo.service;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class MemberAdminService {
@PreAuthorize("hasRole('ADMIN')")
public void deactivate(Long memberId) {
// ADMIN ロールを持つ場合だけ実行される
}
}
メソッドセキュリティは Spring AOP を使って作られており、拒否されたときは AccessDeniedException が投げられ、ExceptionTranslationFilter が 403 の応答に変換します(Spring Security リファレンス)。
自作の Filter を Security のチェーンに入れるとき
自作の Filter を Spring Security のフィルターの並びの中に置きたいときは、addFilterBefore/addFilterAfter で位置を指定します。前掲の RequestIdRequiredFilter をログアウト処理のフィルターより前に入れる例です。
package com.example.demo.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import com.example.demo.web.RequestIdRequiredFilter;
@Configuration
public class SecurityConfig {
@Bean
public RequestIdRequiredFilter requestIdRequiredFilter() {
return new RequestIdRequiredFilter();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, RequestIdRequiredFilter filter) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.addFilterBefore(filter, LogoutFilter.class);
return http.build();
}
/** Bean にした Filter を、組み込みコンテナには登録しない */
@Bean
public FilterRegistrationBean<RequestIdRequiredFilter> requestIdFilterRegistration(RequestIdRequiredFilter filter) {
FilterRegistrationBean<RequestIdRequiredFilter> registration = new FilterRegistrationBean<>(filter);
registration.setEnabled(false);
return registration;
}
}
Spring Security リファレンスには、Filter を @Component などで Bean にすると Spring Boot が組み込みコンテナにも自動登録するため、「コンテナから1回、Spring Security から1回」の計2回、違う順序で呼ばれることがあると書かれています。Bean にしたい場合は、上の3つ目の Bean のように FilterRegistrationBean の enabled を false にして、コンテナへの登録を止めます。
javax から jakarta への import 対応表
Spring Boot 3.0 の移行ガイドに「Jakarta EE は javax ではなく jakarta パッケージを使う」とあり、Spring Boot 4 は Jakarta EE 11・Servlet 6.1 を基準にしています。2.x 向けのサンプルを貼るときは、import を次のように読み替えます。
| Boot 2.x まで | Boot 3.x・4.x |
|---|---|
javax. |
jakarta. |
javax. |
jakarta. |
javax. |
jakarta. |
javax. |
jakarta. |
javax. |
jakarta. |
javax. |
jakarta. |
javax. |
jakarta. |
javax. |
jakarta. |
javax. |
jakarta. |
org.springframework.*(OncePerRequestFilter・HandlerInterceptor など)と org.aspectj.*(@Aspect・@Around など)のパッケージ名はそのままです。
版ごとの基準は次のとおりです。
| 項目 | Spring Boot 3.0 | Spring Boot 4.0 |
|---|---|---|
| Java | 17 以上 | 17 以上 |
| Spring Framework | 6.0 | 7.x |
| Jakarta EE | 10(Servlet 6.0) | 11(Servlet 6.1) |
| Web のスターター | spring- |
spring- |
| AOP のスターター | spring- |
spring- |
よくある質問
Q. Filter と Interceptor の両方で同じログを出す必要はありますか? A. 片方で足ります。静的ファイルやエラー応答も含めて全リクエストを記録したいなら Filter、コントローラー名とメソッド名を残したいなら Interceptor が合います。
Q. Filter で投げた例外を @ControllerAdvice で受け取れますか?
A. 受け取れません。@ExceptionHandler の対象は DispatcherServlet の中で起きた例外です。Filter ではその場でレスポンスを書くか、判定そのものをコントローラーに近い側(Interceptor や AOP)に移します。
Q. Interceptor でリクエスト本文(JSON)を読めますか? A. 本文は一度読むと読み直せないストリームなので、Interceptor で読む設計は合いません。変換後のオブジェクトを見たいなら AOP でコントローラーの引数を受け取る、本文そのものを扱いたいなら Filter で包み直す、のどちらかにします。
Q. AOP はコントローラーにも掛けられますか? A. 掛けられます。コントローラーも Spring の Bean なので、ポイントカットの対象にできます。ただし HTTP のステータスやヘッダーは引数に無いかぎり見えないため、Web 寄りの情報が必要なら Interceptor のほうが書きやすくなります。
まとめ
- Filter は DispatcherServlet の手前で HTTP のリクエスト・レスポンスを扱い、オブジェクトの差し替えもできる
- HandlerInterceptor は DispatcherServlet の中で、どのハンドラーが呼ばれるかを知ったうえで前後に入る
- AOP は Bean のメソッド呼び出しに入り、引数・戻り値・例外を扱える。Web 以外の入口からも効く
@ExceptionHandlerに届くのは DispatcherServlet の中で起きた例外。Filter の例外は届かない- postHandle はハンドラーが正常に終わったときだけ、afterCompletion は preHandle が
trueを返していれば結果にかかわらず呼ばれる - 認証・認可は Spring Security に任せるのが公式の推奨。Interceptor のパス指定はコントローラーのマッピングと別管理になる
- 2.x のサンプルは
javax.servletをjakarta.servletに読み替える。Boot 4 ではスターター名も変わっている
参考・出典
確認日はいずれも 2026年10月7日です。
- Spring Framework リファレンス(7.0.9)MVC Config Interceptors:https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-config/interceptors.html
- Spring Framework リファレンス(7.0.9)Interception:https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet/handlermapping-interceptor.html
- Spring Framework リファレンス(7.0.9)Exceptions:https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet/exceptionhandlers.html
- Spring Framework リファレンス(7.0.9)Controller Advice:https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-advice.html
- Spring Framework リファレンス(7.0.9)Filters:https://docs.spring.io/spring-framework/reference/web/webmvc/filters.html
- Spring Framework リファレンス(7.0.9)Declaring a Pointcut:https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html
- Spring Framework リファレンス(7.0.9)Declaring Advice:https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/advice.html
HandlerInterceptorJavadoc(7.0.9):https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/HandlerInterceptor.htmlOncePerRequestFilterJavadoc(7.0.9):https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/filter/OncePerRequestFilter.htmlInterceptorRegistrationJavadoc(7.0.9):https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/InterceptorRegistration.html- Spring Boot リファレンス(4.1.1)Servlet Web Applications:https://docs.spring.io/spring-boot/reference/web/servlet.html
- Spring Security リファレンス(7.1.1)Architecture:https://docs.spring.io/spring-security/reference/servlet/architecture.html
- Spring Security リファレンス(7.1.1)Authorize HttpServletRequests:https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html
- Spring Security リファレンス(7.1.1)Method Security:https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html
- Spring Boot 4.0 Migration Guide:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide
- Spring Boot 3.0 Migration Guide:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Spring Filter・Interceptor・AOPの違い|比較表と実行順」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。