JBoss AOPとAOPフレームワークの役目アスペクト指向のバリエーション解説(3)(2/2 ページ)

» 2005年01月19日 12時00分 公開
[米山学(クロノス 専務取締役),@IT]
前のページへ 1|2       

アスペクト指向アノテーション

 JDK 5.0では「アノテーション(JSR-175)」と呼ばれる新しい言語仕様が追加された。アノテーションはXDocletを使ったことがある人ならすぐに理解できると思われるが、接頭辞@を持つタグをソースコード中に記述して、さまざまな付加的要素をコードに加えることができる(XDocletではタグによってコードの自動生成を指示する)。

 JBoss AOPではこのアノテーション機能をサポートしており、AOPの目的の1つでもある「カプセル化した振る舞いを透過的に提供する」という部分をアノテーションによって補完している。

 では、このアノテーションを用いたAOPの例を見ていこう。先ほどのMessengerクラスを次のように書き換えてほしい。

public class Messenger {
    @Trace
    public Messenger() {}
    @Trace @Greet
    public void printMessage(String name) {
        System.out.println(name);
    }
}
リスト6 Messenger.java

 ここで記述されている「@Trace」や「@Greet」がJDK 5.0で導入されたアノテーションのタグである。対応する2つのインターフェイスを作成しておく。

public @interface Trace {}
リスト7 Trace.java

public @interface Greet {}
リスト8 Greet.java

 @Greetタグに対応するインターセプタは先ほどのGreetingInterceptorを使うとして、@Traceタグに対応するインターセプタ「TraceInterceptor」を新たに作成する。このインターセプタはメソッドやコンストラクタ呼び出し、フィールド値の読み書きなどをトレース(追跡)するためのインターセプタである。

import org.jboss.aop.joinpoint.*;
import org.jboss.aop.advice.Interceptor;
public class TraceInterceptor implements Interceptor {
    public String getName() {
        return "TraceInterceptor";
    }
    public Object invoke(Invocation invocation) throws Throwable {
        String msg = "";
        if (invocation instanceof MethodInvocation) {
            msg = ((MethodInvocation)invocation).getMethod().toString();
        } else if (invocation instanceof ConstructorInvocation) {
            msg = ((ConstructorInvocation)invocation).getConstructor().toString();
        } else if (invocation instanceof FieldReadInvocation) {
            msg = ((FieldReadInvocation)invocation).getField().toString();
        } else if (invocation instanceof FieldWriteInvocation) {
            msg = ((FieldWriteInvocation)invocation).getField().toString();
        } else {
            msg = invocation.getClass().getName().toString();
        }
        try {
            System.out.println("TRACE START : " + msg);
            return invocation.invokeNext();
        } finally {
            System.out.println("TRACE END");
        }
    }
}
リスト9 TraceInterceptor.java

 さらにMessengerクラスのprintMessage()メソッドを非staticにしたため、Testerクラスもインスタンスを生成するように変更しておこう。最後にjboss-aop.xmlの記述を次のように変更する。

<?xml version="1.0" encoding="UTF-8"?>
<aop>
    <bind pointcut="all(@trace)">
        <interceptor class="TraceInterceptor" />
    </bind>
    <bind pointcut="execution(* Messenger->@greet(..))">
        <interceptor class="GreetingInterceptor" />
    </bind>
</aop>
リスト10 jboss-aop.xml

 この記述では@Greetタグと@Traceタグをジョインポイントとして定義し、それぞれのインターセプタを指定している。

Testerの実行結果

run:
    [java] TRACE START : public Messenger()
    [java] --- Constructor ---
    [java] TRACE END
    [java] TRACE START : public static void     Messenger.printMessage(java.lang.String)
    [java] Hello, JBoss AOP
    [java] TRACE END

 コンストラクタに対してはTraceInterceptorのみが適用され、printMessage()メソッドに対してはTraceInterceptorとGreetingInterceptorの両方が適用されていることが分かる。

 このようにアノテーションを利用することで、開発者が各ジョインポイントに対して適用するインターセプタの制御が簡単で分かりやすいものとなることが理解いただけただろうか。

 JBoss AOPは本来J2EEサーバであるJBoss ASに適用するために開発されたものである。そのためここではEJBの次期バージョンである「EJB 3.0」のコーディングを取り上げ、どのようにアノテーションによるAOPが実現されているのか見てみよう。

 まずは次に示すEJB 3.0による簡単なStateless Session Beanのサンプルコードを見ていただきたい。

import javax.ejb.Remote;
@Remote
public interface HelloRemote {
    public String sayHello(String name);
}
リスト11 HelloRemotejava

import javax.ejb.Stateless;
@Stateless
public class HelloBean implements HelloRemote {
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}
リスト12 HelloBean.java

 HelloRemoteはリモートインターフェイスであり、@Remoteタグによってそれを指定している。このHelloRemoteをimplementsするHelloBeanがEnterprise Bean本体であり、@StatelessタグはこのBeanが「Stateless Session Bean」であることを印付け、EJBコンテナに対してそのように振る舞うことを指定している。

 サーバ側の開発はたったこれだけである。従来のようにEJBObjectを継承したComponentインターフェイスや、EJBHomeを継承するHomeインターフェイスを作成する必要もなければ、煩雑なDDの記述に悩まされることもない。また、Bean本体にもコールバックメソッドの実装を記述する必要もない。

 サーバ側だけでなく、呼び出すクライアント側もコーディングが簡単になっている。従来のようにHomeインターフェイスの参照やナローキャスティングすることなく、単にルックアップするだけでBeanを使用することができる。

public class Client {
    public static void main(String[] args) throws Exception {
        InitialContext ctx = new InitialContext();
        HelloRemote hello =
(HelloRemote)ctx.lookup(HelloRemote.class.getName());
        System.out.println(hello.sayHello("EJB3"));
    }
}
リスト13 HelloBeanを利用するクライアントコード 「Client.java」

 EJB 3.0で取り入れられたこのような手法は「アスペクト指向アノテーション」とも呼ばれており、EJBのサービスレベルのアスペクトを@タグを用いてコードに適用することで、EJB開発をより簡単で透過的なものにしている。


<補足>
EJB3を実際にJBoss上で動かすためには「JBoss 4.0.1 RC2」と「EJB 3.0 Preview 2」が必要となる。
ダウンロードしたアーカイブをそれぞれ解凍・展開したら、EJB 3.0 Preview 2のlibディレクトリ配下の「ejb3.deployer」と「ejb3-interceptors-aop.xml」をJBossの「server/all/deployディレクトリにコピーする。その後「run-call」コマンドでJBossを起動する。


Dynamic AOP

 JBoss AOPではコンパイル時の静的なインターセプションだけでなく、実行時に動的にアドバイスを追加したり取り除いたりすることが可能なDynamic AOPという仕組みが提供されている。Dynamic AOPを用いることで次のような2つの機能を実現できる。

  • インスタンスごとのインターセプション
  • ホット・デプロイメント

 それでは、簡単なDynamic AOPの例を見てみよう。Dynamic AOPを使用するためにはjboss-aop.xmlに次のようにエレメントを記述する。Dynamic AOPではプログラム内でポイントカットやインターセプタを指定するため、jboss-aop.xmlにはそれらは記述しない。

<?xml version="1.0" encoding="UTF-8"?>
<aop>
    <prepare expr="all(Messenger)"/>
</aop>
リスト14 jboss-aop.xml

 次にTesterクラスを以下のように書き換えて実行する。

import org.jboss.aop.*;
import org.jboss.aop.advice.*;
public class Tester {
    public static void main(String[] args) throws Exception {
        Messenger msg = new Messenger();
        msg.printMessage("JBoss AOP");
        AdviceBinding binding =
            new AdviceBinding("execution(* Messenger->printMessage(..))", null);
        binding.addInterceptor(GreetingInterceptor.class);
        AspectManager.instance().addBinding(binding);
        msg.printMessage("JBoss AOP");
    }
}
リスト15 Tester.java

Testerの実行結果

run:
[java] JBoss AOP
[java] Hello, JBoss AOP

 最初のprintMessage()メソッドの呼び出し時(8行目)にはインターセプションは実行されない。ソースコードの10〜13行目の部分がホットデプロイメントの記述であり、2回目のprintMessage()メソッドの呼び出し(15行目)時にはインターセプションが実行されていることが分かる。

 次にインスタンスに対してインターセプタを適用する例を見てみよう。

import org.jboss.aop.*;
import org.jboss.aop.advice.*;
public class Tester {
    public static void main(String[] args) throws Exception {
        Messenger msg1 = new Messenger();
        Messenger msg2 = new Messenger();
        Advised advised = (Advised)msg2;
        advised._getInstanceAdvisor().insertInterceptor(
                                                          new GreetingInterceptor());
        msg1.printMessage("JBoss AOP No.1");
        msg2.printMessage("JBoss AOP No.2");
    }
}
リスト16 Tester.java

Testerの実行結果

run:
[java] JBoss AOP No.1
[java] Hello, JBoss AOP No.2

 この例ではMessengerクラスのインスタンスmsg1とmsg2を生成し、msg2に対してのみインターセプタを適用するようにコーディングしている(10〜12行目)。これにより、実行結果からも分かるとおりインスタンスmsg2にだけ「Hello, 」の文字列が追加されることになる。


 以上ざっとJBoss AOPの実装がどのようなものかを見てきたが、いかがだっただろうか。同じAOPの実装でもAspectJとJBoss AOPの違いを確かめることもできたと思う。しかし、実装が異なるとはいえ、どちらもアスペクト指向プログラミングのためのフレームワークであるということに変わりはない。「アスペクト」という“ソフトウェアの持つさまざまな「側面」”を、コアロジックから分離するための仕組み。それを開発者に提供することこそが、AOPフレームワークの役目なのである。

 次回は米BEA SystemsもスポンサーとしてバックアップするAspectWerkzを取り上げたい。

前のページへ 1|2       

Copyright © ITmedia, Inc. All Rights Reserved.

注目のテーマ