S2ApplicationContextを使用する.
トレース処理を「Crosscutting Concern」として扱うためのInterceptorです。
例として、次のようなサービスクラスにアスペクトしてみます。class Service { public function add($a, $b) { print __CLASS__ . ' called.' . PHP_EOL; return $a + $b; } }次のような実行スクリプトを作成します。
- S2ApplicationContext::importメソッドでServiceクラスをインポートします。
- S2ApplicationContext::registerAspectメソッドでサービスコンポーネントにTraceInterceptorをアスペクトする設定とします。
- S2ApplicationContext::createメソッドでコンテナを生成します。
- S2ContainerのgetComponentメソッドでServiceコンポーネントを取得します。
- Serviceコンポーネントのaddメソッドを実行します。
<?php require_once('S2Container/S2Container.php'); seasar\container\S2ApplicationContext::import(dirname(__FILE__) . '/classes'); seasar\container\S2ApplicationContext::registerAspect('new seasar\aop\interceptor\TraceInterceptor', '/^Service$/'); $container = seasar\container\S2ApplicationContext::create(); $service = $container->getComponent('Service'); print get_class($service) . PHP_EOL; $result = $service->add(2, 3);上記スクリプトを実行すると、サービスクラスのaddメソッドの実行時TraceInterceptorがログを出力します。
% php context.php Service_EnhancedByS2AOP [INFO ] seasar\aop\interceptor\TraceInterceptor::invoke - BEGIN Service->add(2,3) Service called. [INFO ] seasar\aop\interceptor\TraceInterceptor::invoke - END Service->add(2,3) : 5 : 0.0012331008911133 %
S2ContainerFactoryを使用する.
S2ContainerFactoryを使用する場合は、次のようなダイコンファイルを作成します。aspectタグを用いてServiceコンポーネントにTraceInterceptorをアスペクトします。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN" "http://www.seasar.org/dtd/components21.dtd"> <components> <component name="trace" class="seasar\aop\interceptor\TraceInterceptor"/> <component class="Service"> <aspect>trace</aspect> </component> </components>次のような実行スクリプトを作成します。
- ClassLoader::importメソッドでServiceクラスをインポートします。
- S2ContainerFactory::createメソッドでdiconファイルを読み込みます。
- S2ContainerのgetComponentメソッドでServiceコンポーネントを取得します。
- Serviceコンポーネントのaddメソッドを実行します。
<?php require_once('S2Container/S2Container.php'); seasar\util\ClassLoader::import(dirname(__FILE__) . '/classes'); $container = seasar\container\factory\S2ContainerFactory::create(dirname(__FILE__) . '/example.dicon'); $service = $container->getComponent('Service'); print get_class($service) . PHP_EOL; $result = $service->add(2, 3);上記スクリプトを実行すると、サービスクラスのaddメソッドの実行時TraceInterceptorがログを出力します。
% php factory.php Service_EnhancedByS2AOP [INFO ] seasar\aop\interceptor\TraceInterceptor::invoke - BEGIN Service->add(2,3) Service called. [INFO ] seasar\aop\interceptor\TraceInterceptor::invoke - END Service->add(2,3) : 5 : 0.0012331008911133 %
NOTE | |
---|---|
このExampleは examples/aop/interceptor/trace にあります。 |
S2ApplicationContextを使用する.
MockInterceptorは、Mockを使ったテストを簡単に行うためのInterceptorです。S2ApplicationContextを使用する場合は、 コメントアノテーションを用いて、メソッドの戻り値やスローする例外を設定します。
- アノテーションの表記 : @S2Mock
- 引数
- return : 戻り値を設定します。この値はExpressionとして扱われます。(eval関数で処理されます)
- throw : スローする例外を設定します。この値はExpressionとして扱われます。
- 注釈ポイント : メソッド
例として、次のようなサービスクラスにアスペクトしてみます。
class Service { /** * @S2Mock('return' => '10') */ public function add($a, $b) { print __CLASS__ . ' called.' . PHP_EOL; return $a + $b; } /** * @S2Mock('throw' => 'new seasar\exception\NotYetImplementedException("mock exception")') */ public function sub($a, $b) { print __CLASS__ . ' called.' . PHP_EOL; } }次のような実行スクリプトを作成します。
- S2ApplicationContext::importメソッドでServiceクラスをインポートします。
- S2ApplicationContext::registerAspectメソッドでサービスコンポーネントにMockInterceptorをアスペクトする設定とします。
- S2ApplicationContext::createメソッドでコンテナを生成します。
- S2ContainerのgetComponentメソッドでServiceコンポーネントを取得します。
- Serviceコンポーネントのadd、subメソッドを実行します。
<?php require_once('S2Container/S2Container.php'); seasar\container\S2ApplicationContext::import(dirname(__FILE__) . '/classes'); seasar\container\S2ApplicationContext::registerAspect('new seasar\aop\interceptor\MockInterceptor', '/^Service$/'); $container = seasar\container\S2ApplicationContext::create(); $service = $container->getComponent('Service'); print $service->add(2, 3) . PHP_EOL; try { $result = $service->sub(3, 2); } catch(Exception $e) { print get_class($e) . ' : ' . $e->getMessage() . PHP_EOL; }上記スクリプトを実行します。サービスクラスのaddメソッドを実行すると、実際の計算結果(5)ではなくMock値として10が返されます。 subメソッドを実行すると、NotYetImplementedExceptionがスローされます。
% php context.php 10 seasar\exception\NotYetImplementedException : mock exception %
S2ContainerFactoryを使用する.
S2ContainerFactoryを使用する場合は、次のようなダイコンファイルを作成します。aspectタグを用いてServiceコンポーネントにMockInterceptorをアスペクトします。 initMethodタグで、addメソッドに対してsetRetrunValueメソッドで戻り値を設定します。また、subメソッドに対してはsetThrowableメソッドでスローする例外を設定します。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN" "http://www.seasar.org/dtd/components21.dtd"> <components> <component name="mock" class="seasar\aop\interceptor\MockInterceptor"> <initMethod name="setReturnValue"> <arg>"add"</arg> <arg>20</arg> </initMethod> <initMethod name="setThrowable"> <arg>"sub"</arg> <arg>new seasar\exception\NotYetImplementedException('mock exception')</arg> </initMethod> </component> <component class="Service"> <aspect>mock</aspect> </component> </components>Mock値の設定が省略された場合は、コメントアノテーションの設定が反映されます。
次のような実行スクリプトを作成します。
- ClassLoader::importメソッドでServiceクラスをインポートします。
- S2ContainerFactory::createメソッドでdiconファイルを読み込みます。
- S2ContainerのgetComponentメソッドでServiceコンポーネントを取得します。
- Serviceコンポーネントのadd、subメソッドを実行します。
<?php require_once('S2Container/S2Container.php'); seasar\util\ClassLoader::import(dirname(__FILE__) . '/classes'); $container = seasar\container\factory\S2ContainerFactory::create(dirname(__FILE__) . '/example.dicon'); $service = $container->getComponent('Service'); print $service->add(2, 3) . PHP_EOL; try { $result = $service->sub(3, 2); } catch(Exception $e) { print get_class($e) . ' : ' . $e->getMessage() . PHP_EOL; }上記スクリプトを実行します。サービスクラスのaddメソッドを実行すると、実際の計算結果(5)ではなくMock値として20が返されます。 subメソッドを実行すると、NotYetImplementedExceptionがスローされます。
% php factory.php 20 seasar\exception\NotYetImplementedException : mock exception %
NOTE | |
---|---|
このExampleは examples/aop/interceptor/mock にあります。 |
複数のInterceptorをグルーピング化し再利用しやすくします。
例として、次のようなサービスクラスにTraceInterceptorとMockInterceptorをInterceptorChainにまとめてアスペクトしてみます。
class Service { public function add($a, $b) { print __CLASS__ . ' called.' . PHP_EOL; return $a + $b; } }
次のようなダイコンファイルを作成します。aspectタグを用いてServiceコンポーネントにchainコンポーネントをアスペクトします。 chainコンポーネントでは、initMethodタグでtraceコンポーネントとmockコンポーネントをInterceptorChainに登録します。 また、traceコンポーネントとmockコンポーネントもそれぞれ登録します。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN" "http://www.seasar.org/dtd/components21.dtd"> <components> <component class="Service"> <aspect>chain</aspect> </component> <component name="chain" class="seasar\aop\interceptor\InterceptorChain"> <initMethod name="add"><arg>trace</arg></initMethod> <initMethod name="add"><arg>mock</arg></initMethod> </component> <component name="trace" class="seasar\aop\interceptor\TraceInterceptor"/> <component name="mock" class="seasar\aop\interceptor\MockInterceptor"> <initMethod name="setReturnValue"> <arg>"add"</arg> <arg>20</arg> </initMethod> </component> </components>
次のような実行スクリプトを作成します。
<?php require_once('S2Container/S2Container.php'); seasar\util\ClassLoader::import(dirname(__FILE__) . '/classes'); $container = seasar\container\factory\S2ContainerFactory::create(dirname(__FILE__) . '/example.dicon'); $service = $container->getComponent('Service'); print $service->add(2, 3) . PHP_EOL;
上記スクリプトを実行します。サービスクラスのaddメソッドを実行すると、TraceInterceptorのログが出力されます。 また、実際の計算結果(5)ではなくMock値として20が返されます。
% php factory.php [INFO ] seasar\aop\interceptor\TraceInterceptor::invoke - BEGIN Service->add(2,3) [INFO ] seasar\aop\interceptor\TraceInterceptor::invoke - END Service->add(2,3) : 5 : 0.0012331008911133 20 %
NOTE | |
---|---|
このExampleは examples/aop/interceptor/chain にあります。 |
Interceptorをカスタム実装する場合は、次のインターフェースを実装するクラスを作成します。
namespace seasar\aop; interface MethodInterceptor { /** * @param MethodInvocation $invocation */ function invoke(MethodInvocation $invocation); }
次のようなサンプルインターセプターを作成してみます。
class SampleInterceptor implements seasar\aop\MethodInterceptor { public function invoke(seasar\aop\MethodInvocation $invocation){ print 'Before' . PHP_EOL; // <-- 次のインターセプターや実際のメソッドを呼び出す前の処理 $result = $invocation->proceed(); print 'After' . PHP_EOL; // <-- 次のインターセプターや実際のメソッドを呼び出した後の処理 return $result; } }
MethodInvocation::proceed()を実行すると、次のインターセプターや実際のメソッドを呼び出します。 1つのコンポーネントに複数のアスペクトが定義されている場合は、以下のよう実行されます。
MethodInterceptor::invokeメソッドの引数で渡されるMethodInvocationインスタンスを介して、アスペクト対象のインスタンスや、ReflectionMethod、メソッド引数などを取得できます。
© Copyright The Seasar Foundation and the others 2005-2010, all rights reserved. |