[[Spring-MVC/ステップ・バイ・ステップ]] 2008/04/16からのアクセス回数 &counter; #contents ** AopNameSpaceとは何か [#z558866b] Spring 2.0の特徴は、 - NameSpaceを使ってAOPやトランザクションの記述が簡単にできるようになった - AspectJをサポートした ことです。 AopNameSpaceでは、普通のオブジェクト(以下POJOオブジェクトと呼びます)をAspectの Adviceとして使用することができます。 ''AopNameSpaceを使用するとAutoProxyCreatorは使用できないことに注意してください。'' ** 準備 [#c1b26fc9] AopNameSpaceを使用するには、 - spring.jar(version 2.5以降) - aspectjrt.jar(version 1.5以降) - aspectjweaver.jar(version 1.5以降) が必要です。 いつものようにMVN Repositoryで検索すると、以下のようなdependecyタグが見つかりました。 #pre{{ <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に追加して、以下のコマンドを実行してください。 #pre{{ $ rm .project .classpath $ mvn eclipse:eclipse -DdownloadSources=true }} ** AopNameSpaceの例題 [#uee1662d] AopNameSpaceを使った例を順を追って作成していきましょう。 *** POJOオブジェクト [#g5e1af53] Adviceとして使用するPOJOオブジェクトクラス(POJOAdvice)を以下のように定義します。 #pre{{ 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定義ファイル [#m435ea61] AopNameSpaceを使った定義ファイルは他のBean定義ファイルと別ファイルにすると 機能が切り分けられます。 aop-def.xmlは、以下のようになります。 #pre{{ <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を見てみると #pre{{ pointcut="execution(* *.findAll(..))" }} とありますが、型、クラスパスに関係なくfindAllという名前のメソッドにpointcutを設定する 指定です。 次にbの場合のpointcutを見ると #pre{{ pointcut="execution(* org.springframework.showcase.coverc.service.GenericHibernateDao.findById(..)) and args(id)" }} and args(id)でfindByIdの引数idをbの呼び出しに渡すことを指定します。 *** web.xmlの変更 [#j0994e68] aop-def.xmlを追加するために、web.xmlに以下のcontext-paramタグを追加します。 #pre{{ <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/applicationContext.xml /WEB-INF/aop-def.xml </param-value> </context-param> }} *** applicationContext.xmlの変更 [#m6eb002c] 前回のDefaultAdvisorAutoProxyCreatorを使ったAOPと共存できないので、applicationContext.xmlを 以下のように変更します。 #pre{{ <?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> }} ** 例題の実行 [#mb38416e] maven jettyプラグインを使って例題を実行します。 #pre{{ $ mvn jetty:run }} 次にブラウザーで http://localhost:8080/mvc-convention/ と入力すると #pre{{ a called a called }} と表示されます。これは、RecipeManagerのfindAllとGenericHibernateDaoのfindAllの2カ所に pointcutが設定されたからです。 次に、ブラウザのaddリンクをクリックして「test」を入力した後、Save Changeボタンを押してください。 testがリストに追加されましたが、testのdeleteリンクをクリックしてください。 #pre{{ b(4) called a called a called }} とb(4) calledが表示されます。bは、 org.springframework.showcase.coverc.service.GenericHibernateDao.findById とクラスパスを指定しているので1回だけ表示されます。 ** ログ出力との併用 [#ne4e3c45] AopNameSpaceを使ったAOPでは、引数を明示的に指定する必要があり、ログ出力のような チェックプリントのメソッドを実行することができません。 チェックプリントは、デバッガでは追えない並行処理のデバッグに有効な手段です。 ここでは、ログ出力とAopNameSpaceの併用の方法について説明します。 チェックプリントを出力したいBeanをProxyFactoryBeanでラップすることで 指定したBeanにログ出力機能を加えることができます。 - logBaseでinterceptorNames属性を定義します - recipeManagerをStubRecipeDaoManagerからProxyFactoryBeanに代えます - ProxyFactoryBeanのtargetにStubRecipeDaoManagerのBeanを定義します #pre{{ <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> }} *** 実行例 [#w5dd437d] 先ほどと同様に実行すると、 #pre{{ 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の追加]]の 今回使用したファイルは、以下にあります。 #ref(aop-def.xml); #ref(applicationContext.xml); #ref(web.xml); #ref(POJOAdvice.java); ** コメント [#n9849984] この記事は、 #vote(おもしろかった,そうでもない,わかりずらい) 皆様のご意見、ご希望をお待ちしております。 #comment #comment_kcaptcha