1. Обзор
Управление жизненным циклом приложения Spring Boot очень важно для готовой к производству системы. Контейнер Spring обрабатывает создание, инициализацию и уничтожение всех компонентов Bean с помощью ApplicationContext.
Особое внимание в этой статье уделяется фазе разрушения жизненного цикла. В частности, мы рассмотрим различные способы закрытия приложения Spring Boot.
Чтобы узнать больше о том, как настроить проект с помощью Spring Boot, ознакомьтесь со статьей Spring Boot Starter или ознакомьтесь с Spring Boot Configuration .
2. Выключить конечную точку
По умолчанию в Spring Boot Application включены все конечные точки, кроме /shutdown
; это, естественно, часть конечных точек актуатора .
Вот зависимость Maven для их настройки:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
И, если мы хотим также настроить поддержку безопасности, нам нужно:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Наконец, мы включаем конечную точку выключения в файле application.properties :
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
endpoints.shutdown.enabled=true
Обратите внимание, что мы также должны предоставить доступ к любым конечным точкам привода, которые мы хотим использовать. В приведенном выше примере мы предоставили все конечные точки привода, включая конечную точку /shutdown
.
Чтобы закрыть приложение Spring Boot, мы просто вызываем такой метод POST :
curl -X POST localhost:port/actuator/shutdown
В этом вызове порт
представляет собой порт привода.
3. Закрыть контекст приложения
Мы также можем вызвать метод close()
напрямую, используя контекст приложения.
Начнем с примера создания контекста и его закрытия:
ConfigurableApplicationContext ctx = new
SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run();
System.out.println("Spring Boot application started");
ctx.getBean(TerminateBean.class);
ctx.close();
Это уничтожает все bean-компоненты, освобождает блокировки, а затем закрывает bean-фабрику . Чтобы проверить завершение работы приложения, мы используем стандартный обратный вызов жизненного цикла Spring с аннотацией @PreDestroy
:
public class TerminateBean {
@PreDestroy
public void onDestroy() throws Exception {
System.out.println("Spring Container is destroyed!");
}
}
Мы также должны добавить bean-компонент этого типа:
@Configuration
public class ShutdownConfig {
@Bean
public TerminateBean getTerminateBean() {
return new TerminateBean();
}
}
Вот результат после запуска этого примера:
Spring Boot application started
Closing AnnotationConfigApplicationContext@39b43d60
DefaultLifecycleProcessor - Stopping beans in phase 0
Unregistering JMX-exposed beans on shutdown
Spring Container is destroyed!
Здесь важно помнить: при закрытии контекста приложения родительский контекст не затрагивается из-за отдельных жизненных циклов .
3.1. Закрыть текущий контекст приложения
В приведенном выше примере мы создали дочерний контекст приложения, а затем использовали метод close()
для его уничтожения.
Если мы хотим закрыть текущий контекст, одно из решений — просто вызвать конечную точку привода /shutdown
.
Однако мы также можем создать собственную пользовательскую конечную точку:
@RestController
public class ShutdownController implements ApplicationContextAware {
private ApplicationContext context;
@PostMapping("/shutdownContext")
public void shutdownContext() {
((ConfigurableApplicationContext) context).close();
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.context = ctx;
}
}
Здесь мы добавили контроллер, который реализует интерфейс ApplicationContextAware
и переопределяет метод установки для получения текущего контекста приложения. Затем в методе сопоставления мы просто вызываем метод close()
.
Затем мы можем вызвать нашу новую конечную точку, чтобы закрыть текущий контекст:
curl -X POST localhost:port/shutdownContext
Конечно, если вы добавите подобную конечную точку в реальное приложение, вы также захотите защитить ее.
4. Выйдите из SpringApplication
SpringApplication
регистрирует ловушку завершения работы
с JVM, чтобы убедиться, что приложение завершает работу надлежащим образом.
Компоненты могут реализовать интерфейс ExitCodeGenerator
для возврата определенного кода ошибки:
ConfigurableApplicationContext ctx = new SpringApplicationBuilder(Application.class)
.web(WebApplicationType.NONE).run();
int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
@Override
public int getExitCode() {
// return the error code
return 0;
}
});
System.exit(exitCode);
Тот же код с применением лямбд Java 8:
SpringApplication.exit(ctx, () -> 0);
После вызова System.exit(exitCode)
программа завершается с кодом возврата 0 :
Process finished with exit code 0
5. Убить процесс приложения
Наконец, мы также можем закрыть приложение Spring Boot извне приложения с помощью сценария bash. Наш первый шаг для этой опции — заставить контекст приложения записать свой PID в файл:
SpringApplicationBuilder app = new SpringApplicationBuilder(Application.class)
.web(WebApplicationType.NONE);
app.build().addListeners(new ApplicationPidFileWriter("./bin/shutdown.pid"));
app.run();
Затем создайте файл shutdown.bat
со следующим содержимым:
kill $(cat ./bin/shutdown.pid)
Выполнение shutdown.bat
извлекает идентификатор процесса из файла shutdown.pid и использует команду
kill
для завершения приложения загрузки.
6. Заключение
В этой быстрой записи мы рассмотрели несколько простых методов, которые можно использовать для закрытия работающего приложения Spring Boot.
Хотя разработчик должен выбрать подходящий метод; все эти методы должны использоваться по замыслу и по назначению.
Например, .exit()
предпочтительнее, когда нам нужно передать код ошибки в другую среду, скажем, JVM для дальнейших действий. Использование PID
приложения
дает больше гибкости, так как мы также можем запускать или перезапускать приложение с помощью скрипта bash.
Наконец, параметр /shutdown
позволяет завершать работу приложений извне через HTTP . Во всех остальных случаях .close()
будет работать отлично.
Как обычно, полный код для этой статьи доступен в проекте GitHub .