- PR -

URLClassLoaderとgetMethodについて

投稿者投稿内容
てま
会議室デビュー日: 2007/06/15
投稿数: 2
投稿日時: 2007-06-15 05:59
URLClassLoaderとgetMethodの関連について
教えていただけませんでしょうか?

コード:
package loader;
import java.lang.reflect.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.io.*;
public class ScreenControl extends Thread {
        public void run(){
                URL baseURL = null;
                File baseDir = null;
                try{
                    baseDir = (new File( "D:\\My Program\\classLoad\\classes\\" )).getCanonicalFile();
                    baseURL = baseDir.toURI().toURL();
                } catch (Exception e){
                    e.printStackTrace();
                }
                URLClassLoader loader = new URLClassLoader( new URL[]{baseURL} );                        
                Class<?> clazz;
                try {
                        clazz = loader.loadClass("loader.OptionClass");
                } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                        return;
                }                
                Method calc1;
                Method calc2;
                Method calc3;
                Object object = null;
                try{
                        object = clazz.newInstance();
                } catch (Exception e){
                        e.printStackTrace();return;
                }

                try {
                        calc1 = clazz.getMethod("calc", new Class[]{int.class});
                        calc2 = clazz.getMethod("calc", new Class[]{String.class});
                        calc3 = clazz.getMethod("calc", new Class[]{DummyViews.class});
                } catch (SecurityException e2) {
                        e2.printStackTrace();return;
                } catch (NoSuchMethodException e2) {
                        e2.printStackTrace();return;
                }
                Object ret1 = null; //戻り値
                Object ret2 = null; //戻り値
                Object ret3 = null; //戻り値
                try {
                        ret1 = calc1.invoke(object, new Object[]{ 3 });
                        ret2 = calc2.invoke(object, new Object[]{ "3" });
                        ret3 = calc3.invoke(object, new Object[]{ new DummyViews(3) });
                        //  = object.メソッド((int)1); と同じ
                } catch (IllegalArgumentException e3) {
                        e3.printStackTrace();return;
                } catch (IllegalAccessException e3) {
                        e3.printStackTrace();return;
                } catch (InvocationTargetException e3) {
                        e3.getCause().printStackTrace();return;
                }                
                System.out.println("ret1="+ret1);
                System.out.println("ret2="+ret2);
                System.out.println("ret3="+ret3);
        }
}


コード:
package loader;

public class OptionClass{
        public int calc( int p ){
                return p*4;
        }
        public int calc( String p ){
                return Integer.parseInt(p) * 4;
        }
        public int calc( DummyViews p ){
                return p.calc(4);
        }
}



コード:
package loader;
public class DummyViews{
        int b;
        public DummyViews(int f){
                b = f;
        }
        public int calc( int p ){
                return p*b;
        }
    
}



コード:
package loader;
public class TestMain {
        public static void main(String[] args) {
                ScreenControl thread = new ScreenControl();
                thread.start();
        }
}



クラスローダーで取ってきたクラスからメソッドを取得しようとしています
実行すると

D:\\My Program\\classLoad\\classes>java loader.TestMain
ret1=12
ret2=12
ret3=12

と出ます。

今度はこれをswingで実行させようと次のクラスを書きました。

コード:
package loader;
import javax.swing.*;

public class JFrameTest {
        public static void main(String[] args){
                JFrame frame = new JFrame("フレームのタイトル");

                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setBounds( 10, 10, 300, 200);
                frame.setVisible(true);
                ScreenControl sc = new ScreenControl();
                sc.run();
        }
}



実行します。

D:\\My Program\\classLoad\\classes>java loader.JFrameTest
ret1=12
ret2=12
ret3=12

これも問題ないです。

ところがIEのプラグインで実行させるとエラーが起きました。

コード:
package loader;
import javax.swing.*;
public class RootInit extends JApplet {
        public void init() {
                ScreenControl sc = new ScreenControl();
                sc.run();
        }
}



コード:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<TITLE>index.html</TITLE>
</HEAD>
<BODY>
<OBJECT classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
    width="800" height="600" align="baseline" 
    codebase="http://java.sun.com/products/plugin/1.4/jinstall-14-win32.cab#Version=1,4,0,mn">
    <PARAM NAME="code" VALUE="loader/RootInit">
    <PARAM NAME="codebase" VALUE="classes">
</OBJECT>
</BODY>
</HTML> 



index.htmlをIEで開きます

ava.lang.NoSuchMethodException: loader.OptionClass.calc(loader.DummyViews)
at java.lang.Class.getMethod(Unknown Source)
at loader.ScreenControl.run(ScreenControl.java:49)
at loader.RootInit.init(RootInit.java:12)
at sun.applet.AppletPanel.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

とJavaコンソールにでました
どうやらメソッドが見つからなかったようです
おなじプログラムなのにどうして動きがちがうのでしょうか?

<動作環境>
WindowsXPsp2
Java 1.6.0_01-b06
IE 6.0.29
Java Plug-in 1.6.0_01
朝日奈ありす
大ベテラン
会議室デビュー日: 2007/05/02
投稿数: 189
お住まい・勤務地: 最北の地
投稿日時: 2007-06-15 07:22


[ メッセージ編集済み 編集者: 仙堂ありす 編集日時 2007-06-16 20:41 ]
未記入
ぬし
会議室デビュー日: 2004/09/17
投稿数: 667
投稿日時: 2007-06-15 09:43
引用:

Sourceみなおさないのかこのひと


引用:

どのクラスLvで実行されているか、メソッドに渡している引数は正確か考えればミスに気づくでしょう。


通りすがりの第三者ですが・・・どこに問題があるのか分かりません。メソッドに渡している引数が正しくないということですか?
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2007-06-15 09:50
まずは、Classa#getMethodsでどんなメソッドがあるかダンプしてみては?
sawat
大ベテラン
会議室デビュー日: 2006/08/02
投稿数: 112
投稿日時: 2007-06-15 10:31
ちゃんと意図したクラスファイルが読み込まれているのか確認してみては?
コード:
System.out.println(loader.getResource("loader/OptionClass.class"));

nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2007-06-15 10:39
違うClassLoaderで読み込まれたclassは別物ですから、それでオーバーロードがうまく認識していないんじゃないですかね?

引用:

calc3 = clazz.getMethod("calc", new Class[]{DummyViews.class});


で引数に渡しているDummyViewsはAppletのクラスローダによってブラウザ経由で読み込まれたDummyViewsで、

"D:\My Program\classLoad\classes\"を基準としたURLClassLoaderで読み込んだ
loader.OptionClass#calc(DummyViews)のDummyViewsはURLClassLoaderで読み込まれたDummyViewsなのではないですか?
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2007-06-15 16:34
追試。

コード:

/** 読み込み対象のクラス。classファイルにして2箇所に配備 */
public class Hoge {
public void hoge(Hoge hoge) {}
}

/** 読み込みテスト */
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
// クラスローダ1
File baseDir1 = (new File( "C:\\eclipse\\workspace\\TestA\\bin\\" )).getCanonicalFile();
URL baseURL1 = baseDir1.toURI().toURL();
ClassLoader c1 = new URLClassLoader(new URL[]{baseURL1});

// クラスローダ2。1とはURLが違う
File baseDir2 = (new File( "C:\\eclipse\\workspace\\TestB\\bin\\" )).getCanonicalFile();
URL baseURL2 = baseDir2.toURI().toURL();
ClassLoader c2 = new URLClassLoader(new URL[]{baseURL2});

System.out.println("ClassLoader 1 : " + c1);
System.out.println("ClassLoader 2 : " + c2);

Class clazz1 = c1.loadClass("test.Hoge");
Class clazz2 = c2.loadClass("test.Hoge");

System.out.println("class 1 : " + clazz1.getCanonicalName() + " - " + clazz1.getClassLoader());
System.out.println("class 2 : " + clazz2.getCanonicalName() + " - " + clazz2.getClassLoader());

Object obj1 = clazz1.newInstance();
System.out.println(obj1);

Object obj2 = clazz2.newInstance();
System.out.println(obj2);

// クラスローダの違うClassを引数に渡している
Method method1 = clazz1.getMethod("hoge", clazz2);
}
}


とするとjava.lang.NoSuchMethodExceptionとなります。
各所のオブジェクトのClass.getClassLoader()でどのクラスローダからロードされたのか確認して見てください。

# ClassLoaderのテストって面倒だ…

[ メッセージ編集済み 編集者: nagise 編集日時 2007-06-15 16:35 ]
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2007-06-15 17:04
補足。
ローカル実行だと動く理由は、ClassLoaderの委譲の仕組みのせいではないでしょうか。
ClassLoaderは自身の親のClassLoaderを用いてまずclassのロードを試みます。
そのため、ローカル実行の場合は通常のClassLoaderによって対象classがロードされ、
実際にはURLClassLoaderは動いていません。

これは、通常のクラスパスの通ったclassをURLClassLoaderでロードし、
class.getClassLoader()でClassLoaderを確認すると分かります。

本件の場合"loader.OptionClass"がローカル実行時にはクラスパスが通っており、
かつApplet起動時のAppletClassLoaderには見えない場所にあるのではないでしょうか?

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