Programowo zamknij aplikację Spring Boot

110

Jak mogę programowo wyłączenie Wiosna Boot aplikacja bez kończące VM ?

W innych pracach, co jest przeciwieństwem

new SpringApplication(Main.class).run(args);
Axel Fontaine
źródło
1
Słuszna uwaga! Wywołanie funkcji close () powinno załatwić sprawę.
Axel Fontaine
Możliwy duplikat Jak poprawnie zamknąć aplikację Spring Boot?
Anand Varkey Philips
@AnandVarkeyPhilips Nie, zdecydowanie nie jest. Ten dotyczy interfejsu API, a drugi sposobu, w jaki mogą to zrobić operatorzy.
Axel Fontaine
Okej… To pytanie może pomóc innym. Czy chcesz, żebym usunął powyższy komentarz?
Anand Varkey Philips

Odpowiedzi:

111

Zamknięcie SpringApplicationzasadniczo oznacza zamknięcie instrumentu bazowego ApplicationContext. SpringApplication#run(String...)Metoda daje to ApplicationContextjako ConfigurableApplicationContext. Możesz close()to samemu.

Na przykład,

@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();
    }
}

Alternatywnie możesz użyć static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...)metody pomocnika, aby zrobić to za siebie. Na przykład,

@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
źródło
1
Jeśli używam ctx.close (); nie ma potrzeby wywoływania System.exit (n) na końcu, prawda? kontekst close () powinien mieć w środku System.exit ()?
Denys
2
@Denys Nie, kontekst nie kończy procesu Java przy zamknięciu. Wyjście w moim przykładzie pokazuje tylko, jak ExitCodeGeneratormożna użyć. Możesz po prostu powrócić z mainmetody, aby bezpiecznie zakończyć (kod zakończenia 0).
Sotirios Delimanolis
79

W aplikacji do rozruchu wiosennego możesz użyć czegoś takiego

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
źródło
16
Głosuj za pokazaniem, że ApplicationContextmoże być automatycznie wstrzykiwany do innych ziaren.
Anthony Chuinard,
1
@snovelli jak wywołać zainicjować metodę zamykania? zainicjowaćShutdown (x), x = 0?
StackOverFlow
W przypadku warunkowego wyłączenia można to wykonać. SpringApplication.run (..). Close () będzie działać po zakończeniu programu.
Abubacker Siddik
dlaczego SpringApplication. (appContext, () -> returnCode); dlaczego nie można appContext.close (). jaka jest różnica ?
Rams
@StackOverFlow Musisz wstrzyknąć ziarno tam, gdzie go potrzebujesz, a następnie przekazać kod powrotu zgodnie z sugestią (x = 0), jeśli wyłącza się poprawnie. Na przykład możesz wstrzyknąć Shutdown Manager do RestController i zezwolić na zdalne zamknięcie, lub możesz wstrzyknąć go do monitorowania kondycji, które zamknie JVM w przypadku braku usług niższego szczebla
snovelli
36

To działa, nawet gotowe jest drukowane.

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

Więc dodając .close() porun()

Wyjaśnienie:

public ConfigurableApplicationContext run(String... args)

Uruchom aplikację Spring, tworząc i odświeżając nowy ApplicationContext. Parametry:

args - argumenty aplikacji (zwykle przekazywane z głównej metody Java)

Zwraca: działający ApplicationContext

i:

void close()Zamknij ten kontekst aplikacji, zwalniając wszystkie zasoby i blokady, które może zawierać implementacja. Obejmuje to niszczenie wszystkich pojedynczych ziaren w pamięci podręcznej. Uwaga: nie wywołuje zamknięcia w kontekście nadrzędnym; konteksty nadrzędne mają swój własny, niezależny cykl życia.

Tę metodę można wywołać wiele razy bez skutków ubocznych: kolejne bliskie wywołania w już zamkniętym kontekście będą ignorowane.

Zasadniczo więc nie zamknie kontekstu nadrzędnego, dlatego maszyna wirtualna się nie kończy.

ACV
źródło
2
Przypomnijmy, że to rozwiązanie działa w przypadku krótkotrwałego procesu, takiego jak wsad, ale nie używaj go w aplikacjach Spring MVC. Aplikacja po prostu wyłączyła się po uruchomieniu.
Michael COLL
@MichaelCOLL pytanie dotyczy tego, jak programowo zamknąć aplikację rozruchową wiosny, niezależnie od jej typu. Działa również dla Spring MVC
ACV
1
@ACV Masz rację, to działa, działa bardzo dobrze. Ale w przypadku aplikacji, która musi działać (jak aplikacja Spring MVC), myślę, że nie jest to dobry sposób. W moim przypadku użyłem SpringApplication.exit(appContext, () -> returnCode).
Michael COLL
1
Do jakiej maszyny wirtualnej odnosisz się w ostatnim wierszu? Jeśli uruchamiasz aplikację Spring Boot z SpringApplication.run(MyApplication.class, args), nie ma kontekstu nadrzędnego. Jest tylko jeden kontekst, kontekst utworzony i zwrócony przez run, który następnie natychmiast close. @Michael ma rację. To nie zadziała w przypadku programów, które muszą cokolwiek robić po zainicjowaniu kontekstu Spring, czyli w przypadku większości programów.
Zbawiciel
@Savior JVM. Istnieje kontekst nadrzędny. Tutaj mówimy o tym, jak zamknąć aplikację rozruchową Spring. Zwykle w ten sposób nie zamyka się aplikacji internetowych. Dlatego ten mechanizm jest zwykle używany w aplikacjach krótkotrwałych, które coś robią, a następnie muszą się zatrzymać. Domyślnie Spring Boot będzie działał nawet po zakończeniu przetwarzania wsadowego, więc właśnie tam chciałbyś użyć tego mechanizmu.
ACV
3

W aplikacji możesz użyć SpringApplication. Ma exit()metodę statyczną , która przyjmuje dwa argumenty: the ApplicationContexti an ExitCodeGenerator:

czyli możesz zadeklarować tę metodę:

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

Wewnątrz testów integracyjnych możesz to osiągnąć, dodając @DirtiesContextadnotację na poziomie klasy:

  • @DirtiesContext(classMode=ClassMode.AFTER_CLASS) - Powiązany ApplicationContext zostanie oznaczony jako brudny po klasie testowej.
  • @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - Powiązany ApplicationContext zostanie oznaczony jako brudny po każdej metodzie testowej w klasie.

to znaczy

@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
źródło
Dobrze. Gdzie mam pobrać ExecutorServiceExitCodeGenerator? Jeśli jest to fasola, czy możesz pokazać kod fragmentu kreacji (i z jakiej klasy jest on tworzony)? W której klasie należy umieścić metodę shutDown (ExecutorServiceExitCodeGenerator exitCodeGenerator)?
Vlad G.
2

Dzięki temu aplikacja SpringBoot zostanie poprawnie zamknięta, a zasoby zostaną zwrócone do systemu operacyjnego,

@Autowired
private ApplicationContext context;

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

    int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
    System.exit(exitCode);
}
Samim Aftab Ahmed
źródło