Spring BootでAOPを活用した共通処理の実装方法
はじめに
今年から新卒で入社したマーケティング開発チームの鷹野です。
当チームでは主にフロントエンドの開発を担当していますが、最近はバックエンドの業務にも関わるようになり、Spring Boot を使った開発にも触れています。
その中で、AOP(Aspect-Oriented Programming) という概念に触れ、ログの保存などの共通処理を簡易的に実装できることを学んだので、その実装方法をシェアしたいと思います。
AOP(Aspect-Oriented Programming)とは
AOP(Aspect-Oriented Programming) とは、あらゆるオブジェクトに散在する共通処理(ログ出力やトランザクション管理など)を抜き出し、それらを一つにまとめて管理できるようにする手法および概念のことです。
これにより、ビジネスロジックから共通処理を分離することで、コードの再利用性やメンテナンス性を向上させることができます。
AOPの特徴
AOPでは、「アスペクト(Aspect)」 という単位で共通処理を管理し、対象となるメソッドやクラスに対して、アスペクトを適用することができます。これにより、以下のような利点が得られます。
コードの重複を排除:ログやトランザクション管理などの共通処理を一元化することで、重複したコードを減らすことができます。
可読性の向上:ビジネスロジックから横断的な処理を切り離すことで、コードの可読性が向上します。
変更の影響を最小限に抑える:共通処理をアスペクトとして切り出しておくことで、後からの変更や追加が容易になります。
実践
今回担当したのは、記事の配信登録を管理するシステムです。
このシステムでは、記事の登録、更新、停止といった操作を行うことができ、ユーザーの各操作の履歴をログとして記録する必要があります。
このシステムのユーザー操作ログ記録機能の実装をもとにAOPを活用した共通処理を紹介します。
流れとしてはログを記録するための独自アノテーションを作成し、そのアノテーションを使って共通処理を適用します。
独自アノテーションの定義
今回はユーザー操作に関するログを記録するためにLoggableUserOperationアノテーションを定義します。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LoggableUserOperation {
String actionName();
}
@Retention: アノテーションの読み込みタイミングを指定できる。
@Target: アノテーションをどの要素(クラス、メソッド、フィールドなど)に適用できるかを指定できる。
アスペクトクラスの作成
次に、@LoggableUserOperationが付与されたメソッドに対して、共通処理を実行するアスペクトクラスを作成します。
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.JoinPoint;
/**java
* 操作ログを取得するAspectクラス。
*/
@Aspect
@Component
public class UserOperationAspect {
/**
* LoggableUserOperationの付いたEPの例外発生時に実行
*
* @param joinPoint 実行メソッドの情報
*/
@AfterThrowing("@annotation(~~/LoggableUserOperation)")
public void logUserOperationError(JoinPoint joinPoint) {
//ユーザー操作ログ保存処理
}
/**
* LoggableUserOperationの付いたEPの正常終了時に実行
*
* @param joinPoint 実行メソッドの情報
*/
@AfterReturning("@annotation(~~/LoggableUserOperation)")
public void logUserOperation(JoinPoint joinPoint) {
//ユーザー操作ログ保存処理
}
}
AOPによるアドバイスを定義
アドバイス(Advice) は、特定のメソッドが実行されるタイミングで実行される処理のこと
@Before: 対象のメソッドの処理前に実行
@After: 対象のメソッドの処理後に実行
@AfterReturning: 対象のメソッドの処理が正しく終了した場合にのみ実行
@AfterThrowing: 対象のメソッドの処理中に例外が発生した場合にのみ実行
@Around: 対象のメソッドの処理前や処理後など好きなタイミングで実行
JoinPintについて
アノテーションが付与されたメソッドが実行されたタイミングで、そのメソッドの情報(メソッド名、引数など)を取得できるオブジェクト。
アノテーション付与
最後にAOPを適応させたいメソッドに対して作成したアノテーション(@LoggableUserOperation)を付与します。メソッドが実行された後、さきほど定義したアスペクトクラスのメソッドが自動的に実行されます。
以下の例では、記事を登録・更新・停止するメソッドが実行されるとアスペクトのログを保存する処理が実行されることになります。
@LoggableUserOperation(actionName = "記事登録")
public Article saveArticle() {
//処理
}
@LoggableUserOperation(actionName = "記事更新")
public Article updateArticle() {
//処理
}
@LoggableUserOperation(actionName = "記事停止")
public Article terminateArticle() {
//処理
}
まとめ
AOP(アスペクト指向プログラミング)を活用して共通処理(ログ記録)を実装することで、コードの重複を減らし、効率的な開発ができることを実感しました。今後も先輩エンジニアのコードを参考に技術を習得し、よりシンプルでメンテナンスしやすいコードを書くことを意識していきたいと思います。
Discussion