ฉันจะรีสตาร์ทแอปพลิเคชัน Java ได้อย่างไร


96

ฉันจะรีสตาร์ทแอปพลิเคชัน Java AWT ได้อย่างไร ฉันมีปุ่มที่ฉันแนบตัวจัดการเหตุการณ์ ฉันควรใช้รหัสใดในการรีสตาร์ทแอปพลิเคชัน

ฉันต้องการทำสิ่งเดียวกับที่Application.Restart()ทำในแอปพลิเคชัน C #


2
บางทีฉันอาจไม่เข้าใจคำถามของคุณ คุณต้องการให้แอปพลิเคชันของคุณมีปุ่มที่รีสตาร์ทแอปพลิเคชันหรือไม่? ดังนั้นหลังจากที่แอปไม่ทำงานอีกต่อไปแอปควรจะรีสตาร์ทเองได้หรือไม่? นั่นฟังดูเป็นไปไม่ได้สำหรับฉัน
เจย์

ฉันไม่ได้ถามว่าหลังจาก JVM หยุดลงฉันจะถามว่าฉันจะสร้าง java frame หลักของฉันได้อย่างไร
Azfar Niaz

2
เป็นไปได้. ฉันเห็นว่า eclipse workbench รีสตาร์ทบ่อยครั้งแม้แต่ windows ก็ทำเคล็ดลับนี้หลังการอัพเดต ข้อสันนิษฐานที่ผิดคือแอปพลิเคชันเป็นเพียงสิ่งเดียวที่ทำงานโดยไม่มีอะไรอยู่ข้างใต้ เราจะต้องมีตัวเรียกใช้งานที่สามารถรีสตาร์ทได้เต่าลงไปจนสุด
whatnick

เช่นเดียวกับในแอปพลิเคชัน C # ซึ่งคุณสามารถเขียน System.restart () เพื่อทำเช่นนั้นได้อย่างไร
Azfar Niaz

@aniaz คุณควรอัปเดตคำถามเพื่อชี้ให้เห็นว่าคุณต้องการแสดง / ซ่อนเฟรม แอปพลิเคชั่นไม่ใช่เฟรม
whatnick

คำตอบ:


107

แน่นอนว่าสามารถรีสตาร์ทแอปพลิเคชัน Java ได้

วิธีการต่อไปนี้แสดงวิธีการรีสตาร์ทแอ็พพลิเคชัน Java:

public void restartApplication()
{
  final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
  final File currentJar = new File(MyClassInTheJar.class.getProtectionDomain().getCodeSource().getLocation().toURI());

  /* is it a jar file? */
  if(!currentJar.getName().endsWith(".jar"))
    return;

  /* Build command: java -jar application.jar */
  final ArrayList<String> command = new ArrayList<String>();
  command.add(javaBin);
  command.add("-jar");
  command.add(currentJar.getPath());

  final ProcessBuilder builder = new ProcessBuilder(command);
  builder.start();
  System.exit(0);
}

โดยทั่วไปจะทำสิ่งต่อไปนี้:

  1. ค้นหา java ที่เรียกใช้งานได้ (ฉันใช้ java binary ที่นี่ แต่ขึ้นอยู่กับความต้องการของคุณ)
  2. ค้นหาแอปพลิเคชัน (ขวดในกรณีของฉันโดยใช้MyClassInTheJarคลาสเพื่อค้นหาตำแหน่งโถเอง)
  3. สร้างคำสั่งเพื่อรีสตาร์ท jar (โดยใช้ java binary ในกรณีนี้)
  4. ดำเนินการ! (และยุติแอปพลิเคชันปัจจุบันและเริ่มต้นใหม่อีกครั้ง)

6
ไม่มีกรอบเวลาเล็ก ๆ ที่แอปเดียวกันสองเวอร์ชันทำงานในเวลาเดียวกันหรือไม่?
Monir

6
@Veger ทุกคำถามที่มีมากที่เกี่ยวข้องกับคำตอบ
Tomáš Zato - คืนสถานะ Monica

17
@Veger คำถามว่าการSystem.exit(0)ยุติกระบวนการย่อยมีคำตอบเดียวกันหรือไม่ว่าคำตอบนี้ได้ผลจริงหรือไม่และเพราะเหตุใด หากคุณไม่สามารถให้คำอธิบายที่สมเหตุสมผลพร้อมกับคำตอบของคุณได้แสดงว่าคุณทำได้ไม่ดี คำตอบที่ให้คำถามมากกว่าคำตอบไม่ใช่ตัวอย่างของคำตอบอย่างละเอียด คำตอบที่ดีไม่เพียง แต่แสดงรหัส แต่ยังอธิบายถึงวิธีการและเหตุผลที่ใช้งานได้ว่ามีข้อเสียอย่างไรและทางเลือกอื่นคืออะไร คุณไม่ได้พยายามปกปิดสิ่งเหล่านี้ด้วยซ้ำ
Tomáš Zato - คืนสถานะ Monica

8
ความคิดเห็นมากมายที่ถกเถียงกันว่าจะตอบคำถามของ @ Horcrux7 หรือไม่ พวกคุณบอกคำตอบกับเขาได้ตั้งแต่แรกฮ่า ๆ ฉันจะไปข้างหน้าและทำมัน (ฉันรู้ว่าสายไปแล้ว): ไม่มันไม่ ที่นั่น
Voldemort

10
เพื่อตอบคำถามของฉันด้วยตนเอง ตัวอย่างไม่ทำงาน !!! System.exit (0) ยุติกระบวนการไคลเอ็นต์ทันที
Horcrux7

35
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        StringBuilder cmd = new StringBuilder();
        cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java ");
        for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
            cmd.append(jvmArg + " ");
        }
        cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" ");
        cmd.append(Main.class.getName()).append(" ");
        for (String arg : args) {
            cmd.append(arg).append(" ");
        }
        Runtime.getRuntime().exec(cmd.toString());
        System.exit(0);
    }
}

อุทิศให้ทุกคนที่บอกว่าเป็นไปไม่ได้

โปรแกรมนี้รวบรวมข้อมูลทั้งหมดที่มีเพื่อสร้างบรรทัดคำสั่งเดิมขึ้นใหม่ จากนั้นจะเปิดใช้งานและเนื่องจากเป็นคำสั่งเดียวกันแอปพลิเคชันของคุณจะเริ่มทำงานเป็นครั้งที่สอง จากนั้นเราออกจากโปรแกรมดั้งเดิมโปรแกรมลูกยังคงทำงานอยู่ (แม้ใน Linux) และทำสิ่งเดียวกัน

คำเตือน : ถ้าคุณทำงานนี้จะทราบว่ามันไม่เคยสิ้นสุดการสร้างกระบวนการใหม่คล้ายกับระเบิดส้อม


การปรับปรุงที่เป็นไปได้ ManagementFactory.getRuntimeMXBean().getInputArguments()จะให้อาร์กิวเมนต์อินพุตที่ส่งผ่านไปยัง JVM เท่านั้น ไม่มีพารามิเตอร์ที่ส่งผ่านไปยังแอปพลิเคชันของคุณ เช่นjava -jar start.jar -MISSED_PARAM=true. ใน oracle jvm คุณสามารถดึงพารามิเตอร์เหล่านั้นโดยใช้System.getProperty("sun.java.command").
Chris2M

2
VM ระดับบนสุดสามารถสิ้นสุดได้หาก VM ลูกและ VM ระดับบนสุดไม่ได้เชื่อมต่อกันด้วยไพพ์ซึ่งเป็นสิ่งที่เกิดขึ้นเมื่อเริ่มต้น VM ลูก โดยการใช้ProcessBuilderและinheritIO()VM ลูกสามารถเริ่มต้นในลักษณะที่ VM แม่จะสิ้นสุด
Christian Hujer

1
ฉันได้รุ่นนี้ไป ความคิดเห็นนี้จะบอกวิธีหยุด: เปลี่ยนชื่อบางสิ่งในเส้นทางที่มี java.exe
Dale

นี่เป็นการพูดอย่างเคร่งครัดว่าไม่ใช่การรีสตาร์ท แต่เปิดตัว JVM ใหม่ด้วยอาร์กิวเมนต์เดียวกันกับอาร์กิวเมนต์นี้
Thorbjørn Ravn Andersen

4
อะไรคือความแตกต่าง? มีความแตกต่างระหว่างการรีสตาร์ทพีซีและการปิดระบบปฏิบัติการ + การบู๊ตอีกครั้งหรือไม่?
Meinersbur

29

โดยพื้นฐานแล้วคุณทำไม่ได้ อย่างน้อยก็ไม่ใช่วิธีที่เชื่อถือได้ อย่างไรก็ตามคุณไม่จำเป็นต้องทำ

ไม่สามารถเป็นส่วนหนึ่ง

ในการรีสตาร์ทโปรแกรม Java คุณต้องรีสตาร์ท JVM ในการรีสตาร์ท JVM คุณต้อง

  1. ค้นหาjavaตัวเรียกใช้งานที่ใช้ คุณอาจลองใช้System.getProperty("java.home")แต่ไม่มีการรับประกันว่าสิ่งนี้จะชี้ไปที่ตัวเรียกใช้งานที่ใช้เปิดแอปพลิเคชันของคุณ (ค่าที่ส่งคืนอาจไม่ชี้ไปที่ JRE ที่ใช้เปิดแอปพลิเคชันหรืออาจถูกแทนที่ด้วย-Djava.home)

  2. คุณน่าจะต้องการใช้การตั้งค่าหน่วยความจำดั้งเดิมเป็นต้น ( -Xmx, -Xms... ) ดังนั้นคุณต้องพิจารณาว่าการตั้งค่าใดที่ใช้ในการเริ่ม JVM แรก คุณสามารถลองใช้ManagementFactory.getRuntimeMXBean().getInputArguments()แต่ไม่มีการรับประกันว่าจะแสดงการตั้งค่าที่ใช้ สิ่งนี้สะกดแม้กระทั่งในเอกสารของวิธีการนั้น:

    โดยทั่วไปแล้วตัวเลือกบรรทัดคำสั่งบางส่วนของคำสั่ง 'java' จะไม่ถูกส่งไปยังเครื่องเสมือน Java ดังนั้นอาร์กิวเมนต์อินพุตที่ส่งคืนอาจไม่มีตัวเลือกบรรทัดคำสั่งทั้งหมด

  3. หากโปรแกรมของคุณอ่านอินพุตจากStandard.instdin ดั้งเดิมจะหายไปในการรีสตาร์ท

  4. เคล็ดลับและแฮ็กเหล่านี้จำนวนมากจะล้มเหลวต่อหน้าไฟล์SecurityManager.

ไม่ควรต้องมีส่วนร่วม

ฉันขอแนะนำให้คุณออกแบบแอปพลิเคชันของคุณเพื่อให้ง่ายต่อการทำความสะอาดทุกสิ่งและหลังจากนั้นให้สร้างอินสแตนซ์ใหม่ของคลาส "หลัก" ของคุณ

แอปพลิเคชั่นจำนวนมากได้รับการออกแบบมาเพื่อทำอะไรเลยนอกจากสร้างอินสแตนซ์ในเมธอดหลัก:

public class MainClass {
    ...
    public static void main(String[] args) {
        new MainClass().launch();
    }
    ...
}

ด้วยการใช้รูปแบบนี้มันควรจะง่ายพอที่จะทำสิ่งต่างๆเช่น:

public class MainClass {
    ...
    public static void main(String[] args) {
        boolean restart;
        do {
            restart = new MainClass().launch();
        } while (restart);
    }
    ...
}

และปล่อยให้launch()คืนค่าเป็นจริงในกรณีที่แอปพลิเคชันถูกปิดในลักษณะที่ต้องเริ่มต้นใหม่เท่านั้น


3
+1 สำหรับคำแนะนำในการออกแบบที่ดีขึ้น แม้ว่าบางครั้งก็ไม่สามารถทำได้โดยเฉพาะอย่างยิ่งหากใช้ JNI เป็นต้น
maerics

ไลบรารีเนทีฟสามารถแก้ไขสถานะส่วนกลางที่ไม่สามารถแก้ไขได้จากอินเทอร์เฟซ JNI ดังนั้นจะไม่มีวิธี "รีสตาร์ท" สถานะของโปรแกรมนอกเหนือจากการเริ่มกระบวนการ แน่นอนว่าไลบรารีดั้งเดิมควรได้รับการออกแบบที่ดีกว่า แต่บางครั้งคุณก็ขึ้นอยู่กับสิ่งที่คุณไม่สามารถควบคุมได้
maerics

1
คำตอบของคุณไม่ถูกต้องเนื่องจากเป็นไปได้อย่างสมบูรณ์แบบแม้ไม่มีแอปพลิเคชัน / ภูตภายนอกดังที่ Meinersbur แสดงและคำตอบของฉันเอง และเพื่อวัตถุประสงค์ในการอัปเดตด้วยตนเองการรีสตาร์ทแอปพลิเคชันเป็นวิธีแก้ปัญหาที่ดีดังนั้นจึงจำเป็นต้องรีสตาร์ทแอปพลิเคชันด้วยเช่นกัน
Veger

2
แต่คุณทำใช้โปรแกรมประยุกต์ภายนอกjava! คุณลืมไปว่า Java เป็นข้อกำหนดของภาษาไม่ใช่โปรแกรม จะเป็นอย่างไรถ้าฉันรันโปรแกรมของคุณโดยใช้ jvm อื่น ๆ เช่นkaffeเป็นต้น อัปเดตคำตอบของฉันต่อไป :-)
aioobe

1
@Lorenzo ถูกต้อง คำถามเดิมที่นี่คือโดยพื้นฐานแล้วหากแอปพลิเคชันสามารถรีสตาร์ทตัวเองได้ คำตอบคือไม่โดยทั่วไปคุณต้องได้รับความร่วมมือจากผู้อื่น (เช่น OS หรือสคริปต์ Wrapper หรือสิ่งที่คล้ายกัน) ที่สร้างอินสแตนซ์ใหม่
aioobe

10

พูดอย่างเคร่งครัดโปรแกรม Java ไม่สามารถรีสตาร์ทตัวเองได้เนื่องจากจะดำเนินการดังนั้นจึงต้องฆ่า JVM ที่กำลังทำงานอยู่จากนั้นเริ่มต้นใหม่อีกครั้ง แต่เมื่อ JVM ไม่ทำงานอีกต่อไป (ถูกฆ่า) แล้วจะไม่สามารถดำเนินการใด ๆ ได้

คุณสามารถใช้เทคนิคบางอย่างกับคลาสโหลดเดอร์ที่กำหนดเองเพื่อโหลดแพ็คและเริ่มส่วนประกอบ AWT อีกครั้ง แต่อาจทำให้ปวดหัวมากเกี่ยวกับลูปเหตุการณ์ GUI

ขึ้นอยู่กับวิธีการเปิดแอปพลิเคชันคุณสามารถเริ่ม JVM ในสคริปต์ wrapper ซึ่งมีลูป do / while ซึ่งจะดำเนินต่อไปในขณะที่ JVM ออกพร้อมกับรหัสเฉพาะจากนั้นแอป AWT จะต้องเรียกSystem.exit(RESTART_CODE)ใช้ ตัวอย่างเช่นในการเขียนสคริปต์ pseudocode:

DO
  # Launch the awt program
  EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)

แอป AWT ควรออกจาก JVM ด้วยสิ่งอื่นที่ไม่ใช่ RESTART_CODE เมื่อการยุติ "ปกติ" ซึ่งไม่ต้องรีสตาร์ท


ทางออกที่น่าสนใจมาก ปัญหาใน OSX คือโดยทั่วไปแล้วแอป Java จะทำงานจากคอมไพล์JavaApplicationStub... ไม่แน่ใจว่ามีวิธีง่ายๆหรือไม่
Dan Rosenstark

ฉันทำสิ่งนี้ใน windows และทำงานได้ดีอย่างสมบูรณ์ฉันมีสคริปต์แบตช์ที่เริ่มต้นโปรแกรมของฉันโดยการทำjava my-program.javaและบรรทัดสุดท้ายของไฟล์แบตช์คือif "%ERRORLEVEL%" EQU "1" java my-program.javaถ้าโปรแกรมของฉันออกพร้อมกับSystem.exit(1)มันจะรีสตาร์ทมิฉะนั้นSystem.exit(0)จะทำให้ปิด แม้ว่าฉันจะสามารถทำได้โดยใช้ java บริสุทธิ์มาก่อน (การเพิ่ม hook การปิดระบบ + โดยใช้ processbuilder.inheritIO () เพื่อ stdin / out จะถูกส่งผ่านไปยังกระบวนการใหม่) วิธีนี้ง่ายกว่ามาก
Lorenzo

7

โดยทั่วไป Eclipse จะรีสตาร์ทหลังจากติดตั้งปลั๊กอินแล้ว โดยใช้ wrapper eclipse.exe (แอปตัวเรียกใช้งาน) สำหรับ windows แอ็พพลิเคชันนี้เรียกใช้งาน jar ของรันเนอร์ eclipse หลักและหากแอ็พพลิเคชัน eclipse java สิ้นสุดลงด้วยโค้ดเปิดใช้งานใหม่ eclipse.exe จะรีสตาร์ทเวิร์กเบนช์ คุณสามารถสร้างโค้ดเนทีฟบิตเชลล์สคริปต์หรือ wrapper รหัส java อื่นที่คล้ายกันเพื่อให้รีสตาร์ท


5

Windows

public void restartApp(){

    // This launches a new instance of application dirctly, 
    // remember to add some sleep to the start of the cmd file to make sure current instance is
    // completely terminated, otherwise 2 instances of the application can overlap causing strange
    // things:)

    new ProcessBuilder("cmd","/c start /min c:/path/to/script/that/launches/my/application.cmd ^& exit").start();
    System.exit(0);
}

/ นาทีเพื่อเริ่มสคริปต์ในหน้าต่างย่อเล็กสุด

^ &ออกเพื่อปิดหน้าต่าง cmd หลังจากเสร็จสิ้น

ตัวอย่างสคริปต์ cmd อาจเป็น

@echo off
rem add some sleep (e.g. 10 seconds) to allow the preceding application instance to release any open resources (like ports) and exit gracefully, otherwise the new instance could fail to start
sleep 10   
set path=C:\someFolder\application_lib\libs;%path%
java -jar application.jar

นอน 10หลับ 10 วินาที



4

แม้ว่าคำถามนี้จะเก่าและมีคำตอบ แต่ฉันพบปัญหาเกี่ยวกับวิธีแก้ปัญหาบางอย่างและตัดสินใจเพิ่มข้อเสนอแนะของฉันลงในส่วนผสม

ปัญหาของโซลูชันบางอย่างคือพวกเขาสร้างสตริงคำสั่งเดียว นี้จะสร้างปัญหาเมื่อพารามิเตอร์บางมีช่องว่างโดยเฉพาะอย่างยิ่งjava.home

ตัวอย่างเช่นบน windows บรรทัด

final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";

อาจส่งคืนสิ่งนี้:C:\Program Files\Java\jre7\bin\java

Program Filesสายนี้จะต้องมีการห่อในเครื่องหมายคำพูดหรือหนีเนื่องจากพื้นที่ใน ไม่ใช่ปัญหาใหญ่ แต่ค่อนข้างน่ารำคาญและเกิดข้อผิดพลาดโดยเฉพาะในแอปพลิเคชันข้ามแพลตฟอร์ม

ดังนั้นโซลูชันของฉันจึงสร้างคำสั่งเป็นอาร์เรย์ของคำสั่ง:

public static void restart(String[] args) {

        ArrayList<String> commands = new ArrayList<String>(4 + jvmArgs.size() + args.length);
        List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();

        // Java
        commands.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");

        // Jvm arguments
        for (String jvmArg : jvmArgs) {
            commands.add(jvmArg);
        }

        // Classpath
        commands.add("-cp");
        commands.add(ManagementFactory.getRuntimeMXBean().getClassPath());

        // Class to be executed
        commands.add(BGAgent.class.getName());

        // Command line arguments
        for (String arg : args) {
            commands.add(arg);
        }

        File workingDir = null; // Null working dir means that the child uses the same working directory

        String[] env = null; // Null env means that the child uses the same environment

        String[] commandArray = new String[commands.size()];
        commandArray = commands.toArray(commandArray);

        try {
            Runtime.getRuntime().exec(commandArray, env, workingDir);
            System.exit(0);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

4

เพียงแค่เพิ่มข้อมูลที่ไม่มีอยู่ในคำตอบอื่น ๆ

หากprocfs /proc/self/cmdlineใช้ได้

หากคุณกำลังรันในสภาพแวดล้อมที่จัดเตรียมprocfsดังนั้นจึงมี/procระบบไฟล์ที่พร้อมใช้งาน (ซึ่งหมายความว่านี่ไม่ใช่โซลูชันแบบพกพา) คุณสามารถให้ Java อ่าน/proc/self/cmdlineเพื่อรีสตาร์ทตัวเองได้ดังนี้:

public static void restart() throws IOException {
    new ProcessBuilder(getMyOwnCmdLine()).inheritIO().start();
}
public static String[] getMyOwnCmdLine() throws IOException {
    return readFirstLine("/proc/self/cmdline").split("\u0000");
}
public static String readFirstLine(final String filename) throws IOException {
    try (final BufferedReader in = new BufferedReader(new FileReader(filename))) {
        return in.readLine();
    }
}

บนระบบที่พร้อม/proc/self/cmdlineใช้งานนี่อาจเป็นวิธีที่ดีที่สุดในการ "รีสตาร์ท" กระบวนการ Java ปัจจุบันจาก Java ไม่มี JNI เกี่ยวข้องและไม่ต้องคาดเดาเส้นทางและสิ่งต่างๆ นอกจากนี้ยังดูแลตัวเลือก JVM ทั้งหมดที่ส่งไปยังjavaไบนารี บรรทัดคำสั่งจะเหมือนกับกระบวนการ JVM ปัจจุบันทุกประการ

ระบบ UNIX จำนวนมากรวมถึง GNU / Linux (รวมถึง Android) ในปัจจุบันมีprocfsอย่างไรก็ตามในบางระบบเช่น FreeBSD จะเลิกใช้งานและกำลังจะยุติลง Mac OS Xเป็นข้อยกเว้นในแง่ที่ว่ามันไม่ได้มี procfs ของ Windowsยังไม่ได้มีprocfs Cygwin มีprocfsแต่ไม่สามารถมองเห็นได้ใน Java เนื่องจากสามารถมองเห็นได้เฉพาะแอปพลิเคชันที่ใช้ Cygwin DLL แทนการเรียกระบบ Windows และ Java ไม่รู้จัก Cygwin

อย่าลืมนำไปใช้ ProcessBuilder.inheritIO()

ค่าดีฟอลต์คือstdin/ stdout/ stderr(ใน Java เรียกว่าSystem.in/ System.out/ System.err) ของกระบวนการเริ่มต้นถูกตั้งค่าเป็นไปป์ซึ่งอนุญาตให้กระบวนการที่กำลังทำงานอยู่ในปัจจุบันสื่อสารกับกระบวนการที่เพิ่งเริ่มต้นใหม่ หากคุณต้องการที่จะเริ่มต้นกระบวนการในปัจจุบันนี้เป็นส่วนใหญ่มีแนวโน้มไม่ได้สิ่งที่คุณต้องการ แต่คุณต้องการให้stdin/ stdout/ stderrเหมือนกับ VM ปัจจุบัน นี้เรียกว่าได้รับมรดก คุณสามารถทำได้โดยเรียกอินสแตนซ์inheritIO()ของคุณProcessBuilder

หลุมพรางบน Windows

กรณีการใช้งานบ่อยครั้งของrestart()ฟังก์ชันคือการรีสตาร์ทแอปพลิเคชันหลังจากการอัปเดต ครั้งสุดท้ายที่ฉันลองใช้ Windows สิ่งนี้มีปัญหา เมื่อเขียนทับ.jarไฟล์ของแอปพลิเคชันด้วยเวอร์ชันใหม่แอปพลิเคชันจะเริ่มทำงานผิดปกติและให้ข้อยกเว้นเกี่ยวกับ.jarไฟล์ ฉันแค่บอกในกรณีนี้เป็นกรณีการใช้งานของคุณ ย้อนกลับไปตอนนั้นฉันแก้ไขปัญหาโดยการตัดแอปพลิเคชันในไฟล์แบตช์และใช้ค่าส่งคืนวิเศษจากSystem.exit()ที่ฉันสอบถามในไฟล์แบตช์และให้ไฟล์แบตช์รีสตาร์ทแอปพลิเคชันแทน


1
วิธีแก้ปัญหาของฉันบน windows สำหรับข้อผิดพลาดคือการคัดลอก.jarไฟล์ที่สร้างขึ้นใหม่จากไดเร็กทอรี build build\libs\my.jarไปยังไดเร็กทอรีการทำงานปัจจุบันแล้วเรียกใช้สิ่งนั้นแทน สิ่งนี้หลีกเลี่ยงไม่ให้ฉันเขียนทับ jar ที่กำลังทำงานอยู่ (เนื่องจากฉันรวบรวม jar ของฉันใหม่โดยอัตโนมัติระหว่างการรีสตาร์ท) และวิธีนี้ยังช่วยให้มี jar ที่รันได้เสมอในกรณีที่การอัพเดต / คอมไพล์ล้มเหลว โดยที่การรีสตาร์ทจะเรียกใช้ jar ที่คอมไพล์ไว้ก่อนหน้านี้
Lorenzo

3

ฉันกำลังค้นคว้าเรื่องนี้ด้วยตัวเองเมื่อเจอคำถามนี้

แม้ว่าคำตอบจะได้รับการยอมรับแล้วก็ตามฉันก็ยังคงต้องการเสนอแนวทางอื่นเพื่อความสมบูรณ์ โดยเฉพาะ Apache Ant เป็นโซลูชันที่ยืดหยุ่นมาก

โดยพื้นฐานแล้วทุกอย่างจะเข้าสู่ไฟล์ Ant script ด้วยภารกิจการเรียกใช้ Java เดียว (ดูที่นี่และที่นี่ ) เรียกใช้จากโค้ด Java (ดูที่นี่ ) รหัสนี้ Java ซึ่งอาจเป็นวิธีที่เปิดตัวอาจจะเป็นส่วนหนึ่งของโปรแกรมที่จะต้องมีการเริ่มต้นใหม่ แอปพลิเคชันจำเป็นต้องมีการพึ่งพาไลบรารี Apache Ant (jar)

เมื่อใดก็ตามที่แอพลิเคชันจะต้องมีการเริ่มต้นใหม่ก็ควรเรียกวิธีการเปิดตัวและออกจาก VM งาน Ant java ควรมีอ็อพชันforkและspawnตั้งค่าเป็น true

นี่คือตัวอย่างของ Ant script:

<project name="applaucher" default="launch" basedir=".">
<target name="launch">
    <java classname="package.MasinClass" fork="true" spawn="true">
        <jvmarg value="-splash:splash.jpg"/>
        <jvmarg value="-D other VM params"/>
        <classpath>
            <pathelement location="lib-1.jar" />
            ...
            <pathelement location="lib-n.jar" />
        </classpath>
    </java>
</target>
</project>

รหัสสำหรับวิธีการเปิดตัวอาจมีลักษณะดังนี้:

public final void launch(final String antScriptFile) {
 /* configure Ant and execute the task */
   final File buildFile = new File(antScriptFile);
   final Project p = new Project();
   p.setUserProperty("ant.file", buildFile.getAbsolutePath());

   final DefaultLogger consoleLogger = new DefaultLogger();
   consoleLogger.setErrorPrintStream(System.err);
   consoleLogger.setOutputPrintStream(System.out);
   consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
   p.addBuildListener(consoleLogger);

   try {
       p.fireBuildStarted();
       p.init();
       final ProjectHelper helper = ProjectHelper.getProjectHelper();
       p.addReference("ant.projectHelper", helper);
       helper.parse(p, buildFile);
       p.executeTarget(p.getDefaultTarget());
       p.fireBuildFinished(null);
   } catch (final BuildException e) {
       p.fireBuildFinished(e);
   }

   /* exit the current VM */
   System.exit(0);

}

สิ่งที่สะดวกมากที่นี่คือสคริปต์เดียวกันนี้ใช้สำหรับการเริ่มต้นแอปพลิเคชันครั้งแรกและการรีสตาร์ท


3

คำถามเก่าและทั้งหมดนั้น แต่นี่เป็นอีกวิธีหนึ่งที่มีข้อดีบางประการ

ใน Windows คุณสามารถขอให้ตัวกำหนดตารางเวลางานเริ่มแอปให้คุณอีกครั้ง สิ่งนี้มีข้อดีคือการรอระยะเวลาหนึ่งก่อนที่แอปจะรีสตาร์ท คุณสามารถไปที่ตัวจัดการงานและลบงานและหยุดทำซ้ำ

SimpleDateFormat hhmm = new SimpleDateFormat("kk:mm");    
Calendar aCal = Calendar.getInstance(); 
aCal.add(Calendar.SECOND, 65);
String nextMinute = hhmm.format(aCal.getTime()); //Task Scheduler Doesn't accept seconds and won't do current minute.
String[] create = {"c:\\windows\\system32\\schtasks.exe", "/CREATE", "/F", "/TN", "RestartMyProg", "/SC", "ONCE", "/ST", nextMinute, "/TR", "java -jar c:\\my\\dev\\RestartTest.jar"};  
Process proc = Runtime.getRuntime().exec(create, null, null);
System.out.println("Exit Now");
try {Thread.sleep(1000);} catch (Exception e){} // just so you can see it better
System.exit(0);

2

คล้ายกับคำตอบที่ ' ปรับปรุงแล้ว ' ของ Yoda แต่มีการปรับปรุงเพิ่มเติม (ทั้งฟังก์ชันการทำงานการอ่านและการทดสอบ) ขณะนี้สามารถเรียกใช้งานได้อย่างปลอดภัยและรีสตาร์ทได้หลายครั้งตามจำนวนอาร์กิวเมนต์ของโปรแกรมที่กำหนด

  • ไม่มีการสะสมJAVA_TOOL_OPTIONSตัวเลือก
  • ค้นหาคลาสหลักโดยอัตโนมัติ
  • สืบทอด stdout / stderr ปัจจุบัน

public static void main(String[] args) throws Exception {
    if (args.length == 0)
        return;
    else
        args = Arrays.copyOf(args, args.length - 1);

    List<String> command = new ArrayList<>(32);
    appendJavaExecutable(command);
    appendVMArgs(command);
    appendClassPath(command);
    appendEntryPoint(command);
    appendArgs(command, args);

    System.out.println(command);
    try {
        new ProcessBuilder(command).inheritIO().start();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

private static void appendJavaExecutable(List<String> cmd) {
    cmd.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
}

private static void appendVMArgs(Collection<String> cmd) {
    Collection<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();

    String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS");
    if (javaToolOptions != null) {
        Collection<String> javaToolOptionsList = Arrays.asList(javaToolOptions.split(" "));
        vmArguments = new ArrayList<>(vmArguments);
        vmArguments.removeAll(javaToolOptionsList);
    }

    cmd.addAll(vmArguments);
}

private static void appendClassPath(List<String> cmd) {
    cmd.add("-cp");
    cmd.add(ManagementFactory.getRuntimeMXBean().getClassPath());
}

    private static void appendEntryPoint(List<String> cmd) {
    StackTraceElement[] stackTrace          = new Throwable().getStackTrace();
    StackTraceElement   stackTraceElement   = stackTrace[stackTrace.length - 1];
    String              fullyQualifiedClass = stackTraceElement.getClassName();
    String              entryMethod         = stackTraceElement.getMethodName();
    if (!entryMethod.equals("main"))
        throw new AssertionError("Entry point is not a 'main()': " + fullyQualifiedClass + '.' + entryMethod);

    cmd.add(fullyQualifiedClass);
}

private static void appendArgs(List<String> cmd, String[] args) {
    cmd.addAll(Arrays.asList(args));
}

V1.1 Bugfix: null pointer หากไม่ได้ตั้งค่า JAVA_TOOL_OPTIONS


ตัวอย่าง:

$ java -cp Temp.jar Temp a b c d e
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c, d]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp]
$

-13
System.err.println("Someone is Restarting me...");
setVisible(false);
try {
    Thread.sleep(600);
} catch (InterruptedException e1) {
    e1.printStackTrace();
}
setVisible(true);

ฉันเดาว่าคุณไม่ต้องการหยุดแอปพลิเคชัน แต่ต้องการ "รีสตาร์ท" คุณสามารถใช้สิ่งนี้และเพิ่ม "รีเซ็ต" ของคุณก่อนเข้าสู่โหมดสลีปและหลังหน้าต่างที่มองไม่เห็น


4
ผู้ใช้ขอให้รีสตาร์ทแอปพลิเคชันไม่ใช่แค่ซ่อนและแสดงหน้าต่าง
Amr Lotfy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.