- PR -

クラスローダについて。

投稿者投稿内容
シミー
会議室デビュー日: 2004/02/22
投稿数: 16
投稿日時: 2005-03-30 15:30
ClassLoaderクラスのdefineClassメソッドを使い、
他の場所にあるクラスファイルを読み込み、
そのクラスを操作するコードを作成しています。
行おうとしている手順は、以下の通りです。
1.クラスローダのあるディレクトリとは、別のディレクトリにあるクラスファイルを指定する。
2.そのクラスファイルを、バイナリデータとして値を取得する。
3.そのバイナリデータを、defineClassメソッドを使って、Classに変換する。
4.その変換したClassのメソッドを使用し、値を取得する。

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;

public class NetworkClassLoaderSample extends ClassLoader {
 private static byte[] byteArray = null;

 public static void main (String argv[]){
  try {
   // ファイルの読み込み
   // 今回は同一ディレクトリの「UserCount.class」を指定
   FileInputStream in = new FileInputStream("./UserCount.class");
   ByteArrayOutputStream out = new ByteArrayOutputStream();

   byte buff[] = new byte[1024] ;
   int len ;

   // バイナリの抽出
   while((len = in.read(buff, 0, 1024)) != -1 ){
    out.write(buff, 0, len);
   }
   // byte[]型に変換
   byteArray = out.toByteArray();

   // Classに変換
   Class userClass = new NetworkClassLoaderSample().
             defineClass("UserCount", byteArray, 0 ,byteArray.length);
   // ↓「UserCount」と表示されます
   System.out.println(userClass.getName());

↓ここでClassCastExceptionが発生
   // UserCountクラス型を生成
   UserCount user = (UserCount) userClass.newInstance();

   // UserCountクラスのgetUserメソッドの呼び出し
   System.out.println(user.getUser());

  } catch (Exception e ){
   System.err.println("Exception :" + e);
  }
 }
}

バイナリデータを抽出し、Classに変換することはできていると思いますが、
そのClassを使用する場合は、どのようにすれば良いのでしょうか?
よろしくお願い致します。
Edosson
ぬし
会議室デビュー日: 2004/04/30
投稿数: 675
投稿日時: 2005-03-30 15:56
引用:

   // Classに変換
   Class userClass = new NetworkClassLoaderSample().
             defineClass("UserCount", byteArray, 0 ,byteArray.length);
   // ↓「UserCount」と表示されます
   System.out.println(userClass.getName());


この時点で、userClassには、UserCountのインスタンスが格納されているはずではないのでしょうか。
「instanceof UserCount」で、結果はtrueになります?

だとしたら、
引用:

↓ここでClassCastExceptionが発生
   // UserCountクラス型を生成
   UserCount user = (UserCount) userClass.newInstance();


ここは、
引用:

// そのままキャストしちゃえば?
UserCount user = (UserCount) userClass;


じゃダメなのでしょうか。

ちなみに、Class#newInstance()のAPIドキュメントには、
「new 式に空の引数リストを指定した場合と同じように、クラスのインスタンスが生成されます。」
とあります。

[ メッセージ編集済み 編集者: Edosson 編集日時 2005-03-30 15:57 ]
(株)ぽち
ぬし
会議室デビュー日: 2002/09/10
投稿数: 376
投稿日時: 2005-03-30 16:10
クラスローダとかネットワーククラスローダとか使ったことないのでアレなんですが
ちょっと疑問に思ったので。

そもそもネットワーククラスローダを使用するということは

クラスパスの通っていないクラスに対してメソッドをコールしたい

ってのが目的ではないんでしょうか?
# 簡単すぎますか・・

上記前提で、まずUserCountにクラスパスが通っていること自体
おかしいという考えにはならないんでしょうか?

クラスパスが通っているならクラスローダなんか使わず、そのまま
使えばいいですよね?

何が言いたいかというと、キャストではなくリフレクションでメソッド
をコールするのが通常の手法だったりしませんか?


かなり乏しい知識で言っているので間違いあれば指摘欲しいです・・。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2005-03-30 16:19
クラスローダが違うと同じクラスでもキャスト可能な関係にはなりません。
クラスはパッケージという名前空間がありますが、
さらにその上位にクラスローダという名前空間が存在しますので、
サンプルのクラスローダでロードしたクラスは
そのクラスローダでロードしたクラスにしかキャストはできません。

サンプルの例ではサンプルのクラスローダでロードしたクラスから
カレントスレッドのクラスローダでロードしたクラスに
キャストしようとしてClassCastExceptionが発生していると思います。


試していないですが、コンストラクタにカレントスレッドのクラスローダを
渡してあげればいいような気がします。外していたらゴメンナサイ・・・。
java.net.URLClassLoaderのソースを参考にしてみては如何でしょうか。
似たような事をやっていますので、参考になると思います。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2005-03-30 16:27
>(株)ぽちさん
ネットワーククラスローダを使用するときに
対象のクラスのクラスパスが不明な状態でも
キャストを行うケースがあります。
どういうケースかというと

サーバ側
HogeImplクラス(Hogeインターフェイスの実装クラス)

クライアント側
Hogeインターフェイス

が存在していて、ネットワークでHogeImplを取得し、
リフレクションでHogeImplのインスタンスを生成して、
Hogeインターフェイスにキャストして
メソッドコールを行うという場合等に使用できます。

ネットワークの他に、バイトコード操作系のAPIなどを使用したときにも有用です。
a-san
常連さん
会議室デビュー日: 2004/03/15
投稿数: 30
投稿日時: 2005-03-30 17:17
以前に、HTTP経由でクラスをロードするコードを書いたことがあります。
これでいけると思います。
// クライアントアプリを、HttpClassLoaderを介して読み込み、実行する。
Class entry_class = Class.forName(entry_classname, true, httpClassLoader);
KandataClient client = (KandataClient) entry_class.newInstance();
シミー
会議室デビュー日: 2004/02/22
投稿数: 16
投稿日時: 2005-03-30 17:21
Edossonさん、(株)ぽちさん、かつのりさん、ご返信ありがとうございます。

引用:__________________________________________________________

// Classに変換
Class userClass = new NetworkClassLoaderSample().
          defineClass("UserCount", byteArray, 0 ,byteArray.length);

// ↓「UserCount」と表示されます
System.out.println(userClass.getName());

この時点で、userClassには、UserCountのインスタンスが格納されているはずではないのでしょうか。
「instanceof UserCount」で、結果はtrueになります?

instanceof演算子で比較をしてみようと思いましたが、
Class userClassとUserCountクラスは、
非互換性がない型と、コンパイル段階でエラーになりました。

引用:_______________________________________________________

↓ここでClassCastExceptionが発生
   // UserCountクラス型を生成
   UserCount user = (UserCount) userClass.newInstance();
ここは、

引用:
// そのままキャストしちゃえば?
UserCount user = (UserCount) userClass;

そのままキャストを行いますと、こちらも同様に
ClassクラスからUserCountクラスにキャストできませんと、
コンパイルエラーになりました。

引用:_________________________________________

サンプルの例ではサンプルのクラスローダでロードしたクラスから
カレントスレッドのクラスローダでロードしたクラスに
キャストしようとしてClassCastExceptionが発生していると思います。

試していないですが、コンストラクタにカレントスレッドのクラスローダを
渡してあげればいいような気がします。外していたらゴメンナサイ・・・。
java.net.URLClassLoaderのソースを参考にしてみては如何でしょうか。
似たような事をやっていますので、参考になると思います。

java.net.URLClassLoaderクラスのソースを見ながら、
もう少し頑張ってみます。
(株)ぽち
ぬし
会議室デビュー日: 2002/09/10
投稿数: 376
投稿日時: 2005-03-30 17:22
引用:

かつのりさんの書き込み (2005-03-30 16:27) より:
サーバ側
HogeImplクラス(Hogeインターフェイスの実装クラス)

クライアント側
Hogeインターフェイス

が存在していて、ネットワークでHogeImplを取得し、
リフレクションでHogeImplのインスタンスを生成して、
Hogeインターフェイスにキャストして
メソッドコールを行うという場合等に使用できます。


情報ありがとうございます。

なるほど。まさにEJBのようなケースですね。
質問で出てたケースはもろクラスパスのクラスを使っていたので
視野が狭くなっていました。

確かに有用ですが、EJBやDIContainerとか色々ある現状
そこまで根っこから作るケースってあるんですかね?
# また話しをそらしてますが・・

スキルアップ/キャリアアップ(JOB@IT)