Javassist or cglib
Swingアプリケーション用のバイトコードエンハンサに何を使うかの検討だが、結論としてはデフォルトにJavassistを使い、場合によってはcglibも使えるようにシステムプロパティの値によってエンハンサ(を利用したAOP用プロキシの実装)を選べるようにした。※1
Javassistを選択する理由だが、基準として重要視する二つの指標を比べたところ、
- 配布するjarのサイズ
cglib << Javassist
- 実行時の性能
Javassist >> cglib
という結果が出たからだ。
アプリケーションをJava Web Startで配布することを考えると、jarファイルのサイズは小さいに越したことは無い。その点cglibは316KBとJavassistの584KBに比べて200KB以上小さいのは魅力だが、いくつかのベンチマークテストを行った結果ではJavassistがcglibを圧倒したことが大きかった※2。 イントラネットでアプリケーションのダウンロードを実行することを前提とすると、200KBの差は大したことは無いだろう、という思惑もある。
なお、性能に関して今回はjavassistが優勢だったが、テストの仕方やちょっとした条件、ライブラリィのバージョンによっても結果が変わるため、継続した評価が必要だろうと思う。※3
また、Javassistもcglibも拡張したクラスのメソッド(メッセージ)を捕捉する際にフィルタリングを施すことができるのだが、このフィルタの実装に関してもJavassistの方が好みだったことも選択の理由となった。
- JavassistにおけるProxyFactoryのフィルタとハンドラの設定
//ファクトリに対してデフォルトのハンドラとフィルタを設定できる ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(this.clazz); factory.setFilter(new MethodFilter(){ @Override public boolean isHandled(Method method) { //真を戻したメソッドのみ拡張したハンドラの処理対象となる }}); factory.setHandler(new MethodHandler(){ @Override public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable { //捕捉したメソッドの実行前後に挿入したい処理を記述 }});
※1 やはりプリプロセッサが欲しいなぁ。C#のIFDEFと同等のものがあるだけでも違うんだが。
※2 自分で使うことしか考えていないため、テスト内容が偏っており物議を醸し出す可能性があるので、ベンチマークのソースコードとサマリは掲載しないことにする。同じクラスを拡張するクラスの生成、及び拡張したクラスのインスタンスを生成する速度を比較した結果である。
※3 今回はJavassistに3.8.0.GA、cglibは2.2を使用している