だましだまし

そういう生き方 メモ用として使っていく

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"にしてるのが原因だった。
内部的に既に使用している名称だからそこは変えないとダメってことらしい。