Создание/регистрация ApplicationContext, автоконфигурация .. .

В этой статье мы изучим, что происходит, когда мы нажимаем кнопку "Выполнить" в нашей IDE, чтобы запустить наше приложение Spring Boot. Есть много интересных вещей, которые можно открыть для себя, поэтому приготовьтесь погрузиться во внутренние процессы. работы Spring Boot.

Как мы все знаем, основной метод проекта служит точкой входа, но что на самом деле происходит, когда этот метод выполняется?

Это основной метод, который мы будем обсуждать:

@SpringBootApplication
public class SpringBootIntroApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootIntroApplication.class, args);
    }

}

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

run() в классе SpringApplication.

public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    ConfigurableApplicationContext context = null;
    this.configureHeadlessProperty();
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        this.configureIgnoreBeanInfo(environment);
        Banner printedBanner = this.printBanner(environment);
        context = this.createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
        }

        listeners.started(context, timeTakenToStartup);
        this.callRunners(context, applicationArguments);
    } catch (Throwable var12) {
        this.handleRunFailure(context, var12, listeners);
        throw new IllegalStateException(var12);
    }

    try {
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        listeners.ready(context, timeTakenToReady);
        return context;
    } catch (Throwable var11) {
        this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
        throw new IllegalStateException(var11);
    }
}

Примечание. Следующий фрагмент кода является внутренней реализацией метода run в проекте Spring Boot. Мы будем использовать его в качестве ссылки в этой статье, и не ожидается, что мы поймем его на 100%.

Когда вы вызываете метод run для SpringApplication, он создает WebApplicationContext для вашего веб-приложения. Если вы посмотрите на реализацию метода run, вы увидите, что его возвращаемый тип — ConfigurableApplicationContext. Этот интерфейс реализуется прямо или косвенно всеми контекстами приложения.

Тип создаваемого WebApplicationContext зависит от типа вашего веб-приложения. Например, если ваше приложение представляет собой веб-приложение на основе сервлетов, Spring Boot создает файл AnnotationConfigServletWebServerApplicationContext. С другой стороны, если ваше приложение является реактивным веб-приложением, Spring Boot создает файл AnnotationConfigReactiveWebServerApplicationContext. Таким образом, Spring Boot определяет тип вашего веб-приложения и соответственно создает соответствующий файл WebApplicationContext.

Я понимаю, что вам интересно, как Spring Boot определяет тип веб-приложения. Это отличный вопрос, и я с удовольствием вам его объясню.

Позвольте мне объяснить вам, что Spring Boot внутренне классифицирует веб-приложения на две категории: Реактивные и Сервлетные приложения с использованием перечисления WebApplicationType.

в конструкторе класса SpringApplication вы найдете эту строку:

this.webApplicationType = WebApplicationType.deduceFromClasspath();

spring boot использует метод deduceFromClasspath() для определения типа веб-приложения на основе наличия определенных зависимостей от пути к классам. Если он находит зависимость spring-boot-starter-web, он предполагает, что приложение является веб-приложением на основе сервлета, и устанавливает для атрибута webApplicationType значение SERVLET. Точно так же, если он находит зависимость spring-boot-starter-reactive, он предполагает, что приложение является реактивным веб-приложением, и устанавливает webApplicationType в REACTIVE. Если ни одна из этих зависимостей не будет найдена, webApplicationType будет установлено на NONE.

на основе этого типа приложения spring boot создаст соответствующий контекст приложения, но как?

context = this.createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
 return this.applicationContextFactory.create(this.webApplicationType); 
}

Метод createApplicationContext() в SpringApplication создает соответствующий контекст приложения на основе обнаруженного webApplicationType. Он делегирует задачу создания контекста экземпляру ApplicationContextFactory, которому передается обнаруженный webApplicationType в качестве параметра.

Интерфейс ApplicationContextFactory определяет единственный метод create(WebApplicationType), который возвращает новый ConfigurableApplicationContext на основе заданного WebApplicationType.

чтобы проверить, возвращает ли метод запуска соответствующий webApplicationContext :

public static void main(String[] args) {
    ConfigurableApplicationContext run = SpringApplication.run(SpringBootIntroApplication.class, args);
    System.out.println(run instanceof AnnotationConfigServletWebServerApplicationContext );

}

что произошло после создания контекста?

Метод this.refreshContext(context) в методе run() из SpringApplication запускает обновление контекста приложения. Это означает, что контекст приложения будет инициализирован и настроен со всеми необходимыми bean-компонентами и компонентами, необходимыми для запуска приложения.

В процессе обновления Spring будет сканировать путь к классам приложения на предмет всех компонентов, которые были аннотированы @Component или другими аннотациями, и при необходимости создаст и настроит экземпляры этих компонентов. Он также загрузит и настроит любые другие bean-компоненты, которые определены в контексте приложения, например, определенные в файлах конфигурации XML или через классы конфигурации Java.

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

важно отметить, что метод run делает гораздо больше, мы сосредоточимся только на том, как метод run() создает WebApplicationType .

Спасибо за прочтение !