new 演算子が使うクラスローダ [日記]
Javaでは、クラスファイルをロードするクラスローダを自分で作成して利用することが出来ます。
が、そのクラスローダを使ってロードしたクラスをインスタンス化するには、Class
クラスのメソッド newInstance()
を使うことになるので、面倒です。
new
演算子が使えると楽で良いな。
と言うか、new
演算子が使うクラスローダって変更できるのでしょうか?
(new
演算子が使われた時に対象のクラスがロードされていないと自動でクラスファイルがロードされるわけですが、そのときに使われるクラスロードのこと)
ちょっとネットで調べると、
Thread#setContextClassLoader()
で変更できる。
なんて記述が見つかりますが、自分で試したら直ぐに分かることですが、全くのデタラメです。
じゃあ一体、new
演算子が使うクラスローダは、どこで決まるのでしょう。
【答え】
new
演算子を実行しているコードがあるクラスをロードしたクラスローダを使う。
「new
演算子が使うクラスローダを自前のクラスローダに変更したい場合は、new
演算子を使っているクラスを自前のクラスローダでロードする」と言う事です。
解決したような解決しないような…
class A { public void func() { B b = new B(); } }
こんなクラスで、クラスB
を自前のクラスローダでロードしたいなら、クラスA
を自前のクラスローダでロードする必要があります。
ClassLoader cl = new MyClassLoader(); Class c = cl.loadClass("A"); Method m = cl.getMethod("func"); m.invoke(c.newInstance());
じゃあ、クラスA
を上の様なコードじゃなく、new A()
としたいなら、上のコードがあるクラスを自前のクラスローダでロードしたら良い事になります。
と、まぁ、いつまで経っても newInstance()
は必要になってしまうわけですが…。
ならば、
最初のクラスのmain()
で、自前のクラスローダでロードし直したら、解決かな?
public class Entry { static public void main(String[] args) { ClassLoader cl = new MyClassLoader(); Class c = cl.loadClass("Entry"); Method m = c.getMethod("main0", String[].class); m.invoke(null, (Object)args); } static public void main0(String[] args) { // 元の main()の中身。 } }
これで、全部自前のクラスローダでロードされるようになりました?
ところが、そう簡単には行かないのです。
クラスローダは階層構造(数珠繋ぎ)になっていて、自分でロードできないクラスは他のクラスローダにロードを委譲しています。
(というか、順番的には、委譲するのが先で、他のクラスローダでロードできなかったクラスを自分がロードを試みる)
委譲先のクラスローダがクラスをロードしたら、そこで new
演算子が使うクラスローダは元に戻ってしまいます。
なので、自前のクラスローダ(上の例の MyClassLoader
)が、CLASSPATH上にあるクラスやJarファイル中のクラスをロード出来るように実装し、他のクラスローダに委譲する前にロードするようにしなければいけません。
(ちょっとクラスの実装についてのお約束から外れてしまいますが…)
コメント 0