So-net無料ブログ作成

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ファイル中のクラスをロード出来るように実装し、他のクラスローダに委譲するにロードするようにしなければいけません。
(ちょっとクラスの実装についてのお約束から外れてしまいますが…)


タグ:Java
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。