目录
SpringApplicationRunListeners listeners = getRunListeners(args);
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments)
SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
SpringApplicationRunListeners保存了SpringApplicationRunListener类型的集合,SpringApplicationRunListener也是从META-INF/spring.factories文件中获取到并反射实例化出来的。反射的时候调用的是有参数的构造函数,目前就一个值EventPublishingRunListener,如下所示。可以看出EventPublishingRunListener中的多播器保存了SpringApplication 实例化时的ApplicationListener实例。
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
listeners.starting()
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
//EventPublishingRunListener
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
多播器广播了一个ApplicationStartingEvent事件,所有支持ApplicationStartingEven事件的listener执行onApplicationEvent事件处理方法
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args)
封装了启动参数,例如启动时指定的applicaton文件(dev、prod)
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments)
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
//广播environmentPrepared事件
listeners.environmentPrepared(environment);
//将环境绑定到SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
//环境转换器(当前不需要转换,所以还是标准的servlet环境)
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//将ConfigurationPropertySource支持附加到指定的Environment 。 使环境管理的每个PropertySource适应ConfigurationPropertySource并允许经典的PropertySourcesPropertyResolver调用使用configuration property names进行解析
ConfigurationPropertySources.attach(environment);
return environment;
}
- environment :StandardServletEnvironment。在实例化StandardServletEnvironment时会定制属性源PropertySources。
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
StandardServletEnvironment定制属性都将优先于StandardEnvironment超类提供的系统属性和环境变量。且与Servlet相关的属性源在此阶段作为stubs添加,并且在实际的ServletContext对象可用时将被完全初始化。所以此处servletConfigInitParams和servletContextInitParams对应的source都是空对象,起到占位符作用,而父类StandardEnvironment定制属性源systemProperties(System.getProperties())和systemEnvironment(System.getenv())对应的source都有值。
- configureEnvironment:配置环境。方法注释翻译:模板方法以该顺序委派给configurePropertySources(ConfigurableEnvironment, String[])和configureProfiles(ConfigurableEnvironment, String[]) 。 重写此方法以完全控制环境自定义,或者重写上述方法之一以分别对属性源或配置文件进行细粒度控制。因为SpringApplication是可以重写的,所以可以重写某些方法。
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { if (this.addConversionService) { ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService) conversionService); } configurePropertySources(environment, args); configureProfiles(environment, args); }
ConversionService:用于类型转换的服务接口。 这是转换系统的入口。 调用convert(Object, Class)使用此系统执行线程安全的类型转换。并将此实例设置到environment属性propertyResolver中。 configurePropertySources:配置属性源。主要是通过启动参数添加或替换属性源,在启动时指定defaultProperties添加defaultProperties属性源(例如SpringApplicationBuilder的properties(String... defaultProperties)方法) configureProfiles:配置该应用程序环境中哪些配置文件处于活动状态。在配置文件处理期间,可以通过{@code spring.profiles.active}属性激活其他配置文件。主要是通过查找StandardServletEnvironment中propertySources中是否有对应的key。
- listeners.environmentPrepared(environment):广播environmentPrepared事件。这里调试源码发现主要关注ConfigFileApplicationListener的onApplicationEvent。onApplicationEvent会调用onApplicationEnvironmentPreparedEvent方法,该方法会首先通过SpringFactoriesLoader.loadFactories(META-INF/spring.factories)查找EnvironmentPostProcessor所有实例(环境的后置处理器),依次执行每个实例的postProcessEnvironment方法。这里依旧只关注ConfigFileApplicationListener这个EnvironmentPostProcessor。关键代码如下:将配置文件属性源添加到指定的环境。
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
通过Loader构造函数发现了解析properties和yaml文件的PropertySourceLoader,这些PropertySourceLoader也是同通过SpringFactoriesLoader.loadFactories实例化出来的。
load方法。
1、initializeProfiles():初始化一个null和default的profile.null是为了读取application.yml/applicaton.properties。
2、循环处理profiles 。先处理值为null的profile,读取spring.profiles.active值(例如 dev),然后profiles添加dev的profile且移除default的profile.接着循环处理dev的profile。因为该profile不为空且不是默认的,所以会将该profile的name设置为环境的profile(下方代码1处)
3、addLoadedPropertySources:StandardServletEnvironment添加已经加装的PropertySources(如下图5、6所示)
application.yml对应的PropertySource部分内容截图:
public void load() {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
//1
if (profile != null && !profile.isDefaultProfile()) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
resetEnvironmentProfiles(this.processedProfiles);
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
}
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
getSearchLocations方法分析:其中DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/",也就是springboot查找配置文件的路径 ,可以看到asResolvedSet方法先将该字符串解析为list集合,然后进行了反转,所以springboot查找配置文件的顺序为:
private Set<String> getSearchLocations() {
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
return getSearchLocations(CONFIG_LOCATION_PROPERTY);
}
Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
locations.addAll(
asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
return locations;
}
private Set<String> asResolvedSet(String value, String fallback) {
List<String> list = Arrays.asList(StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(
(value != null) ? this.environment.resolvePlaceholders(value) : fallback)));
Collections.reverse(list);
return new LinkedHashSet<>(list);
}