Javaプラットフォームにおけるコード自動生成の考え方の変遷

普段は上位のフレームワークや製品の中に隠蔽されているため、一般の業務アプリケーションのプログラマーには気づきにくいことですが、同じJava言語の環境といっても、時代によってコード自動生成に対する考え方は変わってきています。建材が木、石、鉄筋コンクリートと変化すれば当然建物の工法や設計も異なるように、フレームワークやソフトウェアのアーキテクチャー、開発手法は技術の進歩に合わせて望ましい形に進化させていかなくてはなりません。
ここで、Javaプラットフォームの自動生成に関する技術動向について簡単にまとめてみたいと思います。

昔は静的なソースコード生成が中心

昔といってもほんの10年くらい前のことですが、JDK1.2やJava IDL(CORBA)、RMI、EJB1.xなどが使われていた時代のことです。一部の先進的な研究を除くと、この時代にJavaで自動生成というと一般的にソースコードを自動生成するツールを意味しました。ただし、自動生成といっても、実際さまざまなレベルのものがあります。

  • ifブロックなどコードテンプレートを自動生成する
  • getterメソッドやsetterメソッドを自動生成する
  • アプリケーションの初期の雛形を自動生成する
  • XDocletのようにソース中のメタデータからHomeインタフェースやxmlのような配管コードを自動生成する
  • 画面、ロジック、データアクセスまで完全に自動生成する
  • リモートメソッドのスタブ、スケルトンを自動生成する
  • 単体試験のコードを自動生成する
  • UMLのクラス図やシーケンス図からソースコードを自動生成する

以前に、SIerがExcel→Javaのコード自動生成をPGに押し付けるのは善か悪か? - 達人プログラマーを目指してで書いたExcelを使ったコード生成というのもこの範疇に属します。このようなソースコードの自動生成では、一時的な生産性は向上するものの、一般的には保守や変更が難しいコードが大量に作られることになるため、使い捨てのプロトタイプなど特殊な目的を除けば最近は敬遠される傾向があります。ただし、アプリケーションの雛形生成やIDEやエディタのコードテンプレート機能は保守性の問題が少ないため、現在でも有用だと思います。(getter、setter生成なども依然として当面は有用ですが、Java言語にプロパティが導入されるなど言語自体が進歩すれば今後廃れるようになるでしょう。)

2000年代後半は動的プロキシーが流行

自動生成の技術としては、多くのJavaプログラマーからは(場合によってはアーキテクトも?)いまだによく知られていないようですが、JDK1.3からリクレクションAPI拡張機能として導入された動的プロキシーという大切な機能があります。
Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle
Javaの理論と実践: 動的プロキシーで装飾する
この機能を使うことで、Javaのインターフェースを実装するクラスを実行時に動的に生成することが簡単にできるようになりました。この技術はSpringやSeasar2AOP機能の基礎にもなっています。また、この技術により従来のEJBの静的なスタブコード生成などはほぼ完全に時代遅れとなってしまいました。さらに、動的プロキシーによって、試験の際にインターフェースのモック実装を動的に生成するようなEasyMockjMockといったライブラリーが広く利用されるようになってきたこともプログラマーにとっては重要なことでしょう。(なお、JDKの動的プロキシーはインターフェースに対してしか利用できませんが、cglibというライブラリーを利用することで、クラスに対しても動的にサブクラスを生成することで似たような処理が可能となります。)

これからはバイトコード書き換え技術が一般的に応用されるようになる

動的プロキシーは確かに強力な技術ですが、

  • publicなインターフェースに対してしか利用できない
  • ロキシーのためthis経由での自己呼び出し時に処理を織り込めない
  • cglibでもstaticメソッドやfinalメソッド、コンストラクターの置換は不可

などの制約がありました。したがって、AOPを適用したり、モックテストの作成をしたりする場合に、こうした制約が問題となることも多くありました。古いSpringアプリケーションではインターフェースを実装したサービスクラスやDAOクラスを作るという慣習があり、また、ロジックもドメインオブジェクトではなくこうしたクラスに外だしすることが多いのですが、このような技術上の制約も大いに関連していると思われます。昔のEJBに比べるとPOJOでかなり設計自由度が高くなっているものの、まだまだDDDで自由に設計するには制約があったということです。
ただし、最近はbcelasmJavassist*1といったようなライブラリーに代表されるようなバイトコードを生成したり書き換えたりするような技術がかなり成熟してきています。また、それらの低水準のライブラリーの上にAspectJSpring RooGroovyといったより上位のレイヤーのフレームワークが組み合わせられることで業務アプリケーションでも簡単にこれらのバイトコード変換技術が利用できるようになってきています。この技術を利用することで、メタプログラミングにより従来は手動で作成していた多くの配管コードの記述を自動化することができます。バイトコード変換はJPAの実装でも必須ですし、また、単体テストフレームワークも以前のjMockなどに代わり、JMockitPowerMockなどのようにバイトコード変換を利用した新しいフレームワークが利用できるようになってきており、staticメソッドやprivateメソッド、コンストラクタのモック化すら可能になっています。
なお、Java5から計測API*2というのがあり、クラスロード時にバイトコードを変換するための仕組みをプラグインするためのAPIが標準化されています。
Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle
以前はこうしたバイトコード変換技術は一種の黒魔術のようなあやしいもの、あるいは一部の研究者の遊び道具のように思われていたかもしれませんが、今後はこうした技術が一種のDSLドメイン固有言語)の実装技術として利用されることで(意識するしないにかかわらず)一般のアプリケーション開発者にも手の届くところで利用されるようになってきているのです。*3
もちろん、静的なコード生成や動的プロキシー技術も完全に時代遅れというわけではなく適材適所で今後も使われる局面はあると思いますが、自動生成と聞いた時にJavaソースコードを生成するものしか頭に思い浮かばないのは、少なくともアーキテクトとしては勉強不足でいけないと思います。

*1:Rubyと同様にもともと日本人PGの発明によるものです。

*2:もともと性能測定や監視用のログを埋め込むというところから来ている名前だと思うが、バイトコード変換でできることはもちろんそれだけではない。

*3:一部のPGの方からはJavaは既に時代遅れという声も聞かれますが、このあたりの技術が十分に整理されている点は他のプラットフォームに比べてJavaの魅力の一つであると思います。Java言語自体とプラットフォームは別ということもありますが。