2009年12月17日木曜日

AspectJ で using 的な構文を模倣してみる

C# の using のような感じで Java でも書いてみたいが、現時点の Java (SE 6) では文法的に無理があるので、AspectJ も併用して、どんな風になるか試してみる。 ■ 概要 要点は、あるコードブロックの冒頭でリソースの使用を宣言すると、そのコードブロックの終わりでリソースが自動的に開放されるという事。さらに、次の2点も実現したい。
  • そのスコープをブロックの内部に限定してリソースを隔離する事
  • try/finally のような感じで、例外発生時にもちゃんとリソースが開放される事。
■ 問題 以下のような模擬リソースクラスがあるとする。
public interface Disposable {
   void dispose();
}

public class SomeResource implements Disposable {
   public void write(String str) {
      System.out.println(str);
   }
   public void throwException() {
      throw new RuntimeException("something wrong");
   }
   @Override
   public void dispose() {
      System.out.println("disposed");
   }
}
この模擬リソースを[概要]の項に書いたような要件のもとで使う場合、普通の Java で書くと例えば以下のようなコードになる。
public class Client {
   public static void main(final String[] args) {
      {
         SomeResource d = new SomeResource();
         try {
            d.write(args[0]);
         } finally {
            if (null != d) d.dispose();
         }
      }
   }
}
これをリソースの扱いを簡略化した以下のようなコードに書き直して、なおかつ等価な動作を実現するような AspectJ コードを考えてみたい。d.write() の代わりに d.throwException()を呼んで例外を発生させても d.dispose() が実行される事にも留意する。
public interface Using {}
public class Client {
   public static void main(final String[] args) {
      new Using() { SomeResource d = new SomeResource(); {
         d.write(args[0]);
      }};
   }
}
■ 解1 一番単純な解の一つは、とりあえずこんな感じになると思う。
public aspect DisposeAspect {
   Disposable disposable;
   pointcut settingField(Object o): set (* Using+.*) && args(o);
   after(Object o): settingField(o) {
      disposable = (Disposable)o;
   }
   after(): call(Using+.new()) {
      disposable.dispose();
   }
}
実行引数として"hello"を与えて実行すると、コンソールに"hello"と"disposed"が表示される。また write() の代わりに throwException() を呼ぶとスタックトレースと"disposed"が表示される。 ■ 課題 上記コードは Proof of concept として書いた叩き台で、実用的なものにするにはさらに以下のような課題がある。
  • 抽象化して使いまわせるようにする事。例えば、Disposable 実装クラスだけじゃなくJDBC の Connection などのような任意のオブジェクトにも使えるようにしたい。
  • マルチスレッドで使えるようにする事。
  • ネストした場合について考えること
おいおい考えてみたい。

0 件のコメント:

コメントを投稿