Spring-MVC/ステップ・バイ・ステップ

2008/04/16からのアクセス回数 4430

AopNameSpaceとは何か

Spring 2.0の特徴は、

  • NameSpaceを使ってAOPやトランザクションの記述が簡単にできるようになった
  • AspectJをサポートした

ことです。

AopNameSpaceでは、普通のオブジェクト(以下POJOオブジェクトと呼びます)をAspectの Adviceとして使用することができます。

AopNameSpaceを使用するとAutoProxyCreatorは使用できないことに注意してください。

準備

AopNameSpaceを使用するには、

  • spring.jar(version 2.5以降)
  • aspectjrt.jar(version 1.5以降)
  • aspectjweaver.jar(version 1.5以降)

が必要です。

いつものようにMVN Repositoryで検索すると、以下のようなdependecyタグが見つかりました。

	<dependency>
	  <groupId>org.aspectj</groupId>
	  <artifactId>aspectjrt</artifactId>
	  <version>1.5.4</version>
	</dependency>
	<dependency>
	  <groupId>org.aspectj</groupId>
	  <artifactId>aspectjweaver</artifactId>
	  <version>1.5.4</version>
	</dependency>

これをpom.xmlに追加して、以下のコマンドを実行してください。

$ rm .project .classpath
$ mvn eclipse:eclipse -DdownloadSources=true

AopNameSpaceの例題

AopNameSpaceを使った例を順を追って作成していきましょう。

POJOオブジェクト

Adviceとして使用するPOJOオブジェクトクラス(POJOAdvice)を以下のように定義します。

package org.springframework.showcase.aop;

public class POJOAdvice {
	public void a() {
		System.out.println("a called");
	}
	public void b(Long id) {
		System.out.println("b(" + id +") called");
	}
}
  • aは単に”a called”と出力します
  • bは、引数にidを持ち、"b(id) called"と出力します

AOP定義ファイル

AopNameSpaceを使った定義ファイルは他のBean定義ファイルと別ファイルにすると 機能が切り分けられます。

aop-def.xmlは、以下のようになります。

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

    <bean id="pojoAdvice" class="org.springframework.showcase.aop.POJOAdvice" />
	
    <aop:config>
        <aop:aspect ref="pojoAdvice">			
            <aop:before
                method="a"
                pointcut="execution(* *.findAll(..))" />
            <aop:before
                method="b"
                pointcut="execution(* org.springframework.showcase.coverc.service.GenericHibernateDao.findById(..))
                    and args(id)"
             />
        </aop:aspect>
    </aop:config>
</beans>
  • beans定義では、AopNameSpaceを使用するためにNameSpaceを定義します
  • pojoAdviceがPOJOAdviceのBeanです
  • <aop:config>がAOPの定義を示します
  • <aop:aspect>のrefでpojoAdviceを指定します
  • <aop:before>でpojoAdviceの呼び出すメソッド名とpointcutを指定します

'<aop:config>では、<aop:before>の他に以下の要素(タグ)が使用できます。

要素名目的
<aop:advisor>AOP Advisorを定義します
<aop:after>AOP after advice を定義します(正常・異常にかかわらずメソッドがreturnしたときに適応)
<aop:after-returning>AOP after-return adivice を定義します
<aop:after-throwing>AOP after-throwing advice を定義します
<aop:around>AOP around advice を定義します
<aop:aspect>aspectを定義します
<aop:before>AOP before advice を定義します
<aop:pointcut>pointcut を定義します

'<aop:before>に戻って

  • method: aというメソッドを呼び出すことを指定
  • pointcut: AspectJの記述形式でpointcutを指定

します。 AspectJのpointcutは、

execution ( <戻り値のタイプ> <クラスパス>.<メソッド名>( [ <引数のタイプ> ] ) )

の形式で記述します。

aの場合のpointcutを見てみると

pointcut="execution(* *.findAll(..))"

とありますが、型、クラスパスに関係なくfindAllという名前のメソッドにpointcutを設定する 指定です。

次にbの場合のpointcutを見ると

pointcut="execution(* org.springframework.showcase.coverc.service.GenericHibernateDao.findById(..))
		 and args(id)"

and args(id)でfindByIdの引数idをbの呼び出しに渡すことを指定します。

web.xmlの変更

aop-def.xmlを追加するために、web.xmlに以下のcontext-paramタグを追加します。

	<context-param> 
	  <param-name>contextConfigLocation</param-name> 
	  <param-value> 
		/WEB-INF/applicationContext.xml
		/WEB-INF/aop-def.xml
	  </param-value> 
	</context-param> 

applicationContext.xmlの変更

前回のDefaultAdvisorAutoProxyCreatorを使ったAOPと共存できないので、applicationContext.xmlを 以下のように変更します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
        "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
	<bean id="recipeManager" class="org.springframework.showcase.coverc.service.StubRecipeDaoManager">
		<property name="sessionFactory" ref="sessionFactory" /> 
	</bean>

	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName">
			<value>org.postgresql.Driver</value>
		</property>
		<property name="url">
			<value>jdbc:postgresql://localhost/springdb</value>
		</property>
		<property name="username">
			<value>spring</value>
		</property>
		<property name="password">
			<value>spring</value>
		</property>
	</bean>
	<bean id="sessionFactory" 
		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="hibernateProperties">
			<props>
			<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
			</props>
		</property>
		<property name="mappingDirectoryLocations">
			<list>
				<value>classpath:/org/springframework/showcase/coverc/domain</value>
			</list>
		</property>
		<property name="dataSource">
			<ref bean="dataSource"/>
		</property>
	</bean>	
</beans>

例題の実行

maven jettyプラグインを使って例題を実行します。

$ mvn jetty:run

次にブラウザーで

http://localhost:8080/mvc-convention/

と入力すると

a called
a called

と表示されます。これは、RecipeManagerのfindAllとGenericHibernateDaoのfindAllの2カ所に pointcutが設定されたからです。

次に、ブラウザのaddリンクをクリックして「test」を入力した後、Save Changeボタンを押してください。 testがリストに追加されましたが、testのdeleteリンクをクリックしてください。

b(4) called
a called
a called

とb(4) calledが表示されます。bは、

org.springframework.showcase.coverc.service.GenericHibernateDao.findById

とクラスパスを指定しているので1回だけ表示されます。

ログ出力との併用

AopNameSpaceを使ったAOPでは、引数を明示的に指定する必要があり、ログ出力のような チェックプリントのメソッドを実行することができません。

チェックプリントは、デバッガでは追えない並行処理のデバッグに有効な手段です。

ここでは、ログ出力とAopNameSpaceの併用の方法について説明します。

チェックプリントを出力したいBeanをProxyFactoryBeanでラップすることで 指定したBeanにログ出力機能を加えることができます。

  • logBaseでinterceptorNames属性を定義します
  • recipeManagerをStubRecipeDaoManagerからProxyFactoryBeanに代えます
  • ProxyFactoryBeanのtargetにStubRecipeDaoManagerのBeanを定義します
	<bean id="enterMethodLogAdvice" class="org.springframework.showcase.aop.EnterMethodLogAdvice"/>
	<bean id="leaveMethodLogAdvice" class="org.springframework.showcase.aop.LeaveMethodLogAdvice"/>
	<bean id="logBase" abstract="true">
		<property name="interceptorNames">
			<list>
				<value>enterMethodLogAdvice</value>
				<value>leaveMethodLogAdvice</value>
			</list>
		</property>		
	</bean>
	
	<bean id="recipeManager" class="org.springframework.aop.framework.ProxyFactoryBean" 
		parent="logBase">
		<property name="target">
			<bean class="org.springframework.showcase.coverc.service.StubRecipeDaoManager">
				<property name="sessionFactory" ref="sessionFactory" /> 
			</bean>
		</property>
	</bean>

実行例

先ほどと同様に実行すると、

a called
enter findAll args=()
a called
leave findAll return=[org.springframework.showcase.coverc.domain.Recipe@9ab468, 
org.springframework.showcase.coverc.domain.Recipe@a06db5, 
org.springframework.showcase.coverc.domain.Recipe@82aacf]

-- 途中省略
enter save args=(org.springframework.showcase.coverc.domain.Recipe@fbe09b)
leave save return=null
a called
enter findAll args=()
a called
leave findAll return=[org.springframework.showcase.coverc.domain.Recipe@82c6dc, 
org.springframework.showcase.coverc.domain.Recipe@cdb92b, 
org.springframework.showcase.coverc.domain.Recipe@37bc9e, 
org.springframework.showcase.coverc.domain.Recipe@403477]
enter findById args=(4)
b(4) called
leave findById return=org.springframework.showcase.coverc.domain.Recipe@108172
enter delete args=(org.springframework.showcase.coverc.domain.Recipe@108172)
leave delete return=null
a called
enter findAll args=()
a called
leave findAll return=[org.springframework.showcase.coverc.domain.Recipe@6b2d99, 
org.springframework.showcase.coverc.domain.Recipe@7ec7b9, 
org.springframework.showcase.coverc.domain.Recipe@6a56f0]

のようにログ出力とAopNameSpaceの出力の両方が出ています。

Spring-MVC/ステップ・バイ・ステップ/AOPの追加

今回使用したファイルは、以下にあります。

コメント

この記事は、

選択肢 投票
おもしろかった 0  
そうでもない 0  
わかりずらい 0  

皆様のご意見、ご希望をお待ちしております。


(Input image string)


添付ファイル: filePOJOAdvice.java 519件 [詳細] fileapplicationContext.xml 460件 [詳細] fileweb.xml 493件 [詳細] fileaop-def.xml 572件 [詳細]

トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2008-04-16 (水) 18:00:38 (3262d)
SmartDoc