【Spring】共通処理の種類と実装方法

# 共通処理の種類と概要

## Filter
`javax.servlet.Filter`インターフェースを実装するフィルターを作成することで、リクエストやレスポンスに対して共通の処理を適用できます。フィルターはWebアプリケーションのフロー内の特定のポイントで実行されます。
## AOP（アスペクト指向プログラミング）
`AOP`を使用して、アプリケーションの横断的な関心事（ログ記録、トランザクション管理など）を切り出すことができます。`@Aspect`アノテーションを使用してアスペクトを定義し、関連する処理を適用するポイントカットを指定します。
## HandlerInterceptor
`HandlerInterceptor`インターフェースを実装するカスタムインターセプターを作成し、共通の前処理や後処理を実行できます。これにより、リクエストの前後にログ出力、セキュリティチェック、リソース解放などを行うことができます。
## ControllerAdvice
`@ControllerAdvice`アノテーションを使用して、コントローラー全体に対して共通の処理を提供する`Advice`クラスを作成できます。例えば、例外処理やモデルの初期化などを行うことができます。

# 処理の順番とシーケンス図
## 処理の順番

```mermaid
graph LR
  User-->Browser
  Browser-->Filter
  Filter-->|Pre-processing| Filter
  Filter-->AOP
  AOP-->HandlerInterceptor
  HandlerInterceptor-->Controller
  Controller-->ControllerAdvice
  ControllerAdvice-->Controller
  Controller-->HandlerInterceptor
  HandlerInterceptor-->AOP
  AOP-->Filter
  Filter-->Browser
  Browser-->User
```
## シーケンス図
```mermaid
sequenceDiagram
  participant User
  participant Browser
  participant Filter
  participant AOP
  participant HandlerInterceptor
  participant Controller
  participant ControllerAdvice

  User->>Browser: リクエスト送信
  Browser->>Filter: リクエスト受信
  Filter->>Filter: 前処理
  Filter->>AOP: AOPの前処理
  AOP->>HandlerInterceptor: リクエストフィルタリング
  HandlerInterceptor->>Controller: リクエスト処理
  Controller->>ControllerAdvice: リクエスト処理結果
  ControllerAdvice->>Controller: レスポンス処理結果
  Controller->>HandlerInterceptor: レスポンス生成
  HandlerInterceptor->>AOP: AOPの後処理
  AOP->>Filter: Filterへの制御を戻す
  Filter->>Browser: レスポンス送信
  Browser->>User: レスポンス表示

```

# 推奨される利用方法

## Filter
* Servlet APIレベルでリクエストとレスポンスをフィルタリングする場合に使用します。
* アプリケーション全体のリクエスト処理の前後に処理を適用する必要がある場合に適しています。
* ログ出力、文字エンコーディングの設定、セキュリティチェックなどの共通の処理を実装するのに適しています。
## AOP（アスペクト指向プログラミング）
* 複数のクラスやメソッドにまたがる横断的な処理を適用する場合に有用です。
* ログ出力、トランザクション管理、キャッシュ制御などの横断的関心事を実装するのに適しています。
* メソッド実行前後などの特定のポイントでの処理が必要な場合に使用します。
## HandlerInterceptor
* Spring MVCフレームワークでリクエストとレスポンスの処理を制御する場合に使用します。
* 特定のリクエストに対して前処理や後処理を適用する必要がある場合に適しています。
* ログ出力、認証・認可、リクエストパラメータのバリデーションなどの共通の処理を実装するのに適しています。

## ControllerAdvice
* Spring MVCフレームワークでコントローラーレベルの共通処理を実装する場合に使用します。
* 特定のコントローラークラスや全体のコントローラーに対して前処理や後処理、例外処理などを適用する必要がある場合に適しています。
* 共通のモデル属性の追加、エラーハンドリング、レスポンスの追加処理などを実装するのに適しています。

# サンプルコード
## Filter
```java
@Component
public class CustomFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // リクエスト処理の前に実行する共通処理
        System.out.println("リクエスト処理の前に実行する処理");

        chain.doFilter(request, response);

        // レスポンス送信後に実行する共通処理
        System.out.println("レスポンス送信後に実行する処理");
    }
    
    // 初期化や破棄処理の実装も必要に応じて追加できます
}
```

## AOP（アスペクト指向プログラミング）
```java
@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.MyService.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        // メソッド実行前の共通処理
        System.out.println("メソッド実行前のログ出力");
    }

    @After("execution(* com.example.MyService.*(..))")
    public void afterMethod(JoinPoint joinPoint) {
        // メソッド実行後の共通処理
        System.out.println("メソッド実行後のログ出力");
    }
}
```
## HandlerInterceptor
```java 
@Component
public class CustomInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // リクエスト処理の前に実行する共通処理
        System.out.println("リクエスト処理の前に実行する処理");

        String requestURI = request.getRequestURI();
        
        // 特定のURIに対してのみ処理を実行する場合
        if (requestURI.equals("/special")) {
            // 特定のURIに対する共通処理
            System.out.println("特定のURIに対する共通処理");
            return true; // リクエストの処理を継続
        }
        
        // 特定の条件に基づいて処理を実行する場合
        if (someCondition) {
            // 特定の条件に対する共通処理
            System.out.println("特定の条件に対する共通処理");
            return true; // リクエストの処理を継続
        }
        
        // 上記の条件に該当しない場合、リクエストの処理をスキップ
        response.sendRedirect("/error"); // エラーページにリダイレクトなど、適切な処理を行う
        return false;
    }

    @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("レスポンス送信後に実行する処理");
    }
}
```
## ControllerAdvice
```java
@ControllerAdvice
public class BeforeAfterAdvice {

    @ModelAttribute
    public void addAttributes(Model model) {
        // コントローラーの前処理として実行する共通処理
        model.addAttribute("commonAttribute", "共通属性の値");
    }

    @Before("execution(* com.example.*.*Controller.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        // コントローラーメソッドの前に実行する共通処理
        System.out.println("コントローラーメソッドの前に実行する処理");
    }

    @After("execution(* com.example.*.*Controller.*(..))")
    public void afterMethod(JoinPoint joinPoint) {
        // コントローラーメソッドの後に実行する共通処理
        System.out.println("コントローラーメソッドの後に実行する処理");
    }
}


@ControllerAdvice
public class ExceptionHandlerAdvice {

    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception ex) {
        // 例外処理の共通処理
        System.out.println("例外処理の共通処理");
        
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("errorMessage", ex.getMessage());
        modelAndView.setViewName("error");
        return modelAndView;
    }
}
```

# 最後に
これらの方法は、Springフレームワークの柔軟性とモジュール性を活用して、アプリケーション全体で再利用可能な共通処理を実装するのに役立ちます。
また、これらの方法は、異なるポイントで共通処理を適用するための異なるアプローチを提供します。具体的な要件とアプリケーションの設計に応じて、最適な方法を選択することが重要です。