MicronautのAノテーションで独自処理を追加する方法!自作AOPの作り方
生徒
「Micronautで自分だけのアノテーションを作ってメソッドの前後で特別な処理を挟むことはできますか?」
先生
「はい、Micronautでは独自のアノテーションを定義して、AOPのInterceptorに結び付けることで、任意の処理を自動で実行できます。」
生徒
「具体的にはどうやって作るんですか?」
先生
「基本の流れをステップごとに見ながら解説します。順番に理解していきましょう!」
1. 独自アノテーションの作り方
まずは自分のメソッドに付与するアノテーションを作ります。@Retentionと@Targetを設定して、どの範囲でアノテーションを使うか決めます。そして@AroundでInterceptorを指定することで、アノテーションを付けたメソッドに自動的に処理を挟めます。
import io.micronaut.aop.Around;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@Around(TimingInterceptor.class)
public @interface Timed {
}
2. Interceptorクラスの作成
InterceptorクラスはMethodInterceptorインターフェースを実装して作ります。メソッドの前後で処理を挟むために、context.proceed()の前後に必要な処理を書きます。DIを使えば他のBeanも利用可能です。
import io.micronaut.aop.MethodInterceptor;
import io.micronaut.aop.MethodInvocationContext;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
@Singleton
public class TimingInterceptor implements MethodInterceptor<Object, Object> {
@Inject
private LoggingService loggingService;
@Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
long start = System.currentTimeMillis();
Object result = context.proceed();
long end = System.currentTimeMillis();
loggingService.log(context.getMethodName() + "の実行時間: " + (end - start) + "ms");
return result;
}
}
3. BeanをInterceptorで活用する
Interceptor内では@Injectを使って他のBeanを注入できます。これにより、ロギングや通知、監査などの共通処理を再利用可能なサービスとしてInterceptor内で利用できます。
import jakarta.inject.Singleton;
@Singleton
public class LoggingService {
public void log(String message) {
System.out.println("ログ: " + message);
}
}
4. 独自アノテーションをメソッドに適用
作成した@Timedアノテーションを対象のメソッドに付与します。Interceptorが自動的に処理を挟むため、メソッド側の実装を変更する必要はありません。
import jakarta.inject.Singleton;
@Singleton
public class UserService {
@Timed
public void createUser(String username) {
System.out.println(username + "を作成しました");
}
}
5. 複数Interceptorの活用例
複数のInterceptorを組み合わせることもできます。例えば、TimingInterceptorとSecurityInterceptorを同じメソッドに適用して、処理時間計測とアクセス制御を同時に行えます。Interceptorは呼び出し順に実行されます。
@Singleton
public class SecurityInterceptor implements MethodInterceptor<Object, Object> {
@Inject
private AuthService authService;
@Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
if (!authService.isAllowed(context.getMethodName())) {
throw new SecurityException("アクセス拒否: " + context.getMethodName());
}
return context.proceed();
}
}
6. 実務での活用ポイント
自作AOPを活用すると、メソッドごとの共通処理を一元管理できます。ログ、監査、セキュリティ、通知などをInterceptorでまとめることで、コードの保守性と再利用性が向上します。独自アノテーションを作ると、プロジェクト全体で統一的に処理を挿入できます。
7. 注意点とベストプラクティス
Interceptorを多用しすぎると処理の追跡が難しくなる場合があります。特に複数Interceptorを重ねる場合は順序や副作用に注意してください。また、Interceptor内では例外処理を適切に行い、メソッド本体の処理を邪魔しないように設計することが重要です。