BBH
-Biz Branding Hub-
投稿日 : 
2020/04/09
更新日 : 
2020/04/09

【SpringBoot】SpringAOPを使って処理の前後にログを出すサンプル

アプリケーションを開発しているときに、メソッド実行前に共通の処理を実行させたいという時があるかと思います。
共通関数を作ってそれを呼び出すことでも実現できますが、数が多いといちいちメソッド呼び出しを書くのが面倒です。
また、手動の作業となるので漏れが発生する可能性もあります。
そういったときにSpringAOPという機能を使用すれば、一律で処理の前後の共通処理を走らせることができます。

SpringAOPとは何か

SpringAOPを使用することで、共通処理をメソッド前後に実行することが可能になります。
例えば、メソッド前後にログを埋め込んで、デバッグに使用するといった使い方も可能です。
従来の概念でいうとインターセプトのような感じです。

SpringAOPの簡単な仕組み

Springはオブジェクト(Bean)をDIコンテナで管理しています。
このBeanを呼び出すときに、proxyというオブジェクトが生成されます。
Beanはすべてproxy経由で呼び出されます。
このproxyがBeanの呼び出し時に、AOPが登録されていないかを検索して、実行してくれます。
これによって、共通の処理をメソッド実行前後で一律に実行するよう設定ができるのです。

AOPの設定値

AOPは以下の3つの設定ができます。

AOP設定値

Advice 実行する処理
Pointcut 処理を実行する場所(クラスやメソッド)
JoinPoint 処理を実行するタイミング

これらを設定することで、いつどんな処理をどこで呼び出すかを柔軟に設定できるわけです。

Pointcut

Pointcutには以下の種類が存在します。

Pointcut設定値

execution 正規表現を使ってクラスやメソッドを指定できる
bean Bean名で指定できる
@annotation カスタムアノテーションで指定できる(メソッド単位指定)
@within カスタムアノテーションで指定できる(クラス単位指定)

JoinPoint

JoinPointには以下の種類が存在します。

JoinPoint設定値

Before 処理前
After 処理後
Around 実行前後
AfterReturning 正常終了時
AfterThrowing 異常終了時

SpringAOPのサンプル

では、SpringAOPを使用するための簡単なサンプルを見ていきます。

execution(正規表現)指定のサンプル

まずは、execution(正規表現)指定のサンプルを見てみます。
AOPを定義するための適当なクラスを作成します。
ここでは、「AopSample」としておきます。

AOP ExecutionSample

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AopSample {
	// PointcutのExecution指定サンプル
	
	// Beforeのサンプル
	@Before("execution(* *..*.*Controller.*(..))")
	public void beforeSample(JoinPoint joinPoint) {
		System.out.println("[Before]メソッド開始:" + joinPoint.getSignature());
	}

	// Afterのサンプル
	@After("execution(* *..*.*Controller.*(..))")
	public void afterSample(JoinPoint joinPoint) {
		System.out.println("[After]メソッド終了:" + joinPoint.getSignature());
	}

	// Aroundのサンプル
	@Around("execution(* *..*.*Controller.*(..))")
	public Object aroundSample(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("[Around]メソッド開始:" + joinPoint.getSignature());
		
		try {
			// メソッド実行
			Object result = joinPoint.proceed();
			System.out.println("[Around]メソッド終了:" + joinPoint.getSignature());
			return result;
		} catch(Exception e) {
			System.out.println("[Around]メソッド異常終了:" + joinPoint.getSignature());
			e.printStackTrace();
			throw e;
		}
	}

	// AfterReturningのサンプル
	@AfterReturning("execution(* *..*.*Controller.*(..))")
	public void afterReturningSample(JoinPoint joinPoint) {
		System.out.println("[AfterReturning]メソッド正常終了:" + joinPoint.getSignature());
	}

	// AfterThrowingのサンプル
	@AfterThrowing("execution(* *..*.*Controller.*(..))")
	public void afterThrowingSample(JoinPoint joinPoint) {
		System.out.println("[AfterThrowing]メソッド異常終了:" + joinPoint.getSignature());
	}
}

Pointcutの指定は、「引数 パッケージ名.クラス名.メソッド名(引数)」になります。
上記の「* *..*.*Controller.*(..)」だと、戻り値指定なし、末尾にControllerとつくクラスのすべてのメソッド(引数の指定もなし)が実行されます。
詳しくは、正規表現の文法などを参照下さい。
Aroundだけ書き方が特殊なので注意が必要です。

execution以外の書き方

先ほど説明したようにPointcut(実行箇所)はexecutution以外の書き方も可能です。
execution以外の書き方のサンプルは以下になります。

exectution以外の指定方法サンプル

	// Pointcutをbean指定
	// Bean名の末尾にControllerとつくものを対象
	@Before("bean(*Controller)")
	public void beanSample(JoinPoint jp) {
		System.out.println("[Before - Bean名指定]メソッド開始:" + jp.getSignature());	
	}
	
	// Pointcutをアノテーション指定
	// GetMappingアノテーションがついているメソッドを対象
	@Before("@annotation(org.springframework.web.bind.annotation.GetMapping)")
	public void annotationSample(JoinPoint jp) {
		System.out.println("[Before - annotation指定]メソッド開始:" + jp.getSignature());	
	}
	
	// Pointcutをwithin指定
	// Controllerアノテーションがついているクラスを対象
	@Before("@within(org.springframework.stereotype.Controller)")
	public void withinSample(JoinPoint jp) {
		System.out.println("[Before - within指定]メソッド開始:" + jp.getSignature());	
	}

このように、PointcutとJoinPointを組み合わせることで、様々な条件で処理を割り込ませることができます。

Profile

管理人プロフィール

都内でITエンジニアをやってます。
変遷:中規模SES→独立系SIer→Webサービス内製開発
使用技術はその時々でバラバラですが、C#、AWSが長いです。
どちらかと言うとバックエンドより開発が多かったです。
顧客との折衝や要件定義、マネジメント(10名弱程度)の経験あり。
最近はJava+SpringBootがメイン。

Recommend