Spring3 AOPに挑戦(1)

さて、今度はAOPに挑戦です。

HelloWorldと同様に[File]-[New]-[Spring Template Project]メニューで[Simple Spring Utility Project]でプロジェクトを生成するのが手っ取り早いです。
[Project name]に「AOPSample」、top-level packageに「jp.org.tomotaro.aop」と入力して[Finish]ボタンを押下する。

src/main/resources/META-INF/spring/app-context.xmlに下記のように

を追加する。

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<description>Example configuration to get you started.</description>

	<aop:aspectj-autoproxy />
	<context:component-scan base-package="jp.org.tomotaro.aop" />

</beans>

次に、pom.xmlに下記のようにspring-aop、aspectjrt、aspectjweaverのdependencyを追加する。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples.spring</groupId>
	<artifactId>spring-utility</artifactId>
	<version>1.0.0.CI-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>Spring Utility</name>
	<url>http://www.springframework.org</url>
	<description>
		<![CDATA[
      This project is a minimal jar utility with Spring configuration.
    ]]>
	</description>
	<properties>
		<maven.test.failure.ignore>true</maven.test.failure.ignore>
		<spring.framework.version>3.0.6.RELEASE</spring.framework.version>
	</properties>	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.7</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.framework.version}</version>
		</dependency> 
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.6.11</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.6.11</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.14</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
				</configuration>
			</plugin>
 
		</plugins>
	</build> 
</project>

ExampleService.javaのgetMessage()メソッドを下記のように修正する。

	public String getMessage() {
		System.out.println("getMessage() called.");
		return "Hello world!";	
	}


[File]-[New]-[Class]で「jp.org.tomotaro.aop.SampleIntercepter」を生成し、下記のように修正する。

package jp.org.tomotaro.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class SampleIntercepter {
	@Before("execution(* jp.org.tomotaro.aop.*.*(..))")
	public void before() throws Throwable {
		System.out.println("Before Method started excuting...");
	}
}

@Beforeアノテーションの引数の「"execution(* jp.org.tomotaro.aop.*.*(..))"」は、アスペクトの挿入先であるジョインポイントを指定しています。

executionは「<戻り値の型> <パッケージ名>.<クラス名>.<メソッド名>(<引数>)」の形で指定します。

引数は下記のように指定します。
()     パラメータなし
(..)    どんな引数でも良い(引数なしでもいい)
(*)     どんな引数でも良いが引数の数はひとつだけ
(*,String) 二つ引数を取り、一つ目の型は何でもよいが、二つ目はString型。

つまり、「"execution(* jp.org.tomotaro.aop.*.*(..))"」は戻り値の型と引数は何でも良く、jp.org.tomotaro.aopパッケージのすべてのクラスのすべてのメソッドにアスペクトを挿入することを指定しています。

[File]-[New]-[Class]で「jp.org.tomotaro.test.Tester」を生成し、下記のように修正する。

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);
	}
}


パッケージエクスプローラでTester.javaを選択し、[Run As]-[AspectJ/Java Application]を選択して実行すると、下記のようにコンソールに表示されます。

Before Method started excuting...
getMessage() called.
Hello world!

ExampleServiceのgetMessageメソッドがコールする前にSampleIntercepterのbeforeメソッドが実行されたことが確認できます。

AOPの使いどころとしてはロギング処理が多いと思います。ロギング処理ではコールされたメソッドのクラス名やメソッド名、引数などをロギングしたいと思いますが、SampleIntercepterのbeforeメソッドではそういった情報が取れません。

どうすれば良いのでしょうか?

それは、。。。(次回へ続く)

サンプルソース:AOPSample(1).zip