uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

Grailsプラグインの実装

第4回Grailsコードリーディングで学んだことを記録と整理のために書いておきます(MLに出した記事の焼き直し)。


Grailsのプラグイン実装が達成したいことのひとつは、Groovyで記述された「Groovyのプラグインクラス」およびそのインスタンスとしての「Groovyのプラグインオブジェクト」に関して、更新伝播とかのプラグイン管理周りの機能(notify eventとか)を付け足すということだったと思われます。

このために2つの実装方式がありえて、

  • (1)Groovyのプラグインクラスに動的にメソッドをインジェクションし機能を付け足す。これはExpandoMetaClassなどを使ってGrailsのドメインクラス・アーティファクト周りで実行されていることですね。
  • (2)Java側から見たプラグインオブジェクトとしてDefaultGrailsPlugin インスタンスがGroovyのプラグインオブジェクトの代理オブジェクト(Proxyパターン)の役割を果たす。プラグイン周りのメソッドは、Javaで「硬く」記述される。

で、今のGrailsは(2)の方式をとっていると。こうなっている理由は、メソッド呼び出しに関する実行効率の問題なのでしょう。(1)は明らかに遅いですからね。

プラグインとその管理機構は効率のためJavaコードで固めておく。しかし、特定のコールバック記述やプロパティの初期化をGroovyで書けるようにはなっているというわけですね。この場合のGroovyコードはある種の設定ファイルとして振舞っていると。

つまり、初期化時のネタ元としてクロージャ実体やフィールド値を提供するためにDTO的に使われただけ。インスタンス化はちゃんとされており、直接アクセスしようと思えばできるのでしょうけど、実際にはいったん起動されてしまった後では(再読み込み時を除いて)Grailsの動作中で直接アクセスされることはない(←これは仮説で未確認。未知の用途で呼ばれている可能性はある)。


整理すると、Groovyクラスへの動的なメソッドの追加の方法には2つあって、

bold;">Groovyオブジェクトに動的なメソッド追加を行う:低速だが、追加メソッドを動的に作成可能(動的ファインダー実現可能)。Groovyオブジェクトとしては自然な形。ドメインクラス・アーティファクトで採用。
bold;">Groovyオブジェクトの代替を行うJavaクラスでWrapする:高速だが、メソッドはJavaコードなので変更にはコンパイル再起動が必要。この場合のメソッド追加はGrails自体の設計変更を意味する。プラグインで採用。

という感じでしょうか。

もうちょっと一般化すると、動的なメソッド追加に対して、それぞれ追加する言語・される言語に応じて以下のように整理もできるでしょう。

Javaコードに動的にJavaメソッド・フィールドを追加する code insturment API
Javaコードに動的にGroovy/Javaメソッド・フィールドを追加する Groovyの"カテゴリ"
Groovyコードに動的にGroovyメソッド・フィールドを追加する 上の(1)方式(ExpandoMetaClass)
Groovyコードに動的にJavaメソッド・フィールドを追加する 上の(2)方式