Запись класса запуска 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);
}
}
}
Справочная статья: