一.前言
SpringBoot項目的啟動, 都需要如下的啟動類
@SpringBootApplication
public class SpringBootDemo3Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemo3Application.class, args);
}
}
分析啟動類, 可以看出核心是:
- 注解@SpringBootApplication
- 方法SpringApplication.run(SpringBootDemo3Application.class, args)
分析SpringBoot的啟動原理, 就需要從以上兩處開始
二.注解@SpringBootApplication
點開注解, 源碼如下
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
}
@SpringBootApplication是由三個注解組合而成( 原生JDK注解忽略 ), 分別是:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
即以上三個注解可以替代@SpringBootApplication
2.1 注解@SpringBootConfiguration
首先查看該注解源碼
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
該注解包含:
- @configuration : 該注解是Spring原生注解, 標注該類是Spring配置類, 可以在該類中向IOC容器添加Bean
- @Indexed : 標識項目在編譯打包的時候會在項目中自動生成
META-INT/spring.components
文件, 當SpringContext在執行ComponentScan
掃描時,META-INT/spring.components
將會被CandidateComponentsIndexLoader
讀取並加載,轉換為CandidateComponentsIndex
對象,這樣的話@ComponentScan
不在掃描指定的package,而是讀取CandidateComponentsIndex
對象,從而達到提升性能的目的 - proxyBeanMethods() default true : 外部無論對配置類中的這個組件注冊方法調用多少次獲取的都是之前注冊容器中的單實例對象
2.2 注解@ComponentScan
作用 : @ComponentScan的功能其實就是自動掃描並加載符合條件的組件(比如@Component和@Repository等)或者bean定義,最終將這些bean定義加載到IoC容器中
2.3 注解@EnableAutoConfiguration
作用 : 該注解是SpringBoot的核心注解, 借助@Import的幫助,將所有符合自動配置條件的bean定義加載到IoC容器
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
該注解包含
- @AutoConfigurationPackage
- @Import({AutoConfigurationImportSelector.class})
2.3.1 注解@AutoConfigurationPackage
查看源碼, 該注解中包含@Import(AutoConfigurationPackages.Register.class) , 通過@Import注解將Register導入到ioc容器中, 然后通過Register向IOC容器中導入一系列組件
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
/**
* Base packages that should be registered with {@link AutoConfigurationPackages}.
* <p>
* Use {@link #basePackageClasses} for a type-safe alternative to String-based package
* names.
* @return the back package names
* @since 2.3.0
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages} for specifying the packages to be
* registered with {@link AutoConfigurationPackages}.
* <p>
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return the base package classes
* @since 2.3.0
*/
Class<?>[] basePackageClasses() default {};
}
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
- AnnotationMetadata metadata : 啟動類
- new packageImports(metadata).getPackageNames : 獲得啟動類的包名
- BeanDefinitionRegistry registry
- register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0])); : 將啟動類的包( 包含子包 )所有標注了@Component相關注解的組件導入到IOC容器
2.3.2 @Import({AutoConfigurationImportSelector.class})
將AutoConfigurationImportSelector
- 利用getAutoConfigurationEntry(annotationMetadata);給容器中批量導入一些組件
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
- 調用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)獲取到所有需要導入到容器中的配置類
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
- 利用工廠加載 Map<String, List<String>> loadSpringFactories( ClassLoader classLoader);得到所有的組件
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
- 從META-INF/spring.factories位置來加載一個文件。 默認掃描我們當前系統里面所有META-INF/spring.factories位置的文件 spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
2.4 注解小結
三.方法SpringApplication.run(SpringBootDemo3Application.class, args)
點進run方法, 源碼如下
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
//繼續點進下一層run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
- 首先new了一個SpringApplication對象
- 然后調用SpringApplication對象的run方法
3.1 實例化SpringApplication對象
調用SpringApplication的1個參數的構造方法, 將啟動類信息傳入
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
繼續調用2個參數的構造方法, 傳入2個參數
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
//斷言有主配置類, 否則拋出異常
Assert.notNull(primarySources, "PrimarySources must not be null");
//保存主配置類信息
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//根據加載的處理器確定web應用的類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//獲取所有的初始化器, 從spring.factories尋找
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//獲取所有的應用監聽器, 從spring.factories尋找
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
- 當前web應用類型
- 所有的初始化器
- 所有的應用監聽器
在項目依賴的spring.factories文件中可以找到以上初始化器和監聽器
#路徑springframework\boot\spring-boot\2.3.4.RELEASE\spring-boot-2.3.4.RELEASE.jar!\META-INF\spring.factories
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.context.properties.NotConstructorBoundInjectionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
3.2 調用SpringApplication對象的run方法
將main方法中的args參數傳入, 分析源碼
public ConfigurableApplicationContext run(String... args) {
//實例化1個程序停止運行監聽器
StopWatch stopWatch = new StopWatch();
//記錄啟動時間
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//讓當前應用進入headless模式(自力更生模式)。java.awt.headless
configureHeadlessProperty();
//獲取所有的運行監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
//遍歷 SpringApplicationRunListener 調用 starting 方法;相當於通知所有感興趣系統正在啟動過程的人,項目正在 starting
listeners.starting();
try {
//保存命令行參數(args)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准備環境, 返回或者創建基礎環境信息對象 : StandardServletEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//根據環境配置需要忽略的bean信息
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//創建IOC容器(createApplicationContext())
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准備IOC容器的基本信息
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新IOC容器。refreshContext ---> 創建容器中的所有組件(Spring注解)
refreshContext(context);
//容器刷新完成后 ---> 目前無內容, 預留
afterRefresh(context, applicationArguments);
//停止運行監聽器停止運行, 記錄容器啟動完成花費的時間
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//所有監聽器調用 listeners.started(context); 通知所有的監聽器容器已創建完成
listeners.started(context);
//調用所有runners, 遍歷執行runner的run方法
callRunners(context, applicationArguments);
}catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//通知所有的監聽器, 項目進入運行狀態
listeners.running(context);
}catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
- 獲取所有RunListener (運行監聽器) [為 了方便所有Listener進行事件感知] ---> getSpringFactoriesInstances 去spring.factories找SpringApplicationRunListener
https://www.yuque.com/atguigu/springboot/tmvr0e