JRubyでアノテーションを処理する。
JRuby1.1ではクラスに設定されたアノテーションを直接参照する方法が用意されていない様子。includeしたJavaクラスの「java_class」関数で「Java::JavaClass」というClassのラッパが返されますが、JavaのClassが持つget_annotationとかの関数は持っていません・・・orz。
ということで、クラスから取得するのはあきらめ、別途ユーティリティを作成して、アノテーションにアクセスできるようにしてみました。
- (Rubyで)Moduleに関数annotation,annotationsを追加してアノテーションをとれるようにする。
- 関数内では、自身を引数としてJavaのユーティリティ関数を呼び出す。
- ユーティリティ関数は、引数でクラスを受け取ってそのアノテーションを返す。
という仕組み。関数定義さえやっておけば、以下のような感じで簡単にアノテーションにアクセスできます。
include_class '<javaClass>' <javaClass>.annotations {|a| puts a }
実装
まず、アノテーションを返すJava側のユーティリティです。ただのラッパーですね。
package annotation; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; public class AnnotationUtil { public static <T extends Annotation> T getAnnotation( AnnotatedElement elm, java.lang.Class<T> annotationClass ) { return elm.getAnnotation( annotationClass ); } public static Annotation[] getAnnotations( AnnotatedElement elm ) { return elm.getAnnotations(); } }
Ruby側の関数定義。Moduleに関数を追加しています。
require 'java' module Module include_class 'annotation.AnnotationUtil' # クラスを指定してアノテーションを取得する。 def annotation( annotation_type ) #if self.method_defined?( "java_class" ) # method_defined?はなぜか動作しない・・・。 if self.methods.include?("java_class") return AnnotationUtil.getAnnotation(self.java_class, annotation_type.java_class) else return nil end end # クラスに割り当てられたアノテーションを取得する。 def annotations( ) if self.methods.include?("java_class") return AnnotationUtil.getAnnotations(self.java_class) else return [] end end end
以上。
サンプル
次のようなアノテーションがあって、
package annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * サービスを示すアノテーション * * @version $Revision:$ * @author $Author:$ */ @Target({ElementType.TYPE}) @Retention( RetentionPolicy.RUNTIME ) public @interface Service { /** * サービス名 * @return サービス名 */ String serviceName(); }
「annotation.Annotated」クラスを修飾している場合、
package annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Service(serviceName="Test") public interface Annotated { void method1(); void method2(); }
module Test # annotaionパッケージを取込 include_package 'annotation' # Serviceアノテーションを取得 puts Annotated.annotation(Service) # すべてのアノテーションを列挙 Annotated.annotations.each {|a| puts a.serviceName } end
実行結果です。※「annotation.AnnotationUtil」をJRubyのクラスパスに含めるのをお忘れ無く。
@annotation.Service(serviceName=Test)
Test
あとはこれとERBを組み合わせれば注釈処理プログラムのできあがり! .. といいたいところですが、以下の弱点があります。
とはいえ、javacやaptを使うのは面倒そうなので、さくっと作れるメリットはあるはず。いつか使ってやる!
なお、上のコードではクラスに設定されたアノテーションの取得しかサポートしていないのであしからず。メソッドやフィールドのアノテーション取得は未調査です。