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 件のコメント:
コメントを投稿