启动Spring Boot应用只需执行以下代码:
1
| SpringApplication.run(ExampleApplication.class, args);
|
它会先初始化SpringApplication
实例,然后执行该实例的run
方法:
1
| new SpringApplication(primarySources).run(args);
|
初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
|
初始化比较简单:
- 设置资源加载器,默认为null
- 设置应用程序主源,一般为应用程序启动类
- 推断应用程序类型,枚举值有:
NONE|SERVLET|REACTIVE
- 设置SpringApplication实例的initializers(
ApplicationContextInitializer
)属性
- 设置SpringApplication实例的listeners(
ApplicationListener
)属性
- 推断应用程序主类,其实就是启动类
getSpringFactoriesInstances
在设置SpringApplication实例的的initializers和listeners属性时,调用了getSpringFactoriesInstances
简单工厂方法,它是用来指定接口类型的示例集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
|
SpringFactoriesLoader
SpringFactoriesLoader
是一个简单工厂类,它供Spring内部使用,用于读取META-INF/spring.factories文件(可能存在于类路径中的多个JAR文件中),返回指定接口的实现类的实例集合。
UML类图:
源码
重点看下loadFactoryNames
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { 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); } }
|
通过源码可以学习到MultiValueMap
和ConcurrentReferenceHashMap
的应用:
- MultiValueMap
同一个key,可以存在多个value
- ConcurrentReferenceHashMap
存储软引用或弱引用,内存溢出以前会清理该对象,适用于缓存
实现类
spring-boot-version.jar
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| # 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.web.context.ServerPortInfoApplicationContextInitializer
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ 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
|
spring-boot-autoconfigure-version.jar
1 2 3 4 5 6 7 8
| # Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer
|
启动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); 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; }
|
流程图
![Spring Boot应用启动流程图](/images/springboot/Spring Boot应用启动流程图.png)
在启动过程中,如果发生异常,会执行异常处理,并组播failed事件。
实现类
spring-boot-version.jar
1 2 3 4 5 6 7
| # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters org.springframework.boot.SpringBootExceptionReporter=\ org.springframework.boot.diagnostics.FailureAnalyzers
|