Spring AOPで引数を分解して取得したい
今回はSpringの話。ヒステリックブルーの「春~spring~」の話ではなく、frameworkの方のSpring。
引数をargsで全部取得してtoStringでログに出すみたいなサンプルは山ほどあったんだけど、引数をまとめずに一つ一つ取得するサンプルが少なかったのでメモ。
まずpom.xmlのdependenciesに以下を追加。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.2.3.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.7.2</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.2</version> </dependency>
全部現時点の最新版にしといた。
で、aopされる側のインターフェースとクラスを作成
TestService.java
package nets.service; public interface TestService { public void execute(String hoge, Integer foo); }
TestServiceImpl.java
package nets.service; public class TestServiceImpl implements TestService { @Override public void execute(String hoge, Integer foo) { System.out.println("service impl hoge=" + hoge + ", foo=" + foo); } }
次にaopするクラスを作成
ServiceAspect.java
package nets.aspects; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class ServiceAspect { @Pointcut("execution(* nets.service.TestService.execute(..))") public void serviceExecution() { } @Before(value = "serviceExecution() && args(arg0, arg1)") public void beforeExecution(JoinPoint joinpoint, String arg0, Integer arg1) throws Throwable { System.out.println("before hoge arg0=" + arg0 + ", foo arg1=" + arg1); } }
applicationContext.xmlに下記を追加。aopのスキーマ部分は省略。
<aop:aspectj-autoproxy proxy-target-class="false" /> <bean class="nets.service.TestServiceImpl" /> <bean class="nets.aspects.ServiceAspect" />
で、実際に下記のコードで動かしてみた
@Test public void aop() { try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("config/applicationContext.xml")) { TestService sut = context.getBean(TestService.class); sut.execute("hoge", 128); } }
実行結果はこちら
before hoge arg0=hoge, foo arg1=128 service impl hoge=hoge, foo=128
いけてる。
なんとなくどっちもprototypeにしてやってみる。
<bean class="nets.service.TestServiceImpl" scope="prototype" /> <bean class="nets.aspects.ServiceAspect" scope="prototype" />
結果は省略するが、これも上手く動いた。
エンジニアの強い味方であるstackoverflowにそのものズバリのサンプルが載ってたんだけど、その通り書くのもあれだから下記みたいに最初書いたら動かなくてハマった。
@Aspect public class ServiceAspect { @Pointcut("execution(* nets.service.TestService.execute(..))") public void execution() { } @Before(value = "execution() && args(arg0, arg1)") public void beforeExecution(JoinPoint joinpoint, String arg0, Integer arg1) throws Throwable { System.out.println("before hoge arg0=" + arg0 + ", foo arg1=" + arg1); } }
こう書くと@Beforeの方の読み込み時に
Pointcut is not well-formed: expecting 'name pattern' at character position 10 execution() && args(arg0, arg1)
みたいなエラーが出る。結論から言うと対象のメソッドの名前を"execution"にしてるのが原因だった。
内部的に既に使用している名称だからそこは変えないとダメってことらしい。