Matikan aplikasi Spring Boot secara terprogram

109

Bagaimana saya bisa pemrograman penutupan sebuah musim semi Boot aplikasi tanpa mengakhiri VM ?

Dalam karya lain, kebalikan dari

new SpringApplication(Main.class).run(args);
Axel Fontaine
sumber
1
Poin bagus! Memanggil close () yang seharusnya melakukan pekerjaan itu.
Axel Fontaine
Kemungkinan duplikat dari Bagaimana cara mematikan Aplikasi Spring Boot dengan cara yang benar?
Anand Varkey Philips
@AnandVarkeyPhilips Tidak, jelas tidak. Yang ini tentang API, yang lainnya tentang cara ops untuk melakukannya.
Axel Fontaine
Oke .. Link pertanyaan itu mungkin bisa membantu orang lain. Apakah Anda ingin saya menghapus komentar di atas?
Anand Varkey Philips

Jawaban:

111

Menutup SpringApplicationpada dasarnya berarti menutup yang mendasarinya ApplicationContext. The SpringApplication#run(String...)metode memberikan Anda bahwa ApplicationContextsebagai ConfigurableApplicationContext. Anda kemudian bisa close()melakukannya sendiri.

Sebagai contoh,

@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
        // ...determine it's time to shut down...
        ctx.close();
    }
}

Alternatifnya, Anda dapat menggunakan static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...)metode helper untuk melakukannya untuk Anda. Sebagai contoh,

@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
        // ...determine it's time to stop...
        int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
            @Override
            public int getExitCode() {
                // no errors
                return 0;
            }
        });

        // or shortened to
        // int exitCode = SpringApplication.exit(ctx, () -> 0);

        System.exit(exitCode);
    }
}
Sotirios Delimanolis
sumber
1
Jika saya menggunakan ctx.close (); tidak perlu memanggil System.exit (n) di akhir kan? konteks close () harus memiliki System.exit () di dalamnya?
Denys
2
@ Denys Tidak, konteks tidak keluar dari proses java saat ditutup. Jalan keluar dalam contoh saya hanya menunjukkan bagaimana ExitCodeGeneratorbisa digunakan. Anda bisa kembali dari mainmetode untuk keluar dengan anggun (kode keluar 0).
Sotirios Delimanolis
78

Dalam aplikasi boot musim semi Anda dapat menggunakan sesuatu seperti ini

ShutdownManager.java

import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;

@Component
class ShutdownManager{

    @Autowired
    private ApplicationContext appContext;

    public void initiateShutdown(int returnCode){
        SpringApplication.exit(appContext, () -> returnCode);
    }
}
snovelli
sumber
16
Suara positif untuk menunjukkan bahwa ApplicationContextkaleng disuntikkan secara otomatis ke kacang lain.
Anthony Chuinard
1
@ snovelli bagaimana cara menjalankan metode memulai shutdown? inisiateShutdown (x), x = 0?
StackOverFlow
Jika ada pemadaman bersyarat, ini bisa dijalankan. SpringApplication.run (..). Close () akan dilakukan saat program selesai.
Abubacker Siddik
mengapa SpringApplication. (appContext, () -> returnCode); mengapa tidak bisa appContext.close (). Apa bedanya ?
Rams
@StackOverFlow Anda perlu menyuntikkan bean di tempat yang Anda butuhkan dan kemudian meneruskan kode pengembalian seperti yang Anda sarankan (x = 0) jika dimatikan dengan benar. Misalnya Anda dapat menyuntikkan Manajer Shutdown ke dalam RestController dan mengizinkan pematian jarak jauh, atau Anda dapat menyuntikkannya ke pemantauan pemeriksaan kesehatan yang akan mematikan JVM jika ada layanan hilir yang hilang
snovelli
36

Ini bekerja, bahkan selesai dicetak.

  SpringApplication.run(MyApplication.class, args).close();
  System.out.println("done");

Jadi menambahkan .close()setelahrun()

Penjelasan:

public ConfigurableApplicationContext run(String... args)

Jalankan aplikasi Spring, buat dan refresh ApplicationContext baru. Parameter:

args - argumen aplikasi (biasanya diteruskan dari metode utama Java)

Mengembalikan: ApplicationContext yang sedang berjalan

dan:

void close()Tutup konteks aplikasi ini, lepaskan semua sumber daya dan kunci yang mungkin dimiliki implementasi. Ini termasuk menghancurkan semua kacang tunggal yang di-cache. Catatan: Tidak memanggil dekat pada konteks induk; konteks induk memiliki siklus hidupnya sendiri-sendiri.

Metode ini bisa dipanggil berkali-kali tanpa efek samping: Penutupan panggilan berikutnya pada konteks yang sudah tertutup akan diabaikan.

Jadi pada dasarnya, ini tidak akan menutup konteks induk, itulah mengapa VM tidak berhenti.

ACV
sumber
2
Sekadar pengingat, solusi ini berfungsi untuk proses berumur pendek seperti batch, tetapi jangan gunakan ini pada aplikasi Spring MVC. Aplikasi baru saja mati setelah booting.
Michael COLL
@MichaelCOLL pertanyaannya adalah tentang bagaimana cara mematikan aplikasi boot musim semi secara terprogram apa pun jenisnya. Ini juga bekerja untuk Spring MVC
ACV
1
@ACV Anda benar itu bekerja, bekerja dengan sangat baik. Tetapi untuk aplikasi yang harus tetap aktif (seperti aplikasi Spring MVC), menurut saya, itu bukan cara yang baik untuk melakukan ini. Dalam kasus saya, saya telah menggunakan SpringApplication.exit(appContext, () -> returnCode).
Michael COLL
1
VM apa yang Anda maksud di baris terakhir Anda? Jika Anda memulai aplikasi Spring Boot dengan SpringApplication.run(MyApplication.class, args), maka tidak ada konteks induk. Hanya ada satu konteks, konteks dibuat dan dikembalikan run, yang kemudian Anda segera close. @Michael benar. Ini tidak akan berfungsi untuk program yang perlu melakukan apa pun setelah konteks Spring diinisialisasi, yang merupakan sebagian besar program.
Juruselamat
@Tokopedia Ada konteks induk. Di sini kita berbicara tentang cara mematikan aplikasi boot Spring. Anda biasanya tidak menutup aplikasi web dengan cara ini. Jadi mekanisme ini biasanya digunakan untuk aplikasi berumur pendek yang melakukan sesuatu kemudian harus dihentikan. Secara default, Spring boot akan terus berjalan bahkan setelah Anda menyelesaikan pemrosesan batch sehingga di sanalah Anda ingin menggunakan mekanisme ini.
ACV
3

Dalam aplikasi, Anda dapat menggunakan SpringApplication. Ini memiliki exit()metode statis yang mengambil dua argumen: the ApplicationContextdan an ExitCodeGenerator:

yaitu Anda dapat mendeklarasikan metode ini:

@Autowired
public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) {
    SpringApplication.exit(applicationContext, exitCodeGenerator);
}

Di dalam tes Integrasi Anda dapat melakukannya dengan menambahkan @DirtiesContextanotasi di tingkat kelas:

  • @DirtiesContext(classMode=ClassMode.AFTER_CLASS) - ApplicationContext terkait akan ditandai sebagai kotor setelah kelas pengujian.
  • @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - ApplicationContext terkait akan ditandai sebagai kotor setelah setiap metode pengujian di kelas.

yaitu

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {Application.class},
    webEnvironment= SpringBootTest.WebEnvironment.DEFINED_PORT, properties = {"server.port:0"})
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ApplicationIT {
...
magiccrafter
sumber
Baik. Di mana saya bisa mendapatkan ExecutorServiceExitCodeGenerator? Jika ini adalah kacang, dapatkah Anda menunjukkan kode cuplikan pembuatan (dan dari kelas mana ia dibuat)? Di kelas manakah metode shutDown (ExecutorServiceExitCodeGenerator exitCodeGenerator) harus diletakkan?
Vlad G.
2

Ini akan memastikan bahwa aplikasi SpringBoot ditutup dengan benar dan sumber daya dilepaskan kembali ke sistem operasi,

@Autowired
private ApplicationContext context;

@GetMapping("/shutdown-app")
public void shutdownApp() {

    int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
    System.exit(exitCode);
}
Samim Aftab Ahmed
sumber