2015-08-04 5 views
-3

Цель:обновления среды выполнения с Исполнителями

  1. Запуск класса
  2. Измени второго класса
  3. Сохранить и компилировать второго класса
  4. без остановки и запуска первого класса изменения ко второму классу должны видны в консоли

Проблема:

В настоящее время изменения не отражаются после сохранения и компиляции.

+0

Что вы пытаетесь достичь? Остерегайтесь переменной области: runnable действителен только в (1). Кроме того, loadclass не загружает новый класс, если он уже загружен: http://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html # loadClass% 28java.lang.String,% 20boolean% 29 – Florent

+0

@ThreaT Что вы пытаетесь сделать, это плохой программный паттерн, и я не даже уверен, что это сработает. Класс hotswap в основном используется отладчиками. Имейте в виду также, что «Класс hotswap» не означает «Инстанс hotswap»! – Florent

+0

@ThreadT Обязательно проверьте демо на моем [втором ответе] (http://stackoverflow.com/a/31815421/3071928);) –

ответ

3

Если вы хотите перезагрузить измененный файл класса, вам не нужно запрашивать загрузчик классов, который уже загрузил предварительную версию, это всегда будет поставлять эту уже загруженную версию. Используйте новый загрузчик классов, например

... 
Class<?> reloadClass(String classLocation, String className) throws Exception { 
    URL url = new File(classLocation).toURI().toURL(); 
    URLClassLoader cl = new URLClassLoader(new URL[] { url }, String.class.getClassLoader()); 
    Class<?> c = cl.loadClass(className); 
    cl.close(); 
    return c; 
} 
... 

EDIT:

Хорошо, я проверил его с упрощенной версией кода. Изменения в моей только немного косметические (скопированы из Binkan Salaryman). Оно работает.

public class Autorunner extends Thread { 

private Class runnable; 
private File output; 

public Autorunner(Class runnable, File output) { 
    this.runnable = runnable; 
    this.output = output; 
} 

@Override 
public void run() { 
    try { 

     //This is only to get the location of the classfile 
     URL url = Test.class.getProtectionDomain().getCodeSource().getLocation(); 

     Class runtimeClass = reloadClass(url,Test.class.getName()); 
     Method method = runtimeClass.getMethod("main", String[].class); 
     method.invoke(null, (Object) null); 
     System.out.flush(); 
    } catch (NoSuchMethodException | SecurityException | IOException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassNotFoundException ex) { 
    } 
} 


Class<?> reloadClass(URL classLocation, String className) throws ClassNotFoundException, IOException { 
    URLClassLoader cl = new URLClassLoader(new URL[] { classLocation }, String.class.getClassLoader()); 
    Class<?> c = cl.loadClass(className); 
    cl.close(); 
    return c; 
} 
+0

«Это работает». vs «этот код далеко не работает» - что вы сделали? - Какие ошибки вы получаете? - Что вы можете с ними поделать? - Он работает с изменениями? - повторите, если нет. - ... –

+0

Как я уже писал, я протестировал код. Ты думаешь, я шучу, если я напишу это? Может быть, у вас есть исключение, проигнорированное пустым уловом? – Joachim

3

Я думаю, что фрагмент кода Иоахима отлично работает (не проверено):

public class Autosaver implements Runnable { 
    public static void main(String args[]) { 
     Autosaver instance = new Autosaver(); 
     Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(instance, 0, 5, TimeUnit.SECONDS); 
    } 

    @Override 
    public void run() { 
     try { 
      Class<? extends Test> Test_class = reloadClass(Test.class.getProtectionDomain().getCodeSource().getLocation(), Test.class.getName()); 
      new Autorunner(Test_class, new File("Test.txt")).run(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    private static <X> Class<X> reloadClass(URL classLocation, String className) throws ClassNotFoundException, IOException { 
     URLClassLoader loader = new URLClassLoader(new URL[] { classLocation }, String.class.getClassLoader()); 
     @SuppressWarnings({"unchecked"}) 
     Class<X> result = (Class<X>) loader.loadClass(className); 
     loader.close(); 
     return result; 
    } 
} 
+0

Не * добавить * код, * отредактировать * его ... любую информацию об ошибке? ... –

+0

См. Редактирование Joachim, выглядит примерно так же. И кстати. '' ClassLoader'' хранит ссылку '' Class'', и если вы пишете '' Test.class'', вы ссылаетесь на скомпилированный '' class Test'' –

1

Вот испытанный, полностью рабочий, показательный класс тест горячей замены программы. Перед запуском вам нужно создать «./Test.jar» и «./tmp/Test.jar» и поместить в файл «Test.class» (без пакета в коде, без папки в банке), который вы скомпилировали с методом main и оператором System.out.println.

Если что-то не работает должным образом, обязательно сообщите описания ошибок и то, что вы пробовали.

Код для "./Autosaver.jar" (название не имеет значения):

public class Program { 
    private static final File Test_classLocation = new File("./Test.jar"); 
    private static final File alternativeTest_classLocation = new File("./tmp/Test.jar"); 

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { 
     System.out.println("Test.class location = " + Test_classLocation.getAbsolutePath()); 
     System.out.println("alternative Test.class location = " + alternativeTest_classLocation.getAbsolutePath()); 
     while (true) { 
      testInvocation(); 
      swapFiles(Test_classLocation, alternativeTest_classLocation); 
      Thread.sleep(3000L); 
      testInvocation(); 
      swapFiles(Test_classLocation, alternativeTest_classLocation); 
      Thread.sleep(3000L); 
     } 
    } 

    private static void testInvocation() throws IOException, ClassNotFoundException { 
     Class<?> Test_class = reloadClass(Test_classLocation.toURI().toURL(), "Test"); 
     invokeMain(Test_class, new String[0]); 
    } 

    private static void swapFiles(File a, File b) throws IOException { 
     Path aTempPath = new File(b.getAbsolutePath() + ".tmp").toPath(); 
     Files.move(a.toPath(), aTempPath); 
     Files.move(b.toPath(), a.toPath()); 
     Files.move(aTempPath, b.toPath()); 
    } 

    private static <X> Class<X> reloadClass(URL classLocation, String className) throws ClassNotFoundException, IOException { 
     URLClassLoader loader = new URLClassLoader(new URL[]{classLocation}, null); 
     @SuppressWarnings({"unchecked"}) 
     Class<X> result = (Class<X>) loader.loadClass(className); 
     loader.close(); 
     return result; 
    } 

    private static void invokeMain(Class<?> mainClass, String[] args) { 
     try { 
      Method mainMethod = mainClass.getDeclaredMethod("main", String[].class); 
      mainMethod.invoke(null, new Object[]{args}); 
     } catch (NoSuchMethodException | IllegalAccessException e) { 
      throw new Error(e); 
     } catch (InvocationTargetException e) { 
      System.err.println("invocation of " + mainClass.getName() + ".main(" + String.join(",", args) + ") threw an exception:"); 
      e.printStackTrace(); 
     } 
    } 
} 

код для "./Test.jar!Test.class":

public class Test { 
    public static void main(String[] args) { 
     System.out.println("old " + Test.class); 
    } 
} 

Код для "./tmp/Test.jar!Test.class":

public class Test { 
    public static void main(String[] args) { 
     System.out.println("new" + Test.class); 
    } 
} 

Выход:

Test.class location = D:\rd\test\out\artifacts\Autosaver\.\Test.jar 
alternative Test.class location = D:\rd\test\out\artifacts\Autosaver\.\tmp\Test.jar 
new class Test 
old class Test 
new class Test 
old class Test 
new class Test 
old class Test 
new class Test 
old class Test 
new class Test 
old class Test 
new class Test 
old class Test 
new class Test 
old class Test 
new class Test 
old class Test 
new class Test 
... 

Вы можете скачать молнию в демо here.

+0

Это не то, что я ищу. ** Тест ** должен быть файлом '.java', а не файлом' .jar'. – ThreaT

+0

Почему я даже пытаюсь помочь вам ?.- –

+0

Просто прочитайте документацию ['' URLClassLoader'' (http://docs.oracle.com/javase/8/docs/api/java/net/URLClassLoader.html) и рефакторинг для каталогов вместо компиляции ваших * .java-файлов с помощью '' javac'' –