JMockitで拡張クラスローダーに読まれるクラスをモックするのを足掻いた結果
惨敗だ。
クラスローダーの委譲関係は、親が先で通常3つのクラスローダーがいる。
ブートストラップ・クラスローダー
拡張クラスローダー
アプリケーション・クラスローダー(システム・クラスローダー)
MockUp対象クラスが拡張クラスローダーにいる。
クラスを書き換えると、JMockit関連のクラスが埋め込まれるため、いざ動こうとすると拡張クラスローダーからは見付けられない。
じゃあ、拡張クラスローダーにjmockit.jarを読ませればどうなるか。
拡張クラスローダーに読み込ませるには、二つのやり方がある。
- lib/extにjarを置く
- -Djava.ext.dirsでディレクトリを指定
junitを加えると、テストケースが見つからないよ、ってなる。
おいおい、結局全部になるんじゃねーの?
そこから考えられる方針は二つ。
- 拡張クラスローダーに寄せる
- アプリケーションクラスローダーに寄せる
ワクワクしねーから、斜め上を試す。
システムクラスローダーを作ってみる。
システムプロパティ-Djava.system.class.loaderで指定可能。
システムクラスローダーはアプリケーションクラスローダーを親に持ち、新たな委譲関係ができる。
そのため、ClassLoaderを引数に取るpublicコンストラクタが必要となる。
その先、かなりごにょごにょ。
アプリケーションクラスローダーをURLClassLoaderにキャストして、クラスパスをURL配列で掠め取り、委譲関係を辿り拡張クラスローダーを見つけて、リフレクションでそのURLをねじ込む。なんとなく雰囲気でAccessController使ってみたり。
さきほどの拡張クラスローダーに寄せるパターンを実現するための一つの方法として、ねじ込みクラスローダーが完成したわけだが、使う気にならん!
ちなみにjmockitのソースを読む限りでは、Startupを読み込んだクラスローダーがシステムクラスローダーと異なった場合、ちょっとしたことをやっているようだ。
あと、もちろんjavaagentはシステムクラスローダーによって読み込まれることは決まってる。
考えるのをやめよう。幸せは訪れない。