SpringBoot源碼學(xué)習(xí)筆記之啟動(dòng)流程分析
1概述
本文基于Spring Boot版本2.1.4(SringBoot最新版本2.4.2奶赔。(參考文檔: https://docs.spring.io/spring-boot/docs/current/api/)
2SpringBoot啟動(dòng)流程分析
2.1分析入口
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
// spring-boot-2.1.4.RELEASE-sources.jar!\org\springframework\boot\SpringApplication.java
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//1.選擇ApplicationContentType容器類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//2.初始化,得到 ApplicationContextInitializer.class的springFactories
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//3.設(shè)置監(jiān)聽器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//4.設(shè)置主類(有main的那個(gè)類)
this.mainApplicationClass = deduceMainApplicationClass();
}
2.2容器選擇deduceFromClasspath
/**
* An enumeration of possible types of web application.
*
* @author Andy Wilkinson
* @author Brian Clozel
* @since 2.0.0
*/
public enum WebApplicationType {
/**
* The application should not run as a web application and should not start an
* embedded web server.
*/
NONE,
/**
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
*/
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.
*/
REACTIVE;
2.2setInitializers(ApplicationContextInitializer)
0 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
1 = "org.springframework.boot.context.ContextIdApplicationContextInitializer"
2 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
3 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
4 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
5 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"
2.3setListeners(ApplicationListener)
0 = "org.springframework.boot.ClearCachesApplicationListener"
1 = "org.springframework.boot.builder.ParentContextCloserApplicationListener"
2 = "org.springframework.boot.context.FileEncodingApplicationListener"
3 = "org.springframework.boot.context.config.AnsiOutputApplicationListener"
4 = "org.springframework.boot.context.config.ConfigFileApplicationListener"
5 = "org.springframework.boot.context.config.DelegatingApplicationListener"
6 = "org.springframework.boot.context.logging.ClasspathLoggingApplicationListener"
7 = "org.springframework.boot.context.logging.LoggingApplicationListener"
8 = "org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"
9 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"
2.5run
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 1.獲取 spring:SpringApplicationRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 2.這里涉及容器的創(chuàng)建象浑、刷新等,類似于Spring
// org.springframework.boot.web.servlet.context.
//AnnotationConfigServletWebServerApplicationContext@25ce9dc4, started on Thu Jan 01 08:00:00 CST 1970
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
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;
}
3源碼細(xì)節(jié)
3.1getSpringFactoriesInstances
// spring-boot-2.1.4.RELEASE-sources.jar!\org\springframework\boot\SpringApplication.java
//根據(jù) type和 classLoader得到springFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 1.根據(jù) ApplicationContextInitializer
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 2.springFactories實(shí)例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// 3.排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
// spring-core-5.1.6.RELEASE-sources.jar!\org\springframework\core\io\support\SpringFactoriesLoader.java
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
// 1.得到 org.springframework.context.ApplicationContextInitializer:ConfigurableApplicationContext
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
// 0 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
// 1 = "org.springframework.boot.context.ContextIdApplicationContextInitializer"
// 2 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
// 3 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
// 4 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
// 5 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"
}
// spring-core-5.1.6.RELEASE-sources.jar!\org\springframework\core\io\support\SpringFactoriesLoader.java
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// todo,cache數(shù)據(jù)來(lái)源
// 啟動(dòng)的時(shí)候會(huì)掃描所有jar包下META-INF/spring.factories這個(gè)文件痊夭。第二段代碼的意思是將這些掃描到的文件轉(zhuǎn)成Properties
// 對(duì)象吞鸭,后面兩個(gè)核心代碼的意思就是說(shuō)將加載到的Properties對(duì)象放入到緩存中
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 加載路徑
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
3.2啟動(dòng)類上的注解
/**
* Indicates a {@link Configuration configuration} class that declares one or more
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
* annotation that is equivalent to declaring {@code @Configuration},
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 1.2.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//1.@SpringBootConfiguration這個(gè)注解說(shuō)明再點(diǎn)進(jìn)去查看詳情發(fā)現(xiàn)就是一個(gè)@Configuration注解扩所,這說(shuō)明啟動(dòng)類就是一個(gè)配置類。支持Spring以JavaConfig的形式啟動(dòng)焰盗。
@SpringBootConfiguration
//3.springboot的核心注解
@EnableAutoConfiguration
//2.組件掃描的意思,即默認(rèn)掃描當(dāng)前package以及其子包下面的spring的注解咒林,例如:@Controller熬拒、@Service、@Component等等注解垫竞。
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
/**
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
* for a type-safe alternative to String-based package names.
* @return base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
* <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 base packages to scan
* @since 1.3.0
*/
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
3.2.1SpringBootConfiguration
3.2.2ComponentScan
3.2.3EnableAutoConfiguration
該注解是springboot的核心注解,本質(zhì)都是利用了spring framework的 import功能澎粟。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//1.
@AutoConfigurationPackage
//2.
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
3.2.3.1AutoConfigurationPackage
自動(dòng)配置包,最終依賴的確實(shí)@Import這個(gè)注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 后置處理器,將BeanDef注入到ioc容器
// 導(dǎo)入類型為:Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
3.2.3.2AutoConfigurationImportSelector
自動(dòng)引入組件
/**
* {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
* auto-configuration}. This class can also be subclassed if a custom variant of
* {@link EnableAutoConfiguration @EnableAutoConfiguration} is needed.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Madhura Bhave
* @since 1.3.0
* @see EnableAutoConfiguration
*/
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
private static final String[] NO_IMPORTS = {};
private static final Log logger = LogFactory
.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
final class AutoConfigurationMetadataLoader {
protected static final String PATH = "META-INF/"
+ "spring-autoconfigure-metadata.properties";
private AutoConfigurationMetadataLoader() {
}
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
// spring-boot-autoconfigure-2.1.4.RELEASE-sources.jar!/
// org/springframework/boot/autoconfigure/AutoConfigurationImportSelector.java
// 執(zhí)行時(shí)機(jī):refresh(AbstractApplicationContext.java):invokeBeanFactoryPostProcessors(beanFactory);
/*
spring-boot-autoconfigure\2.1.4.RELEASE\spring-boot-autoconfigure-2.1.4.RELEASE.jar!\
META-INF\spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
*/
/**
* Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
* of the importing {@link Configuration @Configuration} class.
* @param autoConfigurationMetadata the auto-configuration metadata
* @param annotationMetadata the annotation metadata of the configuration class
* @return the auto-configurations that should be imported
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 這就是一種自定義SPI的實(shí)現(xiàn)方式的功能
// 獲取所有jar中類型為:AutoConfiguration.class的子類,例如:
// 0 = "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration"
// 1 = "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration"
// 2 = "org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration"
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
// 加載路徑:META-INF/spring.factories
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;
}
@Configuration
@AutoConfigureAfter({JmxAutoConfiguration.class})
// 條件注解,加載SpringApplicationAdminJmxAutoConfiguration的bean條件
@ConditionalOnProperty(
prefix = "spring.application.admin",
value = {"enabled"},
havingValue = "true",
matchIfMissing = false
)
public class SpringApplicationAdminJmxAutoConfiguration {
@Configuration
@ConditionalOnClass({ MBeanExporter.class })
@ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true",
matchIfMissing = true)
public class JmxAutoConfiguration implements EnvironmentAware, BeanFactoryAware {
4總結(jié)
SpringBoot在啟動(dòng)的時(shí)候,首先會(huì)構(gòu)造SpringApplication對(duì)象,獲取并設(shè)置Initializers和Listeners,得到main啟動(dòng)類欢瞪。
-
接著調(diào)用run()方法活烙,run()方法執(zhí)行容器刷新,
2.1.刷新容器的時(shí)候遣鼓,執(zhí)行到容器的后置處理器invokeBeanFactoryPostProcessors方法時(shí),會(huì)去解析啟動(dòng)類上的@SpringBootApplication復(fù)合注解啸盏。
2.2.這個(gè)注解中有一個(gè)@EnableAutoConfiguration注解,表示開啟自動(dòng)配置功能(實(shí)際是import :AutoConfigurationPackages.Registrar.class)
2.3.另外還有個(gè)@Import注解引入了一個(gè)AutoConfigurationImportSelector這個(gè)類(它去掃描我們所有jar包下的META-INF下的spring.factories 自動(dòng)配置類文件骑祟,而從這個(gè)配置文件中取找key為EnableAutoConfiguration類的全路徑的值,并加載到容器)
然后在根據(jù)這些配置類中的條件注解回懦,來(lái)判斷是否將這些配置類及關(guān)聯(lián)的Bean在容器中進(jìn)行實(shí)例化,
(判斷的條件:主要是判斷項(xiàng)目是否有相關(guān)jar包或工程是否引入了相關(guān)的bean)次企。