クラスパスの動的変更を検討しています。うまく機能する解決策を 1 つ見つけましたが、それは addURL() への明示的な呼び出しを使用して行われます。(おそらく起動時)
ただし、デフォルトのクラスローダーがクラスを見つけられないように見える場合は、実行時にクラスローディングプロセスをインターセプトしてクラスを見つけたいと思います。ClassLoaderをサブクラス化してデフォルトに委譲findClass()
し、これらのメソッドが呼び出されたことを示すデバッグ行を出力しようとしましたが、クラスが暗黙的なクラスローディングloadClass()
を介して依存クラスを使用している場合、それらが呼び出されることはありません。
// regular object instantiation with 'new'
BrowserLauncher launcher;
launcher = new BrowserLauncher();
// static methods
Foobar.doSomethingOrOther();
// Class.forName()
Class cl = Class.forName("foo.bar.baz");
// reflection on a Class object obtained statically
Class<Foobar> cl = Foobar.class;
// do something with cl, like call static methods or newInstance()
このような状況でクラスローディングはどのように機能しますか? (対 Classloader.loadClass() が明示的に呼び出される単純なケース)
以下は、カスタムクラスローダーでの私の試みです。の引数リストを指定して DynClassLoader0.main() を使用し、{"some.package.SomeClass", "foo", "bar", "baz"}
some.package.SomeClass が外部の .jar ファイルにある他のクラスを参照する場合、上記のメソッドのいずれかを使用すると、なぜ私の DynClassLoader0 の findClass() および loadClass( ) 呼ばれる?loadClass が呼び出されるのは、以下の main() 関数で loadClass を明示的に呼び出したときだけです。
package com.example.test.classloader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class DynClassLoader0 extends ClassLoader {
public DynClassLoader0()
{
super();
}
public DynClassLoader0(ClassLoader parent)
{
super(parent);
}
public void runMain(String classname, String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
// [***] here we explicitly use our classloader.
Class<?> cl = loadClass(classname);
Method main = cl.getMethod("main", String[].class);
main.invoke(null, new Object[] {args});
}
@Override protected Class<?> findClass(String name) throws ClassNotFoundException
{
System.out.println("findClass("+name+")");
return super.findClass(name);
}
@Override public Class<?> loadClass(String name) throws ClassNotFoundException
{
System.out.println("loadClass("+name+")");
return super.loadClass(name);
}
static public void main(String[] args)
{
// classname, then args
if (args.length >= 1)
{
String[] classArgs = new String[args.length-1];
System.arraycopy(args, 1, classArgs, 0, args.length-1);
ClassLoader currentThreadClassLoader
= Thread.currentThread().getContextClassLoader();
DynClassLoader0 classLoader = new DynClassLoader0(currentThreadClassLoader);
// Replace the thread classloader - assumes
// you have permissions to do so
Thread.currentThread().setContextClassLoader(classLoader);
try {
classLoader.runMain(args[0], classArgs);
}
catch (Exception e) {
e.printStackTrace();
}
}
else
{
System.out.println("usage: DynClassLoader {classname} [arg0] [arg1] ...");
}
}
}
編集:私はすでにこれらの質問を見てきました:
編集: kdgregory が以下で言っていることは正しいと思いました。クラスローダーを明示的に使用すると ([***]
コメントとしてコード内の行を参照)、そのクラスから実行されるすべてのコードは、同じクラスローダーから暗黙的なクラスローディングを引き起こします。しかし、私の DynClassLoader0.loadClass() は、最も外側の明示的な呼び出し以外では決して呼び出されません。