本文基于SpringBoot2.1.5暂不探讨@SpringApplication如何装载SpringApplication,先专注于该类做了些什么事情。
构造函数
该类装载的时候会准备一系列数据,我们反编译源码,查看代码如下
这边我们挑几个重点的属性展开
setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))
这里断点处进入SpringFactoriesLoader.loadFactoryNames这个方法,我们接下来追踪到这个类的这个方法看下具体怎样获取这些类,因为下面的方法就是根据上面获取的类全限定名初始化类
这个方法我们看到他的注释写了从指定资源位置获取类的全限定名,我们主要关注下面的方法
classLoader.getResources(FACTORIES_RESOURCE_LOCATION)
这个常量的值META-INF/spring.factories
而该classLoader是SpringApplication所以获取的是该限定名所在Jar下面的文件
这些类的全限定名都在该文件内定义好了,所以上面第二个方法将返回一个以spring.factories为内容的Map,这里解析该资源的方法请读者重点理解下,实际的result要多出来几个key(该方法会返回父ClassLoader->ExtClassLoader),
最终我们的第一个方法以传入的factoryClassName作为key进行筛选获取一个List,这个方法在后面的ApplicationListener的相关Listener的初始化流程是一致的
setListeners(this.getSpringFactoriesInstances(ApplicationListener.class))
该方法的初始化与上面类同,生成的类在spring.factories文对应条目下
mainApplicationClass = this.deduceMainApplicationClass()
run
首先这里的StopWatch用来计时启动时间
然后我们先跳过ConfigurableApplicationContext看下SpringApplicationRunListeners这个类的获取,我们追踪到还是调用上面的方法获取spring.factories下面的类名进行实例化,这里实例化的是org.springframework.boot.context.event.EventPublishingRunListener ,
接下来我们看下ConfigurableEnvironment这个类的初始化
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
这里这个函数首先根据WebApplicationType去new一个相应类型的Environment
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();
}
}
然后填充这些配置数据
/**
* Template method delegating to
* {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
* {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
* Override this method for complete control over Environment customization, or one of
* the above for fine-grained control over property sources or profiles, respectively.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureProfiles(ConfigurableEnvironment, String[])
* @see #configurePropertySources(ConfigurableEnvironment, String[])
*/
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
/**
* Add, remove or re-order any {@link PropertySource}s in this application's
* environment.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureEnvironment(ConfigurableEnvironment, String[])
*/
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(
new MapPropertySource("defaultProperties", this.defaultProperties));
}
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource(
"springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
/**
* Configure which profiles are active (or active by default) for this application
* environment. Additional profiles may be activated during configuration file
* processing via the {@code spring.profiles.active} property.
* @param environment this application's environment
* @param args arguments passed to the {@code run} method
* @see #configureEnvironment(ConfigurableEnvironment, String[])
* @see org.springframework.boot.context.config.ConfigFileApplicationListener
*/
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles(); // ensure they are initialized
// But these ones should go first (last wins in a property key clash)
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
这里涉及到比较多的类,不具体一一展开,主要是填充了默认的property和activePorfiles。
完成配置准备阶段以后会通知前面的SpringListener,接下来进行绑定
/**
* Bind the environment to the {@link SpringApplication}.
* @param environment the environment to bind
*/
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
try {
Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
}
catch (Exception ex) {
throw new IllegalStateException("Cannot bind to SpringApplication", ex);
}
}
/**
* Create a new {@link Binder} instance from the specified environment.
* @param environment the environment source (must have attached
* {@link ConfigurationPropertySources})
* @return a {@link Binder} instance
*/
public static Binder get(Environment environment) {
return new Binder(ConfigurationPropertySources.get(environment),
new PropertySourcesPlaceholdersResolver(environment));
}
因为层级比较深,不深入展开,主要关注binder这个类,怎样填充属性
这里我们关注ConfigurableApplicationContext的初始化,首先这里会new一个org.springframework.context.annotation.AnnotationConfigApplicationContext类还是根据前面WebApplicationType来创建实例
然后要进入我们主要分析的
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
首先这个context会设置environment,然后设置resourceLoader
然后调用原先初始化的initializers的initialize方法,接下来发送通知给listener的contextPrepared,这里要重点关注DefaultListableBeanFactory.registerSingleton方法会更新状态,最后发送contextLoaded通知
prepareContext完成以后以后就要调用refreshContext,这个函数的主体代码主要在
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
方法比较多因为本身是抽象类是继承类实现的,这里我们的Context是上面给出的AnnotationConfigApplicationContext
这个会在其他文章具体来讲,涉及到Bean的生命周期
接下去,我们会调用afterRefresh,这个方法里面没有代码,是个protected方法,我们可以在入口作为监听使用,最后调用了callRunner会调用ApplicationRunner.class, CommandLineRunner这两个类的方法,所以我们常常用继承CommandLinerunner来在控制台启动,这之前会发送一个应用started的通知,然后发送running状态通知