web-dev-qa-db-ja.com

実行時に使用済みクラスをリロードJava

私は、ディレクトリを監視し、ディレクトリの変更を検出すると、ディレクトリ内のすべてのテストを実行するプログラムに取り組んでいます。

これには、キャッシュされたコピーを取得する代わりに、プログラムがクラスを動的にロードする必要があります。

テストクラスを動的にロードできます。テストへの変更は、実行時に検出されて使用されます。ただし、これは、テストによってテストされたクラスには当てはまりません。

クラスを動的にロードし、テストクラスのリストを返すための私のコード:

List<Class<?>> classes = new ArrayList<Class<?>>();
    for (File file : classFiles) {
        String fullName = file.getPath();
        String name = fullName.substring(fullName.indexOf("bin")+4)
                .replace('/', '.')
                .replace('\\', '.'); 
        name = name.substring(0, name.length() - 6);

            tempClass = new DynamicClassLoader(Thread.currentThread().getContextClassLoader()).findClass(name)          } catch (ClassNotFoundException e1) {
            // TODO Decide how to handle exception
            e1.printStackTrace();
        }

        boolean cHasTestMethods = false;
        for(Method method: tempClass.getMethods()){
            if(method.isAnnotationPresent(Test.class)){
                cHasTestMethods = true;
                break;
            }
        }
        if (!Modifier.isAbstract(cachedClass.getModifiers()) && cHasTestMethods) {
            classes.add(tempClass);
        }
    }
    return classes;

dynamicClassLoaderは、ここで説明するリローダーと同じです インスタンス化時にクラスをリロードするようにJavaを強制する方法は?

それを修正する方法はありますか?すべてのクラスが動的にロードされると思いました。ただし、DynamicClassLoaderでloadclassを上書きしないことに注意してください。これを行うと、テストクラスがinitを与えるためです

編集:これは機能しません、クラスはロードされますが、その中のテストは検出されません...

List<Request> requests = new ArrayList<Request>();
    for (File file : classFiles) {
        String fullName = file.getPath();
        String name = fullName.substring(fullName.indexOf("bin")+4)
                .replace('/', '.')
                .replace('\\', '.'); 
        name = name.substring(0, name.length() - 6);
        Class<?> cachedClass = null;
        Class<?> dynamicClass = null;
        try {
            cachedClass = Class.forName(name);


            URL[] urls={ cachedClass.getProtectionDomain().getCodeSource().getLocation() };
            ClassLoader delegateParent = cachedClass .getClassLoader().getParent();
            URLClassLoader cl = new URLClassLoader(urls, delegateParent) ;
            dynamicClass = cl.loadClass(name);
            System.out.println(dynamicClass);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

編集編集:私は次のようなテスト方法を検出します:

            for(Method method: dynamicClass.getMethods()){
            if(method.isAnnotationPresent(Test.class)){
                requests.add(Request.method(dynamicClass, method.getName()));
            }
        }
15
Sven

リンクされた回答とまったく同じようにカスタムClassLoaderを使用した場合、メソッドprotected Class<?> loadClass(String name, boolean resolve)はオーバーライドされません。これは、JVMが依存関係を解決しているときに、親クラスローダーに委任することを意味します。そしてもちろん、親ClassLoaderに委任していない場合、いくつかの必要なクラスを見逃すリスクがありました。

最も簡単な解決策は、適切な親クラスローダーを設定することです。現在、Thread.currentThread().getContextClassLoader()を渡しています。これは、委任がnotをそのローダーに委任するが、変更されたクラスをロードすることを主な目的としているため、少し奇妙です。どのクラスローダーが存在し、どれを使用し、どれを使用しないかを考える必要があります。例えば。クラスFooが現在のコードの範囲内にあるが、それを新しいClassLoaderで(再)ロードしたい場合、Foo.class.getClassLoader().getParent()は新しいClassLoadernullである可能性があることに注意してください。ただし、この場合、正しい親であるbootstrapローダーを使用するため、これは問題ではありません。

意図に一致する適切な親ClassLoaderを設定すると、そのカスタムClassLoaderは不要になることに注意してください。デフォルトの実装( URLClassLoader を参照)はすでに正しいことを行っています。また、現在のJavaバージョンではCloseableであるため、動的ロードのシナリオにさらに適しています。

クラスのリロードの簡単な例を次に示します。

import Java.io.IOException;
import Java.net.URL;
import Java.net.URLClassLoader;

public class ReloadMyClass
{
  public static void main(String[] args)
  throws ClassNotFoundException, IOException {
    Class<?> myClass=ReloadMyClass.class;
    System.out.printf("my class is Class@%x%n", myClass.hashCode());
    System.out.println("reloading");
    URL[] urls={ myClass.getProtectionDomain().getCodeSource().getLocation() };
    ClassLoader delegateParent = myClass.getClassLoader().getParent();
    try(URLClassLoader cl=new URLClassLoader(urls, delegateParent)) {
      Class<?> reloaded=cl.loadClass(myClass.getName());
      System.out.printf("reloaded my class: Class@%x%n", reloaded.hashCode());
      System.out.println("Different classes: "+(myClass!=reloaded));
    }
  }
}
10
Holger