Rooとインタータイプ宣言

Rooのアプトシリーズ中

Rooを理解する上で重要な要素の一つがAspectJインタータイプ宣言です。

インタータイプ宣言は,AspectJ などが提供する, 「クラスに新たなメソッド,フィールド等を追加する」仕組みを指します.

複数のクラスに共通のメソッドやフィールドを持たせたいとき, 対象のクラス群から実装の一部を分割しておきたいときに使用されます.

http://netail.net/aosdwiki/index.php?%A5%A4%A5%F3%A5%BF%A1%BC%A5%BF%A5%A4%A5%D7%C0%EB%B8%C0

英語だとInter-Type DeclarationsでよくITDなどと略されています。

Rooで一つのEntityクラスを作ったときはこんな感じになります。

f:id:yamkazu:20101127181909p:image

Employee.javaの中身を見ると以下のようなコードになっています。

@RooJavaBean
@RooToString
@RooEntity
public class Employee {

    @NotNull
    private String name;

}

JPAでカラムにマッピングするためのフィールドと@RooXXXのアノテーションが付与されています。privateフィールドしかないのでかなり不思議な印象を受けます。

よくあるJPAのEntityは本来以下のようなコードです。

@Entity
public class Employee {

    @Id
    @GeneratedValue
    private Long id;
    @NotNull
    private String name;

    ... getter setter
}

でもRooのEntityのコードはPrivateフィールドと@RooXXXのアノテーションしかありません。ここで重要なのがタイトル通りのインタータイプ宣言ですよと。Rooは本来JPAのEntityとして必要なコードをJavaファイル名_Roo_挿入されるアスペクト.ajという形でAspectJのファイルで分離してコンパイル時(AspectJでコンパイルするとき)にEntityに必要なメソッド,フィールド等を追加するという仕組みをとっています。

Employee_Roo_Entity.ajを見てみます。このようなコードになっています。

privileged aspect Employee_Roo_Entity {
    
    declare @type: Employee: @Entity;
        
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long Employee.id;

    public Long Employee.getId() {
        return this.id;
    }

    ... 以下省略

Javaっぽいコードですが、これはAspectの定義ファイルです。この内部でインタータイプの定義がされています。

インタータイプメンバー宣言

private Long Employee.id;

javaっぽいフィールド定義に見えますが、これはEmployeeクラスにidというフィールドを追加するという意味になります。

インタータイプメソッド宣言

    public Long Employee.getId() {
        return this.id;
    }

フィールド宣言とほぼ一緒ですが、これはEmployeeにgetId()というメソッドを追加するという意味です。

declare句

これはちょっと上記と記法が異なりますがなんとなく見た目で理解できると思います。

    declare @type: Employee: @Entity;

これは@typeでクラス、インタフェースのアノテーションを追加するという宣言していて、Employeeクラスに@Entityアノテーションを追加する記法になります。

特権アスペクト

privileged aspect Employee_Roo_Entity {

privilegedという見慣れないキーワードがありますが、これは特権アスペクトというやつです。通常動作だとAspectJはJavaのアクセス機構の制限をうけた状態で動作します。この例だとEmployeeのprivateフィールドにアクセスするようなアスペクトは記述できません。privilegedを付けるとコレが一切の制限を受けなくなり、今回だとEntityのprivateなフィールドにアクセスし放題になります。

@Configurableアノテーション

これはITDとは直接関係ありませんが、これをクラスに付与すると普通にクラスをnewしてもSpringのDIが有効になるという、SpringがDDDをサポートするために用意している機能です。RooのEntityはActiveRecodeスタイルコードを生成します。つまりEntityがpersist()、sava()やstaticなfindByIdなどの検索メソッドを持っています。これを実現するためにEntityの内部でJPAのEntityManagerをインジェクトする必要があります。そのために@Configurableアノテーションを付与しています。

ちなみにEntityのstaticなメソッドでどうやってEntityManagerとっているのかなと見てみたら以下のようなコードになっていました。

    public static final EntityManager Employee.entityManager() {
        EntityManager em = new Employee().entityManager;
        if (em == null) throw new IllegalStateException("Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)");
        return em;
    }

自分自身をnewして、そのタイミングで@Configurableの機能でentityManagerがフィールドにInjectされて、そのフィールドを取得するという形です。なんかこれスマートなんですかね?ちょっと強引な気もしましたが、単純といえば単純な仕組みです。

このような形でRooはITDを使って毎回わりと退屈なEntityのコーディングをサポートしてくれています。

これは丸山先生の資料からパクった情報ですが、Rooの生みの親であるBen Alexはとあるブログに以下のようなコメントをしています。

Thanks for posting this blog. I always enjoy seeing people’s reaction when they discover the power of inter-type declarations (ITDs) and the incredible usefulness of it. The surprising fact is ITDs have existing in AspectJ for years and years, resulting in a very mature building block that hasn’t been previously exploited in this manner for productivity or code generation.

私は人々が、インタータイプ宣言のパワーとその信じられないほどの有用性を発見した時の反応を見るのを、いつも楽しんでいる。
驚くべき事実は、ITDは、何年も何年も前から、AspectJの中に、非常に成熟した構成要素として存在していたということである。ただ、それは以前には、こうしたやり方では、生産性とコード生成の為には、開拓されてこなかったのである。 

http://dhruba.name/2009/12/31/eclipse-ajdt-intertypes-and-push-in-refactoring/

ITDすごいですね!それにしてもBenのコメントの日付をみると1/1ですね。新年から本当お疲れ様です。

ITDでついてもっと詳しく知りたい方は以下の書籍がおすすめです。

アスペクト指向入門 -Java ・ オブジェクト指向から AspectJプログラミングへ

アスペクト指向入門 -Java ・ オブジェクト指向から AspectJプログラミングへ


よくわらないやり取りシリーズ的な記述は若干読みにくいですが、内容は非常に濃くてAspectJはもちろんのことITDについても言及されています。

と今日はこのへんで。