В дополнение к обычным событиям Spring Framework, таким как ContextRefreshedEvent, SpringApplication посылает некоторые дополнительные события приложения.

Некоторые события фактически запускаются до создания ApplicationContext, поэтому нельзя регистрировать слушателя этих событий в качестве аннотации @Bean. Вы можете зарегистрировать их с помощью SpringApplication.addListeners(…​) или метода SpringApplicationBuilder.listeners(…​).

Если нужно, чтобы эти слушатели регистрировались автоматически, независимо от способа создания приложения, то можно добавить файл META-INF/spring.factories в проект и сослаться на слушатель(и) с помощью ключа org.springframework.context.ApplicationListener, как показано в следующем примере:

org.springframework.context.ApplicationListener=com.example.project.MyListener

События приложения отправляются в следующем порядке по мере выполнения приложения:

  1. Событие ApplicationStartingEvent отправляется в начале выполнения, но перед началом любой обработки, за исключением регистрации слушателей и инициализаторов.

  2. Событие ApplicationEnvironmentPreparedEvent отправляется, когда Environment, которое будет использоваться в контексте, известно, но перед созданием контекста.

  3. Событие ApplicationContextInitializedEvent отправляется, когда ApplicationContext подготовлен и вызваны ApplicationContextInitializers, но перед загрузкой определений бинов.

  4. Событие ApplicationPreparedEvent отправляется непосредственно перед началом обновления, но после загрузки определений бинов.

  5. Событие ApplicationStartedEvent отправляется после обновления контекста, но перед тем, как будут вызваны все средства выполнения приложения и командной строки.

  6. Сразу после этого отправляется событие AvailabilityChangeEvent с LivenessState.CORRECT, чтобы обозначить, что приложение считается работающим.

  7. Событие ApplicationReadyEvent отправляется после вызова любого средства выполнения приложений и командной строки.

  8. Сразу после этого отправляется событие AvailabilityChangeEvent с ReadinessState.ACCEPTING_TRAFFIC, чтобы обозначить, что приложение готово к обработке запросов.

  9. Событие ApplicationFailedEvent отправляется, если при запуске возникло исключение.

Приведенный выше список включает только события SpringApplicationEvent, которые привязаны к SpringApplication. В дополнение к ним следующие события также публикуются после ApplicationPreparedEvent и перед ApplicationStartedEvent:

  • Событие WebServerInitializedEvent отправляется после готовности WebServer. ServletWebServerInitializedEvent и ReactiveWebServerInitializedEvent – это варианты сервлета и реактивного сервера соответственно.

  • Событие ContextRefreshedEvent отправляется, если обновляется ApplicationContext.

Зачастую нет нужды использовать события приложения, но знать об их существовании может быть полезно. На внутреннем уровне Spring Boot события используются для выполнения различных задач.
Слушатели событий не должны выполнять потенциально длительные задачи, поскольку по умолчанию они выполняются в одном потоке. Рассмотрите возможность использования средств выполнения приложения и командной строки вместо этого.

События приложения отправляются с помощью механизма публикации событий Spring Framework. Часть этого механизма обеспечивает, что событие, опубликованное для слушателей в дочернем контексте, также будет опубликовано для слушателей в любых контекстах-предках. Следовательно, если ваше приложение использует иерархию экземпляров SpringApplication, слушатель может получить несколько экземпляров одного и того же типа события приложения.

Чтобы слушатель мог отличить событие для своего контекста от события для дочернего контекста, он должен запросить внедрение своего контекста приложения, а затем сравнить внедренный контекст с контекстом события. Контекст может быть внедрен путем реализации ApplicationContextAware или, если слушатель является бином, с помощью аннотации @Autowired.

Веб-окружение

SpringApplication пытается создать нужный тип ApplicationContext по вашему указанию. Для определения WebApplicationType используется следующий алгоритм:

  • Если присутствует Spring MVC, используется AnnotationConfigServletWebServerApplicationContext

  • Если Spring MVC отсутствует, а Spring WebFlux присутствует, используется AnnotationConfigReactiveWebServerApplicationContext

  • В противном случае используется AnnotationConfigApplicationContext

Это означает, что если вы используете Spring MVC и новый WebClient из Spring WebFlux в одном приложении, Spring MVC будет использоваться по умолчанию. Вы можете легко переопределить это, вызвав setWebApplicationType(WebApplicationType).

Также можно полностью управлять используемым типом ApplicationContext, вызывая setApplicationContextClass(…​).

Зачастую желательно вызывать setWebApplicationType(WebApplicationType.NONE) при использовании SpringApplication в тесте JUnit.

Доступ к аргументам приложения

Если необходимо получить доступ к аргументам приложения, которые были переданы в SpringApplication.run(…​), можно внедрить бин org.springframework.boot.ApplicationArguments. Интерфейс ApplicationArguments предоставляет доступ как к сырым аргументам String[], так и к парсированным аргументам option и non-option, как показано в следующем примере:

Java
import java.util.List;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
    public MyBean(ApplicationArguments args) {
        boolean debug = args.containsOption("debug");
        List<String> files = args.getNonOptionArgs();
        if (debug) {
            System.out.println(files);
        }
        // если запустить с параметром "--debug logfile.txt", выводится ["logfile.txt"].
    }
}
Kotlin
import org.springframework.boot.ApplicationArguments
import org.springframework.stereotype.Component
@Component
class MyBean(args: ApplicationArguments) {
    init {
        val debug = args.containsOption("debug")
        val files = args.nonOptionArgs
        if (debug) {
            println(files)
        }
        // если запустить с параметром "--debug logfile.txt", выводится ["logfile.txt"].
    }
}
Spring Boot также регистрирует CommandLinePropertySource в Environment из Spring. Это позволяет помимо прочего внедрять отдельные аргументы приложения с помощью аннотации @Value.

Использование ApplicationRunner или CommandLineRunner

Если необходимо выполнить определенный код после запуска SpringApplication, можно реализовать интерфейсы ApplicationRunner или CommandLineRunner. Оба интерфейса работают одинаково и предусматривают один метод run, который вызывается непосредственно перед тем, как SpringApplication.run(…​) будет завершен.

Этот контракт хорошо подходит для задач, которые должны выполняться после запуска приложения, но перед тем, как оно начнет принимать трафик.

Интерфейс CommandLineRunner предоставляет доступ к аргументам приложения в виде массива строк, в то время как ApplicationRunner использует интерфейс ApplicationArguments, рассмотренный ранее. В следующем примере показан CommandLineRunner с методом run:

Java
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) {
        // делаем что-то...
    }
}
Kotlin
import org.springframework.boot.CommandLineRunner
import org.springframework.stereotype.Component
@Component
class MyCommandLineRunner : CommandLineRunner {
    override fun run(vararg args: String) {
        // делаем что-то...
    }
}

Если определено несколько бинов CommandLineRunner или ApplicationRunner, которые должны вызываться в определенном порядке, можно дополнительно реализовать интерфейс org.springframework.core.Ordered или использовать аннотацию org.springframework.core.annotation.Order.

Выход из приложения

Каждое SpringApplication регистрирует перехватчик завершения в JVM, чтобы обеспечить поэтапное закрытие ApplicationContext при выходе. Можно использовать все стандартные обратные вызовы жизненного цикла Spring (например, интерфейс DisposableBean или аннотацию @PreDestroy).

Кроме того, бины могут реализовать интерфейс org.springframework.boot.ExitCodeGenerator, если им нужно возвращать определенный код выхода при вызове SpringApplication.exit(). Этот код выхода можно передать в System.exit(), чтобы вернуть его в качестве кода состояния, как показано в следующем примере:

Java
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class MyApplication {
    @Bean
    public ExitCodeGenerator exitCodeGenerator() {
        return () -> 42;
    }
    public static void main(String[] args) {
        System.exit(SpringApplication.exit(SpringApplication.run(MyApplication.class, args)));
    }
}
Kotlin
import org.springframework.boot.ExitCodeGenerator
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import kotlin.system.exitProcess
@SpringBootApplication
class MyApplication {
    @Bean
    fun exitCodeGenerator() = ExitCodeGenerator { 42 }
}
fun main(args: Array<String>) {
    exitProcess(SpringApplication.exit(
        runApplication<MyApplication>(*args)))
}

Кроме того, интерфейс ExitCodeGenerator может быть реализован исключениями. При возникновении такого исключения Spring Boot возвращает код выхода, передаваемый реализованным методом getExitCode().

Если существует более чем один ExitCodeGenerator, то используется первый сгенерированный ненулевой код выхода. Чтобы управлять порядком вызова генераторов, дополнительно реализуйте интерфейс org.springframework.core.Ordered или используйте аннотацию org.springframework.core.annotation.Order.

Функции администрирования

Можно включить функции, связанные с администрированием, для приложения, задав свойство spring.application.admin.enabled. Это откроет SpringApplicationAdminMXBean для платформы MBeanServer. Можно использовать эту функцию для удаленного администрирования приложения Spring Boot. Эта функция также может быть полезна для любой реализации службы-обёртки.

Если вы хотите узнать, через какой HTTP-порт работает приложение, получите свойство с ключом local.server.port.

Отслеживание запуска приложений

Во время запуска приложения SpringApplication и ApplicationContext выполняют множество задач, связанных с жизненным циклом приложения, жизненным циклом бинов или даже с обработкой событий приложения. Благодаря ApplicationStartup Spring Framework позволяет отслеживать последовательность запуска приложения с помощью объектов StartupStep. Эти данные можно собирать в целях профилирования или просто для лучшего понимания процесса запуска приложения.

Имеется возможность выбора реализации ApplicationStartup при настройке экземпляра SpringApplication. Например, чтобы использовать BufferingApplicationStartup, можно написать:

Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MyApplication.class);
        application.setApplicationStartup(new BufferingApplicationStartup(2048));
        application.run(args);
    }
}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
    runApplication<MyApplication>(*args) {
        applicationStartup = BufferingApplicationStartup(2048)
    }
}

Первая доступная реализация, FlightRecorderApplicationStartup, предусматривается Spring Framework. Она добавляет связанные со Spring события запуска в сессию Java Flight Recorder и предназначена для профилирования приложений и приведение жизненного цикла Spring-контекста в соответствие с событиями JVM (такими как операции выделения, операции очистки памяти от ненужных данных, загрузка классов...). После конфигурирования можно записывать данные, запустив приложение с активированным Flight Recorder:

$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar

Spring Boot поставляется с вариацией BufferingApplicationStartup; эта реализация предназначена для буферизации этапов запуска и сброса их во внешнюю систему метрик. Приложения могут запрашивать бин типа BufferingApplicationStartup в любом компоненте.

Spring Boot также можно сконфигурировать так, чтобы он открывал конечную точку startup, которая передает эту информацию в виде документа JSON.