Wicketがクラスローダを差し変える方法
はじめに
WicketがReloding対応のためにクラスローダを差し替える方法について、基本的なところをメモします。
「基本的なところ」と書いたのは細部に謎が残っているからです。
私の好奇心を満たせる程度には分かったので、まあいいや、みたいな。
実装方法の基本的なところ
currentThreadのcontextClassLoaderにReloadingClassLoaderのインスタンスを突っ込んでおいて、適切な場面でそれを取り出すことになります。
currentThreadはJava標準のアレです。
currentThreadのcontextClassLoaderに突っ込むタイミング
以下の2カ所です。
- フィルタ初期化時(WicketFilter.init()メソッド)
- リクエストがきたとき(WicketFilter.doGet()メソッド)
下記はWicketFilter.init()の抜粋です。
引用部分でgetClassLoader()を使用しています。ReloadingWicketFilter使用時にはgetClassLoader()はReloadingClassLoaderのインスタンスを返します。
結果ReloadingClassLoaderのインスタンスが、Thread.currentThread().setContextClassLoader()メソッドの引数になります。
final ClassLoader previousClassLoader = Thread.currentThread().getContextClassLoader(); final ClassLoader newClassLoader = getClassLoader(); try { if (previousClassLoader != newClassLoader) { Thread.currentThread().setContextClassLoader(newClassLoader); } (以下略)
ReloadingWicketFilter使用時のクラス図は以下のようになっています。
getClassLoader()がオーバーロードされるわけです。
WicketFilter.doGet()メソッドも類似の処理が記述されています。
currentThreadのcontextClassLoaderから取り出すところ
ここが「私の好奇心を満たせる程度には分かったので〜」と書いてしまった理由だったりします。
currentThreadのcontextClassLoaderを取り出す際には、getContextClassLoader()を使用するわけですが、「必然性があってgetContextClassLoader()で取り出しているか?」を全部チェックするのはちょっとなぁ、みたいなところがありまして。
getContextClassLoader()、クラスローダが欲しいときには結構普通に使いますしね。
現在分かっている範囲でgetContextClassLoader()が必要だと私が言える箇所は、Applicationクラス(実際にはサブクラスですけども)のインスタンス生成です。
ここで、「自分を生成したクラスローダにクラス名の解決を依頼する」というJavaの仕様が生きてきます。
Wicketを利用してアプリを書く際にApplicationクラスが基点になりいろいろなオブジェクトを生成するわけです。よって、Applicationを生成するクラスローダをReloadingClassLoaderにしておけば、Appicationクラスが生成するオブジェクトもReloadingClassLoaderによってロードされたクラスに基づいて生成されます。これでReloadingClassLoaderの使用が伝播していきます。
WicketFilter.init()では、ファクトリ内でgetContextClassLoader()を使用してReloadingClassLoaderを取得しています。
デフォルトではContextParamWebApplicationFactory.createApplication()メソッドがAppicationオブジェクトを生成します。下記に一部抜粋します。
protected WebApplication createApplication(final String applicationClassName) { try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = getClass().getClassLoader(); } final Class applicationClass = loader.loadClass(applicationClassName); if (WebApplication.class.isAssignableFrom(applicationClass)) { // Construct WebApplication subclass return (WebApplication)applicationClass.newInstance(); } (以下略)
最後に
細かい謎は残っているのですが、クラスローダの差し替えについての調査は、ここまでにしようかと今は思っています。