Spring3 AOPに挑戦(2)
それでは、ジョインポイントの情報をコンソールに表示してみましょう。SampleIntercepterのbeforeメソッドを下記のように修正します。
@Before("execution(* jp.org.tomotaro.aop.*.*(..))") public void before(JoinPoint joinPoint) throws Throwable { System.out.println("Before Method started excuting..."); Signature signature = joinPoint.getSignature(); System.out.println(signature.getDeclaringTypeName()+ "." + signature.getName()); }
コンソールに次のように表示されるはずです。
Before Method started excuting...
jp.org.tomotaro.aop.Service.getMessage
getMessage() called.
Hello world!
2行目の「jp.org.tomotaro.aop.Service.getMessage」が今回追加したジョインポイントの情報です。
このように、beforeメソッドにJoinPointの引数を追加するだけで、Springからジョインポイントの情報を受け取れるようになります。ちなみに、beforeメソッドのように特定のジョインポイントで実行されるアクションのことをAdviceと言います。
今回、Before Adviceのメソッド名をbeforeとしましたが、自由に変更することが出来ます。
また、AdviceにはBefore、After、AfterReturning、AfterThrowing、Aroundなどがあり、それぞれ次のようなタイミングで起動されます。
Before Advice: ジョインポイントの実行前に起動されるアドバイス。
After Advice: ジョインポイントの実行後に起動されるアドバイス。ジョインポイントが正常終了時、例外発生時、いずれの場合でも起動される。
AfterReturning Advice: ジョインポイントの正常終了後に起動されるアドバイス。
AfterThrowing Advice: ジョインポイントの例外発生後に起動されるアドバイス。
Around Advice: ジョインポイントの周囲で起動するアドバイス。アドバイス内でジョインポイントを起動する。
SampleIntercepterに他のAdviceも実装してみました。
ちなみに、先ほどは@Beforeアノテーションでジョインポイントを直接指定していましたが、他のアドバイスにもそれぞれ直接ジョインポイントを指定してしまうと、ジョインポイントを変更したい場合に、すべてを変更しなくてはならなくなってしまい、メンテナンス性が良くありません。
したがって、ポイントカットにジョインポイントを指定し、それぞれのアノテーションにはポイントカットを指定するようにしています。
package jp.org.tomotaro.aop; 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.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class SampleIntercepter { @Pointcut("execution(* jp.org.tomotaro.aop.*.*(..))") public void pointCut() { } //@Before("execution(* jp.org.tomotaro.aop.*.*(..))") @Before("pointCut()") public void before(JoinPoint joinPoint) throws Throwable { System.out.println("Before Method started excuting..."); Signature signature = joinPoint.getSignature(); System.out.println(signature.getDeclaringTypeName()+ "." + signature.getName()); } @After("pointCut()") public void after(JoinPoint joinPoint) throws Throwable { System.out.println("After Method executed..."); Signature signature = joinPoint.getSignature(); System.out.println(signature.getDeclaringTypeName() + "." + signature.getName()); } @AfterReturning(value = "pointCut()", returning = "returnValue") public void afterReturning(JoinPoint joinPoint, Object returnValue) throws Throwable { System.out.println("After Method returning..."); Signature signature = joinPoint.getSignature(); System.out.println(signature.getDeclaringTypeName() + "." + signature.getName()); System.out.println("returnValue=" + returnValue.toString()); } @AfterThrowing(value = "pointCut()", throwing = "throwable") public void afterThrowing(JoinPoint joinPoint, Throwable throwable) { System.out.println("After Exception throwing..."); Signature signature = joinPoint.getSignature(); System.out.println(signature.getDeclaringTypeName() + "." + signature.getName()); System.out.println(throwable.getMessage()); } @Around(value = "pointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("Around start..."); Signature signature = joinPoint.getSignature(); System.out.println(signature.getDeclaringTypeName() + "." + signature.getName()); Object returnValue = joinPoint.proceed(); System.out.println("Around end..."); if(returnValue != null) { System.out.println("Around returnValue=" + returnValue.toString()); } return returnValue; } }
そして、ServiceとExampleServiceにthrowExceptionメソッドを追加します。
package jp.org.tomotaro.aop; public interface Service { String getMessage(); void throwException() throws Exception; }
package jp.org.tomotaro.aop; import org.springframework.stereotype.Component; @Component public class ExampleService implements Service { public String getMessage() { System.out.println("getMessage() called."); return "Hello world!"; } public void throwException() throws Exception { throw new Exception("Test!!"); } }
最後にTesterのmainメソッドにServiceのthrowExceptionメソッド呼び出しを追加します。
package jp.org.tomotaro.test; import jp.org.tomotaro.aop.Service; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Tester { public static void main(String[] args) { ApplicationContext appContext = new ClassPathXmlApplicationContext( "classpath:./META-INF/spring/app-context.xml"); Service service = appContext.getBean(Service.class); String message = service.getMessage(); System.out.println(message); try { service.throwException(); } catch (Exception e) { } } }
実行すると、コンソールに下記のように表示されます。
Before Method started excuting...
jp.org.tomotaro.aop.Service.getMessage
Around start...
jp.org.tomotaro.aop.Service.getMessage
getMessage() called.
After Method executed...
jp.org.tomotaro.aop.Service.getMessage
After Method returning...
jp.org.tomotaro.aop.Service.getMessage
returnValue=Hello world!
Around end...
Around returnValue=Hello world!
Hello world!
Before Method started excuting...
jp.org.tomotaro.aop.Service.throwException
Around start...
jp.org.tomotaro.aop.Service.throwException
After Method executed...
jp.org.tomotaro.aop.Service.throwException
After Exception throwing...
jp.org.tomotaro.aop.Service.throwException
Test!!