バイトコード操作技術とは?


Javaは、JavaのソースコードをコンパイルしてJavaバイトコード(クラスファイル)に変換し、それをJava仮想マシンで実行して動かすものです。これは、基本中の基本です。が、実をいえば、最近はこれ以外の形でJavaのプログラムを作り動かすような手法が使われつつあります。それは「クラスのバイトコード操作」と呼ばれる技術です。

バイトコード操作とは、文字通り「Javaのクラスファイルの中身を直接書き換えることでクラスの中身を変えてしまう」技術です。この手法を使えば、例えば、既にあるクラスの中に、新たにフィールドやメソッドを追加したり、中にあるメソッドの内容を変更したりできてしまいます。それまではソースコードを修正してコンパイルすることでクラスファイルを作成する必要がありましたが、この技術を使えばソースコードがなくともクラスファイルを直接操作できるわけです。

なぜ、そんなトリッキーとも思える方法が使われるようになったのでしょうか。それは、最近注目されつつある「アスペクト指向プログラミング(AOP)」などのように、クラスに後から様々な処理を追加するような技術を実現するのに必要であるからです。例えば、あるメソッドの中に、そのメソッドの本来の役割とは異なる処理を追加しなければならないような場合があったとしましょう。ソースコードでそれらを書くと、1つのメソッド内に複数の処理が組み込まれ、非常に気持ちの悪い状態となります。後からそのメソッドに必要な処理を組み込んで対処することで、そうした問題を解決できるようになります。

まぁ、言葉でいってもわかりにくいでしょうから、実際にバイトコード操作がどういうものか、試してみましょう。ここでは、「Javassist」というフレームワークを使ってみます。これは東京工業大学の千葉滋氏によるもので、現在、JBossのサブプロジェクトとして開発が行われています。以下のURLより、最新のファイルをダウンロードすることができます。

http://labs.jboss.com/javassist/

JBossのJavassistのページ。ここからファイルのダウンロードページやチュートリアルページなどに移動できる。


Javassistによるバイトコード操作は、思った以上に簡単です。基本的な手順を整理すると以下のようになります。

1.ClassPoolインスタンスを取得する。ClassPoolは利用可能なクラスを階層的に整理しアクセスできるようにするものです。クラスパス上にあるすべてのクラスが、ここから利用可能です。
2.ClassPoolからCtClassを取得する。CtClassは、Javassistに用意されているもので、クラスを抽象化したクラスです。このCtClassインスタンスとして、操作するクラスを取得します。
3.CtClassのメソッドを利用して、クラスを操作します。メソッドやフィールドを操作する場合は、やはりJavassistに用意されているCtMethod、CtFieldといったクラスのインスタンスとしてそれらを操作します。
4.最後にCtClassのwriteFileメソッドを使ってクラスを保存します。これによりクラスファイルが書き換えられ、次にクラスがロードされるときには変更された状態となります。

クラス、フィールド、メソッドがそれぞれCtClass、CtField、CtMethodという形として用意されていますので、これらの使い方さえある程度わかれば、クラスファイルを操作するのはそう難しいものではありません。