はじめに
Javaのクラスファイルというのは、ソースコードからコンパイルして作成する――これが、長い間の常識でした。しかし昨今、この常識を覆すような技術が次々と登場しています。最近のフレームワークなどを見ていて、ふと不思議に思ったことはありませんか? XMLで必要な情報を定義すると、自動的にそのクラスが生成されたり、既にあるクラスの機能が書き換わったりする。プログラマがソースコードを書くこともなく、必要なクラスが作成されてしまう。そうした「クラスの自動生成」の技術を大幅に進歩させたのが「クラスのバイトコードを直接書き換える」技術です。
対象読者
- Javaを使ったプログラム作成を行っている中級レベルのプログラマ。
- クラスのバイトコード操作について興味のあるひと。
- 同じような
Bean
クラスをひたすら作り続けるのに、いい加減、嫌気がさしている人。
クラス生成の新しい形とは?
最近のフレームワークなどでは、XMLの情報を元にクラスを自動的に作成し利用するようなものが増えています。こうしたものは、一体、どうやってクラスファイルを生成しているのでしょうか。
まず、誰もが思いつくのは「ソースコードのテキストを生成し、それを外部からコンパイルする」という方法でしょう。JavaでもRuntime
クラスのexec
を使うことでプログラム内からjavacを実行させることができます。この手法を使えば、確かにクラスファイルを作成することは可能です。
が、この方法では「クラスの改変」に対応するのはかなり大変です。例えば、作成したクラスにフィールドやメソッドを追加したい、となったらどうすればよいでしょう。もちろん、ソースコードのテキストを常に保管しておき、それを修正してはリコンパイルするという形で対応することは不可能ではないでしょう。が、柔軟なクラスの改変を行えるようにするためにはかなり苦労しそうです。
最近の、非常にフレシキブルなクラスファイルの生成を行えるフレームワークは、「クラスのバイトコード操作」技術によって実現されています。これは、文字通り既に完成しているクラスのバイトコードファイルを直接書き換えることでその中身を作り変えてしまう技術です。
この、いってみれば「掟破り」とも言える強力な手法を支えているのが、「Javassist」と呼ばれるフレームワークです。これは、東京工業大学の千葉滋氏による純日本製の技術です。このJavassistを利用することで、実に簡単にクラスのバイトコードを外部から操作できるようになります。
ここでは、このJavassistを利用し、XMLファイルからBean
クラスを自動生成するプログラムを作成してみます。Bean
クラスは、最も自動生成に向いたクラスといってよいでしょう。さまざまなデータを保持するのに、私たちは必要に応じてさまざまなBean
クラスを作成しますが、正直いってプロパティを実装するためのprivateフィールドだのアクセサ・メソッドだのを延々と書き続けなければいけないのは面倒この上ありません。また、後でプロパティを追加したいと思ったら、ソースコードを修正してリコンパイルし……などということを繰り返さねばなりません(次のJava 7ではプロパティの実装がかなり簡略化されそうですが、それまでプログラミングを待つわけにもいきませんね)。
ここでは、あらかじめ定義しておいたDTDに従ってXMLファイルを用意することで、それを読み込み、Bean
クラスを自動生成するプログラムを作成します。もし、プロパティを追加したければ、XMLに追加をして再度プログラムを実行すれば自動的にクラスファイルが変更されます。
Javassistの入手とインストール
Javassistは、現在、以下のURLにてプロジェクトが公開されています。同ページ内のリンクより、最新版をダウンロードすることが可能です。
ここでは、2007年5月現在最新版である3.4GAを利用します。ダウンロードしファイルを展開すると、「javassist.jar」というファイルが中に保管されています。これが、Javassistのライブラリです。これをclasspathの通っている場所に配置してください。この他に、特にインストールなどの作業は不要です。
Beanクラス生成の考え方
BeanMakerというクラスを作成します。ここでは、2種類の機能を実装させます。
- 1つ目は、指定の名前のクラスファイルを自動作成する機能です。これは、
Runtime
クラスのexec
を使い、ごく単純なソースコードファイルを作成して外部からコンパイルし作成をします。
- 2つ目は、XMLファイルの情報を元に、指定のクラスにプロパティを追加する機能です。XMLにプロパティ名と値のタイプを記述しておけば、その値を保管するprivateフィールドとアクセサ・メソッドをクラスファイルに追加します。
1つ目の方法は、プログラム内からクラスを生成する最も基本的な方法です。これは、本記事に限らずいろいろと応用できるテクニックでしょう。そして2つ目が、Javassistを利用したものです。クラスファイルを後から修正するテクニックとして理解してください。
DTDを定義する
では、DTDの定義から行うことにしましょう。ここでは、Beanに追加するプロパティをXMLで定義させることにします。必要な項目としては、次のようになるでしょう。
- ルートには、fieldsを用意する。この中に、フィールドの情報を記述する。
- 個々の情報は、fieldノードとして用意する。fieldにはnameとtypeのノードが用意されており、ここにフィールド名と値のタイプをそれぞれ記述する。
以上の点を整理し、DTDファイルを作成しましょう。ここでは、「beanmaker.dtd」というファイル名で作成しておくことにします。
<?xml version="1.0" encoding="utf-8" ?> <!ELEMENT fields (title,field*)> <!ELEMENT field (name,type)> <!ELEMENT title (#PCDATA)> <!ELEMENT name (#PCDATA)> <!ELEMENT type (#PCDATA)>
fieldsノードには、titleが1つと複数のfieldを収められるようにしてあります。titleは、今回は特に使っていませんが、クラスのタイトル情報として利用できるように項目だけ用意しておきました。