SpringBoot 源码分析 - SpringApplication启动流程六
- 初始化基本流程
- SpringApplication的prepareEnvironment准备环境
- SpringApplication的getOrCreateEnvironment创建环境
- configureEnvironment配置环境
- ApplicationConversionService的getSharedInstance配置转换器
- SpringApplication的printBanner打印banner
- createApplicationContext创建上下文
初始化基本流程
SpringApplication的prepareEnvironment准备环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();//获取环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);//ConfigurationPropertySources附加到环境一次
listeners.environmentPrepared(environment);//广播环境准备好的事件
bindToSpringApplication(environment);//绑定到SpringApplication
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());//推断环境,如果不是这个类型,要进行切换
}
ConfigurationPropertySources.attach(environment);//又附加一次,主要是为了放最前面
return environment;
}
SpringApplication的getOrCreateEnvironment创建环境
根据前面的webApplicationType
判断要创建哪种环境。
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
configureEnvironment配置环境
这里主要是配置了很多的类型转换器和格式转换器,另外两个跟换进属性相关
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();//配置转换器
environment.setConversionService((ConfigurableConversionService) conversionService);//设置到环境中去
}
configurePropertySources(environment, args);//配置默认属性
configureProfiles(environment, args);//配置代码添加的环境
}
ApplicationConversionService的getSharedInstance配置转换器
这个里面有配置很多转换器,我就不多讲了,自己看就行了。
/**
* Add converters appropriate for most environments.
* @param converterRegistry the registry of converters to add to
* (must also be castable to ConversionService, e.g. being a {@link ConfigurableConversionService})
* @throws ClassCastException if the given ConverterRegistry could not be cast to a ConversionService
*/
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
/**
* Add converters useful for most Spring Boot applications.
* @param registry the registry of converters to add to (must also be castable to
* ConversionService, e.g. being a {@link ConfigurableConversionService})
* @throws ClassCastException if the given ConverterRegistry could not be cast to a
* ConversionService
*/
public static void addApplicationConverters(ConverterRegistry registry) {
addDelimitedStringConverters(registry);
registry.addConverter(new StringToDurationConverter());
registry.addConverter(new DurationToStringConverter());
registry.addConverter(new NumberToDurationConverter());
registry.addConverter(new DurationToNumberConverter());
registry.addConverter(new StringToPeriodConverter());
registry.addConverter(new PeriodToStringConverter());
registry.addConverter(new NumberToPeriodConverter());
registry.addConverter(new StringToDataSizeConverter());
registry.addConverter(new NumberToDataSizeConverter());
registry.addConverter(new StringToFileConverter());
registry.addConverter(new InputStreamSourceToByteArrayConverter());
registry.addConverterFactory(new LenientStringToEnumConverterFactory());
registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());
if (registry instanceof ConversionService) {
addApplicationConverters(registry, (ConversionService) registry);
}
}
其他剩下的暂时不讲,因为比较复杂,很多都是一些属性的配置,比如会获取你的spring.profiles.active
信息,而且是ConfigFileApplicationListener
收到环境准备好的事件后做的事,当然还有其他的一些配置信息,我们还是先把主线弄明白再搞细节,不过这里配置完成会进行广播,这次的事件是ApplicationEnvironmentPreparedEvent
:
SpringApplication的printBanner打印banner
内部支持 "gif", "jpg", "png","txt":
默认把资源放到resources
下,名字为banner.xxx
就可以了,自己可以去试试:
我们经常看到的默认的在这里面:
public static void main(String[] args){
SpringApplication.run(SteamGridApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 启动成功 ლ(´ڡ`ლ)゙ \n" +
" _, ___ __, _, _, _ _, __, _ __,\n" +
" (_ | |_ /_\\ |\\/| / _ |_) | | \\\n" +
" , ) | | | | | | ~~ \\ / | \\ | |_/\n" +
" ~ ~ ~~~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ");
}
createApplicationContext创建上下文
根据类型创建对应的上下文对象,默认全是注解
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET://AnnotationConfigServletWebServerApplicationContext
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE://AnnotationConfigReactiveWebServerApplicationContext
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default://AnnotationConfigApplicationContext
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
准备环境这里比较复杂,可以写很多,这个还是细节的时候去看,先把脉络理完吧,后面讲上下文的准备,这个也很重要,准备完了就是spring
的核心refresh
了。