https://docs.oracle.com/javase/jp/8/api/java/lang/instrument/package-summary.html
プログラマーがバイトコードを書き換えるためには、エージェントというものを作る。エージェントを書き換え対象のJVMに適用するには2通りの方法がある。
- JVMの実行時に-javaagentオプションによってエージェントJARを指定する。-javaagentは複数指定できる。
- すでに起動済みのJVMに対してエージェントを適用する場合は、別JVMとしてエージェントを起動して起動済みのJVMにアタッチする。jconsoleがこちらのパターン。Attach APIというものを使うようだが、これは開発ベンダー依存APIなので注意。
バイトコードを書き換えることができるので、普通のプロダクトではできないようなある意味、黒魔術っぽいことができるようになる。このインストゥルメンテーションを使っている有名なプロダクトを調べてみた(ドキュメントで調べただけで実際には1つも試してはいない)。
- JRebel・・・サーバー上にデプロイするアプリケーションのように長時間起動するアプリケーションを開発中にソースコードを書き換えた時にIDEがコンパイルしたバイトコードを使ってサーバーが使っているバイトコードを置き換えることで、リアルタイムに変更を反映できる。いわゆるホットスワッピングとかホットコード置換とかホットデプロイと呼ばれるもの。デバッガーがコードを変更するのと似ているが、適用できる範囲が違うらしい。Spring-Loadedも似ている。販売元による比較がこちらにある。Seaser(S2Container)はDIコンテナだからエージェントを使ったアプローチではないけど実現しようとしていることは似ている。
- NewRelic・・・JVMに限らずソフトウェアの監視を行うサービスだが、JVMアプリケーションを監視する場合は、-javaagentで指定する。ログを監視サーバーに送るのかな。
- Lombok・・・ お決まりの「ボイラープレート」コード削減することができる。EclipseなどのIDEに導入するときに-javaagentで指定する。エージェントだけでなくjavax.annotation.processingも使用して実現している。
- JMockit・・・テスト時のモックを作るためのツール。
- JaCoCo・・・テスト時のカバレッジ測定ツール。
- AspectJ・・・アスペクト指向言語、ツール。
インストゥルメンテーションを使うと色々面白いことができるけど、リスクも多い。考慮点は以下の通り。
- 変更されたバイトコードそのものを見ることはできないから、ソースコードに書いた通りに動くことを求めているシチュエーションでは使うべきではない。つまり挙動そのものを書き換えないユースケースのみで使う方が安全。Lombokにはdelombokという機能で書き換えたソースコードを生成できる。NewRelicは性能監視しかしないので、副作用は限定的なはず。他のプロダクトは開発時やテスト時に使うのでほとんどの場合、実運用では使わない。AspectJは実運用でも使えてしまうので注意が必要。
- 複数のインストゥルメンテーション製品を使うとちゃんと動かないことがある。しかもエラーがわかりにくく、はまりやすい。
- リフレクションを使うよりは速い。
- 学習コストが高い。バイトコードを操作する機能を作る時は、ASM、BCEL、Javassistなどを使うがどれも簡単ではない。例えばASMを使う場合はバイトコードとJVMの動作の仕組みを理解する必要がある。
0 件のコメント:
コメントを投稿