Spring Boot入門：AOPを日常生活で例える

## はじめに
前回の記事では、Spring Bootが持つDI（依存性注入）機能について説明しました。今回は、AOP（アスペクト指向プログラミング）の基本的な概念とその活用方法について説明します。

※前回記事は以下をご参照ください。


https://qiita.com/matsuuranext0303/items/d95b6980494607558bb2

## 1.AOP（アスペクト指向プログラミング）とは？

AOP（アスペクト指向プログラミング）は、共通処理を横断的に実装するための手法です。例えば、ログ処理やトランザクション管理、セキュリティチェックなど、複数の場所で共通して行う処理をまとめて管理することができます。
<br>

**身近な例だと、映画館の入場ゲートで行われるチケットチェックが挙げられます。** 映画館に入るには必ず入場ゲートでチケットの確認が必要です。チケットには入場可能な時間が記載されており、その時間になるまで入場ゲートでのチェックは行われません。もし、時間がまだ来ていない、もしくは日付が異なるチケットが提示された場合、入場は許可されません。このようなチェックは毎回行われ、映画によって異なる内容でも、入場の際には必ず実施されます。この「チケットチェック」が、AOPの共通処理に当たります。

次にこの例をコードで表してみましょう。

### AOPによるチケットチェックの実装例
以下は、AOPを使って映画館のチケットチェックの処理を実装した例です。入場ゲート（アスペクト）でチケットのチェック（共通処理）を実行し、チケットが有効であれば入場できるようにしています。
※チケットの時間を考慮せず日付のみでチェックしています。

```java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

// アプリケーションクラス
@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) {
        // Spring Boot アプリケーションを起動
        ApplicationContext context = SpringApplication.run(MainApplication.class, args);

        // 日付と映画名を指定して入場
        String ticketDate = "2025-03-12";
        String movieName = "MovieTitle";

        // MovieTheater クラスのインスタンスを取得
        MovieTheater movieTheater = context.getBean(MovieTheater.class);

        // 映画館に入場
        movieTheater.enterTheater(ticketDate, movieName);  // ここで AOP によるチェックが入る
    }
}

// チケットチェックのアスペクト（共通処理）
// AOPを使って共通処理（チケットチェック）を切り出す
@Aspect
@Component
public class TicketCheckAspect {

    // チケットが有効かどうかをチェック
    @Before("execution(* MovieTheater.enterTheater(..)) && args(date, movie)")
    public void checkTicket(String date, String movie) {
        // 現在の日時（仮に実行日時を"2025-03-12"とします）
        String currentDate = "2025-03-12";
        
        // チケットの有効日をチェック
        if (!date.equals(currentDate)) {
            throw new IllegalArgumentException("無効なチケットです。入場できません。");
        }
        System.out.println("チケットが確認されました。入場可能です。");
    }
}

// 映画館の入場処理クラス
@Component
public class MovieTheater {

    // 映画館に入場するメソッド
    public void enterTheater(String date, String movie) {
        System.out.println("映画館に入場します...");
    }
}


```


## 2.実行の流れ

それではこのコードにおける実行順序とAOPがどこでどのように使われているかを説明していきます。

### 1）Spring Bootアプリケーションの起動
    
最初にMainApplicationクラスのmainメソッドが起動します。
``` java:MainApplicationクラス
    public static void main(String[] args) {
```
必要な変数を指定し、インスタンスを取得した後にmovieTheater.enterTheaterメソッドを呼び出します。
``` java:MainApplicationクラス
        // 映画館に入場
        movieTheater.enterTheater(ticketDate, movieName);  // ここで AOP によるチェックが入る
```

### 2）enterTheaterメソッド（映画館入場）の起動

次にmovieTheater.enterTheater(ticketDate, movieName) を実行します。
と、その前にここでenterTheaterメソッドを実行する前にAOPの登場です。
checkTicketメソッドの実装箇所より前に
「@Before("execution(* MovieTheater.enterTheater(..)) && args(date, movie)")」
と記述することで"enterTheaterメソッドを実行する前に、このメソッド（checkTicket）を実行してください"という意味となります。（引数はポイントカット式と呼ばれる記述ですが、ここの書き方は省略します。）
``` java:TicketCheckAspectクラス
// チケットチェックのアスペクト（共通処理）
// AOPを使って共通処理（チケットチェック）を切り出す
@Aspect
@Component
public class TicketCheckAspect {

    // チケットが有効かどうかをチェック
    @Before("execution(* MovieTheater.enterTheater(..)) && args(date, movie)")
    public void checkTicket(String date, String movie) {
```
@Beforeアノテーションは、対象メソッドが実行される前に実行される処理を指定します。今回の例では、enterTheaterメソッドの実行前にcheckTicketメソッドを実行することで、映画館へ入場する前にチケットが有効かどうかを確認しており、具体例のケースに沿った処理となります。

なお、@Beforeを使うにあたっては対象クラス（TicketCheckAspect）に@Aspectを付与する必要があります。
@Beforeは@Aspectの機能の一部であるためこの記述がないと@Beforeが機能しません。


### 3）AOP（共通処理であるチケットチェック処理の実行）
この処理ではチケットの日付をチェックし、当日の日付と一致していれば有効である文言を返し、AOPメソッドの処理は正常終了を返します。
一致していない場合は例外処理とし、その旨を伝えるメッセージとともに結果を返します。
``` java:TicketCheckAspectクラス
        // 現在の日時（仮に実行日時を"2025-03-12"とします）
        String currentDate = "2025-03-12";
        
        // チケットの有効日をチェック
        if (!date.equals(currentDate)) {
            throw new IllegalArgumentException("無効なチケットです。入場できません。");
        }
        System.out.println("チケットが確認されました。入場可能です。");
```

### 4）実行結果
もしチケットが正しい日付であれば、次のようなメッセージが表示されます。

```
チケットが確認されました。入場可能です。
映画館に入場します...
```
チケットの日付が無効な場合、IllegalArgumentExceptionがスローされ、入場を防ぎます。

```
Exception in thread "main" java.lang.IllegalArgumentException: 無効なチケットです。入場できません。
```

チケットチェックの処理は映画館入場処理の中に組み込むこともできますが、切り離して共通機能とすることにより、コードが読みやすく修正する際も扱いやすくなり、チケットチェックの処理を他の処理の実行前にも呼び出す。といったことも可能です。


## 3.まとめ
このように、AOPを使用することで、共通の横断的処理をアスペクトとして切り出し、メインのビジネスロジックであるenterTheaterメソッドから分離することができます。これにより、映画館のチケットチェックはアプリケーション全体で一貫して実行され、コードがすっきりと保たれます。
AOPは、ロギング、トランザクション管理、セキュリティチェック、バリデーションなど、業務アプリケーションでよく使われる共通処理を一元管理するのに非常に有効な手法なため、アプリケーション内で共通化したい処理に試してみてください。