Springについて AOPコンテナ

DI×AOPコンテナ

AOPとは、あるオブジェクトが本来行わくてよい処理を(ロギングやトランザクション)を分離して、他のオブジェクトで実装するためのものです。
これをAspect Oriented Programming:アスペクト指向プログラミングと言います。
分離された処理をAdviceといい、これはとあるオブジェクトを実行した時任意のタイミングで割り込まれ実行されます。
Adviceを呼出すタイミングをJoinpointといい、Joinpointが呼ばれるとAdviceは必ず実行されます。このJoinPointをフィルタリングしAdviceを呼ぶかどうか判断することをPointcutといいます。

AOPにはDIと同じく、アノテーションを利用するものと、Bean定義ファイルに設定を記述する二通りの方法があります。

アノテーションを使ったAOP
どのようにAOPを利用するかを解説します。

pomを開き以下の依存関係を追加します。
org.aspectj aspectjweaver
プロジェクトにパッケージsample03を作成します。
Sample01のファイルに加えてSampleAspectクラスを作成してください。

package sample03;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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 SampleAspect {
	@Before("execution(* findProduct(String))")
	public void before(JoinPoint jp){
		//メソッド開始時に動作するAdvice
		System.out.println("ジョインポイント Before --- メソッドが呼ばれる前に出てきます");
		Signature sig = jp.getSignature();
		System.out.println("---> メソッド名を取得します:" + sig.getName());
		Object[] objs = jp.getArgs();
		System.out.println("---> 引数の値を取得します:" + objs[0]);
	}

	@After("execution(* findProduct(String))")
	public void after(){
		//メソッド終了時に動作するAdvice
		System.out.println("ジョインポイント After --- メソッドが呼ばれたあとに出てきます");
	}

	@AfterReturning(value="execution(* findProduct(String))", returning="product")
	public void afterRetruning(Product product){
		//メソッド呼出しが例外の送出なしに終了した際に動作するAdvice
		System.out.println("ジョインポイント AfterReturning --- メソッドを呼んだあとに出てきます");
	}

	@Around("execution(* findProduct(String))")
	public Product around(ProceedingJoinPoint pjp) throws Throwable{
		//メソッド呼出しの前後に動作するAdvice
		System.out.println("ジョインポイント Around Before --- メソッドを呼ぶ前に出てきます");

		//メソッド名の出力
		Signature sig = pjp.getSignature();
		System.out.println("---> aop:around メソッド名を取得します:" + sig.getName());

		Product p = (Product)pjp.proceed();
		System.out.println("ジョインポイント Around After --- メソッドを呼んだ後に出てきます");
		return p;
	}

	@AfterThrowing(value="execution(* findProduct(String))", throwing="ex")
	public void afterThrowing(Throwable ex){
		//メソッドの呼出しが例外を送出した際に動作するAdvice
		System.out.println("ジョインポイント AfterThrowing --- 例外になったら出てきます");
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- @Autowiredを利用する場合の宣言 -->
	<context:annotation-config />
	<!-- @Componentを利用する場合の宣言 base-package属性で指定したパッケージ名以下のコンポーネントを検索する -->
	<context:component-scan base-package="sample03" />
	<!-- @Aspectを利用する場合の宣言 -->
	<aop:aspectj-autoproxy />

</beans>

記載されてないクラスについてはsample01と同じコードを使用しています。

Aspectクラスに記述されたJoinpointアノテーションの後ろの括弧内の構文がJoincutになります。
ここでは基本的なexecutinに絞って説明します。
executionは呼び出し先のメソッドを条件にしPointcutを設定します。
以下に例を記します。
execution(public String jp.co.sample.Example.Method())
これはjp.co.sample.Exampleクラスのpublic String Method()メソッドを条件にしています。
以下に基本的な構文を記します。
Execution(修飾子△戻り値△パッケージ.クラス(インタフェース).メソッド名(引数の型)△throws△例外)
・修飾子、throws△例外は省略可能
・パッケージ、クラス名、インタフェース名はワイルドカードを指定可能
・パッケージ、クラス名は省略可能
・複数のパッケージと一致させるには「..」を使用
・引数に「..」を使用すると、あらゆる引数と一致させることができる。

Bean定義ファイルでAOP
DIと同様にBean定義ファイルでAOPを利用できます。
プロジェクトにパッケージsample03を作成します。
ファイルの構成はsample03と同様にしてください。

package sample04;

import org.aspectj.lang.ProceedingJoinPoint;

public class SampleAspect {
	public void before(){
		//メソッド開始時に動作するAdvice
		System.out.println("ジョインポイント Before --- メソッドが呼ばれる前に出てきます");
	}

	public void after(){
		//メソッド終了時に動作するAdvice
		System.out.println("ジョインポイント After --- メソッドが呼ばれたあとに出てきます");
	}

	public void afterReturning(Product product){
		//メソッド呼出しが例外の送出なしに終了した際に動作するAdvice
		System.out.println("ジョインポイント AfterReturning --- メソッドを呼んだあとに出てきます");
	}

	public Product around(ProceedingJoinPoint pjp) throws Throwable{
		//メソッド呼出しの前後に動作するAdvice
		System.out.println("ジョインポイント Around Before --- メソッドを呼ぶ前に出てきます");
		Product p = (Product)pjp.proceed();
		System.out.println("ジョインポイント Around After --- メソッドを呼んだ後に出てきます");
		return p;
	}

	public void afterThrowing(Throwable ex){
		//メソッドの呼出しが例外を送出した際に動作するAdvice
		System.out.println("ジョインポイント AfterThrowing --- 例外になったら出てきます");
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop.xsd">

	<bean id="SampleLogic" class="sample04.SampleLogicImpl" autowire="byType" />
	<bean id="SampleDao" class="sample04.SampleDaoImpl" />
	<bean id="sampleAspect" class="sample04.SampleAspect" />

	<aop:config>
		<aop:aspect id="sAspect" ref="sampleAspect">
			<aop:pointcut id="pc" expression="execution(* findProduct())" />
			<aop:before pointcut-ref="pc" method="before" />
			<aop:after pointcut-ref="pc" method="after" />
			<aop:after-returning pointcut-ref="pc" method="afterReturning" returning="product"/>
			<aop:around pointcut-ref="pc" method="around" />
			<aop:after-throwing pointcut-ref="pc" method="afterThrowing" throwing="ex"/>
		</aop:aspect>
	</aop:config>

</beans>

Adviceのソースからアノテーションを除いて、Bean定義ファイルに移動したようなイメージです。
また、AspectクラスをアノテーションでComponentも出来ましたが今回は、DI定義で行っているため、Bean定義ファイルにコンポーネントの追加を設定しています。

この記事を読んだ人は以下の記事も読んでいます

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA