Процесс запуска SpringBoot

Spring Boot

Запись класса запуска SpringBoot

Наиболее интуитивно понятное различие между SpringBoot и Spring заключается в том, что SpringBoot имеет свой собственный независимый класс запуска.

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

Как видно из приведенного выше кода, определение аннотации (@SpringBootApplication) и определение класса (SpringApplication.run()) являются двумя основными точками запуска SpringBoot.

Принцип запуска SpringBoot и общий связанный с ним процесс

Что делает аннотация @SpringBootApplication

Общий процесс аннотации @SpringBootApplication выглядит следующим образом:

Аннотация @SpringBootApplication и основные функции в основном дополняются следующими тремя вложенными аннотациями.

  • @ComponentScan
  • @SpringBootConfiguration
  • @EnableAutoConfiguration

@ComponentScan

@ComponentScan в основном предназначен для определенияпуть для сканированиянашел идентификациютребуется сборкаКлассы автоматически подключаются к bean-контейнеру Spring.

Студенты, которые занимались веб-разработкой, должны были использовать аннотации @Controller, @Service, @Repository, проверить исходный код, и вы обнаружите, что один из нихОбщая аннотация @ComponentДа, аннотация @ComponentScan по умолчанию соберет классы, отмеченные аннотациями @Controller, @Service, @Repository, @Component, в контейнер Spring.

  • Пользовательский путь сканирования имеет аннотации @Controller, @Service, @Repository, @Component, добавленные в контейнер Spring.
  • Добавьте классы без вышеуказанных аннотаций под путь сканирования к контейнеру Spring через includeFilters
  • Отфильтруйте классы, которые не нужно добавлять в контейнер Spring, через excludeFilters.
  • Настройте метод аннотации, который добавляет аннотацию @Component

@SpringBootConfiguration

@SpringBootConfiguration наследуется от @Configuration, функции этих двух также одинаковы, помечая текущий класс как класс конфигурации,
И он будет включать один или несколько экземпляров метода, объявленного в текущем классе, помеченного аннотацией @Bean, в контейнер Spring, а имя экземпляра — это имя метода.

@EnableAutoConfiguration

Краткое описание @EnableAutoConfiguration:Собирайте и регистрируйте определения bean-компонентов для конкретных сценариев с помощью @Import..

  • @EnableScheduling загружает определения bean-компонентов, связанных с инфраструктурой планирования Spring, в контейнер IoC через @Import.
  • @EnableMBeanExport предназначен для загрузки определений bean-компонентов, связанных с JMX, в контейнер IoC через @Import.

И @EnableAutoConfiguration также использует помощь @Import для загрузки всех определений bean-компонентов, соответствующих условиям автоконфигурации, в контейнер IoC, вот и все!

@EnableAutoConfiguration как составная аннотация определяет собственную ключевую информацию следующим образом:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

Среди них наиболее важным является @Import(AutoConfigurationImportSelector.class).С помощью AutoConfigurationImportSelector @EnableAutoConfiguration может помочь приложениям SpringBoot загрузить все подходящие конфигурации @Configuration в контейнер IoC, созданный и используемый SpringBoot.

Закулисная реализация автоматической настройки основана на SpringFactoriesLoader. SpringFactoriesLoader — это частная схема расширения среды Spring. Его основная функция — загрузить конфигурацию из указанного файла конфигурации META-INF/spring.factories.

При использовании с @EnableAutoConfiguration он обеспечивает дополнительную поддержку поиска конфигурации, то есть в соответствии с полным именем класса @EnableAutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration в качестве ключа для поиска, получить соответствующий набор классов @Configuration .

Автоматическая конфигурация @EnableAutoConfiguration становится следующей: поиск всех файлов конфигурации META-INF/spring.factories из пути к классам и создание экземпляров элементов конфигурации, соответствующих org.springframework.boot.autoconfigure.EnableutoConfiguration, посредством отражения (Java Refletion) Настройка классов для соответствующих Контейнер IoC в виде JavaConfig, помеченного @Configuration, а затем объединить их в один и загрузить в контейнер IoC.

Процесс выполнения SpringApplication.run()

Инициализировать SpringApplication

Конструктор SpringApplication сначала инициализирует SpringApplication:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = new HashSet();
    this.isCustomEnvironment = false;
    this.lazyInitialization = false;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

1. Infer WebApplicationType, основная идея заключается в поиске определенного класса в текущем пути к классам.

2. позвонитьgetSpringFactoriesInstancesНайдите класс, ключ которого ApplicationContextInitializer, из файла spring.factories и создайте его экземпляр, затем вызовитеsetInitializersметод установлен наSpringApplicationизinitializersв свойствах.

Найдите класс реализации ApplicationContextInitializer, настроенный в файле META-INF\spring.factories.

3. позвонитьgetSpringFactoriesInstancesНайдите класс, ключом которого является ApplicationListener, из файла spring.factories и создайте его экземпляр, затем вызовитеsetListenersметод установлен наSpringApplicationизlistenersв свойствах. Процесс заключается в поиске всех прослушивателей событий приложения.

Найдите класс реализации ApplicationListener, настроенный в файле META-INF\spring.factories.

4. ВызовdeduceMainApplicationClassМетод находит основной класс, который находится здесьSpringBootDemoApplicationДобрый

private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
        StackTraceElement[] var2 = stackTrace;
        int var3 = stackTrace.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            StackTraceElement stackTraceElement = var2[var4];
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    } catch (ClassNotFoundException var6) {
    }

    return null;
}

Запустить SpringApplication

метод запуска

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

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

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

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

Настройте и подготовьте среду:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
        ConfigurableEnvironment environment = this.getOrCreateEnvironment();
        this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach((Environment)environment);
        listeners.environmentPrepared((ConfigurableEnvironment)environment);
        this.bindToSpringApplication((ConfigurableEnvironment)environment);
        if (!this.isCustomEnvironment) {
            environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
        }

        ConfigurationPropertySources.attach((Environment)environment);
        return (ConfigurableEnvironment)environment;
    }

Создайте контекст контейнера Spring:

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }

Настройте контекст контейнера Spring:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        this.postProcessApplicationContext(context);
        this.applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            this.logStartupInfo(context.getParent() == null);
            this.logStartupProfileInfo(context);
        }

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }

        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }

        if (this.lazyInitialization) {
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }

        Set<Object> sources = this.getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        this.load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

После создания контейнера Spring метод обратного вызова postProcessApplicationContext:

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
        if (this.beanNameGenerator != null) {
            context.getBeanFactory().registerSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator", this.beanNameGenerator);
        }

        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext)context).setResourceLoader(this.resourceLoader);
            }

            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader)context).setClassLoader(this.resourceLoader.getClassLoader());
            }
        }

        if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
        }

    }

После создания контейнера Spring вызывается метод callRunners:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        Iterator var4 = (new LinkedHashSet(runners)).iterator();

        while(var4.hasNext()) {
            Object runner = var4.next();
            if (runner instanceof ApplicationRunner) {
                this.callRunner((ApplicationRunner)runner, args);
            }

            if (runner instanceof CommandLineRunner) {
                this.callRunner((CommandLineRunner)runner, args);
            }
        }

    }

Справочная статья:

woo woo woo.cn blog on.com/the rhyme/afraid/…

blog.Wang Qi.love/articles/SP…