1、SpringApplication
public ConfigurableApplicationContext run(String... args) {
...
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//StandardServletEnvironment#1.1.1
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
ConfigurableApplicationContext context = createApplicationContext();
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.started(context);
callRunners(context, applicationArguments);
listeners.running(context);
return context;
}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = getOrCreateEnvironment();//实例化StandardServletEnvironment
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
...
return environment;
}
参考文章得知针对ApplicationEnvironmentPreparedEvent
事件触发核心监听器ConfigFileApplicationListener
执行。
1.1、解析应用参数Program Arguments & 环境属性Environment Variable
由核心类StandardServletEnvironment
及其父类AbstractEnvironment完成解析&持有 Program Arguments、Environment Variable配置值。
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
...
sources.addFirst(new SimpleCommandLinePropertySource(args));// #3
}
StandardServletEnvironment父类AbstractEnvironment属性MutablePropertySources持有应用参数|运行参数args【CommandLineArgs】
。
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
environment.getActiveProfiles(); //AbstractEnvironment#getActiveProfiles
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
2、AbstractEnvironment
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active";
public AbstractEnvironment() {
customizePropertySources(this.propertySources);//StandardServletEnvironment#customizePropertySources
}
public String[] getActiveProfiles() {
return StringUtils.toStringArray(doGetActiveProfiles());
}
protected Set<String> doGetActiveProfiles() {
synchronized (this.activeProfiles) {
if (this.activeProfiles.isEmpty()) {
String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setActiveProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.activeProfiles;
}
}
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
}
主要作用是创建MutablePropertySources
、PropertySourcesPropertyResolver
。
2.1、StandardServletEnvironment
Spring中的StandardServletEnvironment将由基于servlet的web应用程序使用,所有基于servlet的web相关ApplicationContext类将会初始化一个StandardServletEnvironment实例。
StandardServletEnvironment实例化过程:
- MutablePropertySources实例化。
- PropertySourcesPropertyResolver实例化。
- 获取到应用全部的
系统属性以及环境变量属性
。
2.1.1、StandardServletEnvironment实例化
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();
}
}
StandardServletEnvironment的实例化会调用其抽象类AbstractEnvironment
的构造方法,在AbstractEnvironment构造方法中调用子类StandardServletEnvironment#customizePropertySources
方法。
2.1.2、StandardServletEnvironment类信息
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {
/** servletContext初始化参数属性源名 */
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
/** ServletConfig初始化参数属性源名 */
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";
/** JNDI属性源名 */
public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiProperties";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);//StandardEnvironment#customizePropertySources
}
@Override
public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}
}
2.2、StandardEnvironment
public class StandardEnvironment extends AbstractEnvironment {
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
}
- getSystemProperties():其实就是通过
(Map) System.getProperties()
获取系统属性包括用户自定义的JVM的参数【-D】。 - getSystemEnvironment():通过
(Map) System.getenv()
获取应用环境变量属性,例如spring.profiles.active=dev
等。
此时并没有解析配置文件的配置信息
3、ConfigurableApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
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);
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
ConfigurableApplicationContext的子类同时继承了GenericApplicationContext
。在其实例化过程中触发其父类的实例化,此时存在IOC顶级容器DefaultListableBeanFactory
的实例化。
4、SimpleCommandLinePropertySource
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
4.1、SimpleCommandLineArgsParser
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionName;
String optionValue = null;
if (optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf('='));
optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
}
else {
optionName = optionText;
}
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
- CommandLineArgs#addOptionArg:Map集合类型的存储结构。
- CommandLineArgs#addNonOptionArg:List集合类型的存储结构。
如果Program Arguments项中配置项是通过"–"符号设置,则以Map集合类型的结构存储,否则List集合类型的结构存储。
Program Arguments: --mq.client=huawei --mq.name=lisi mq.age=10 mq.addr=北京
5、PropertySourcesPropertyResolver
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
private final PropertySources propertySources;//MutablePropertySources
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
for (PropertySource<?> propertySource : this.propertySources) {
Object value = propertySource.getProperty(key);
if (value != null) {
if (resolveNestedPlaceholders && value instanceof String) {
value = resolveNestedPlaceholders((String) value);
}
logKeyFound(key, propertySource, value);
return convertValueIfNecessary(value, targetValueType);
}
}
}
return null;
}
}
PropertySourcesPropertyResolver中PropertySources即为MutablePropertySources,其包含servletContextInitParams、servletConfigInitParams、jndiProperties、systemEnvironment、systemProperties五部分。最终通过systemEnvironment获取到spring.profiles.active
的环境配置实际值。
6、ConfigFileApplicationListener
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();//通过SPI机制加载全部EnvironmentPostProcessor相关的后置处理器
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
}
spring-boot.jar#spring.factories文件:
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
ConfigFileApplicationListener本身也是EnvironmentPostProcessor后置处理器。
同时存在三个不同扩展名的配置文件,配置信息均生效,如果文件中存在相同配置key则其优先级为:properties>yaml>yml。
6.1、静态内部类Loader
private Map<Profile, MutablePropertySources> loaded;
private Map<DocumentsCacheKey, List<Document>> loadDocumentsCache = new HashMap<>();
public void load() {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
initializeProfiles();//获取环境变量spring.profiles.active
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
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();
}
静态内部类Loader中的profiles包含两个一个是通过StandardServletEnvironment获取环境变量得到的。另一个profiles为null,其作用是保证读取到默认文件的配置文件信息【即使没有通过spring.profiles.active环境变量激活profiles】。
- profiles通常包括两个:代表默认配置文件的null以及通过spring.profiles.active激活的profiles。
- SearchLocations:获取不同路径下的全部配置文件。
- propertySourceLoaders:配置文件不同后缀扩展名类型【.properties、.yml】的解析器。
loadDocumentsCache的作用:对于相同路径下的配置文件避免重复加载。
loaded:配置文件抽象成Document后的存储容器。通常包含默认文件【profile = null】以及激活的配置文件。
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));
});
}
getSearchNames:就是选择配置文件默认名DEFAULT_NAMES【application】。
6.1.1、获取项目配置文件所在的磁盘路径
private Set<String> getSearchLocations() {
// CONFIG_LOCATION_PROPERTY:spring.config.location
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
return getSearchLocations(CONFIG_LOCATION_PROPERTY);
}
// CONFIG_ADDITIONAL_LOCATION_PROPERTY : "spring.config.additional-location"
Set<String> locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY);
locations.addAll(
// DEFAULT_SEARCH_LOCATIONS : "classpath:/,classpath:/config/,file:./,file:./config/"
asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS));
return locations;
}
通常情况下会选择DEFAULT_SEARCH_LOCATIONS配置的文件分别读取应用项目中配置信息。
如果启动类增加运行参数–spring.config.location=/home/Desktop/application-dev.properties 则应用配置信息均由对应文件负责
6.1.2、抽象配置文件为Document
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
...
Set<String> processed = new HashSet<>();
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,consumer);
}
}
}
}
propertySourceLoaders:PropertiesPropertySourceLoader
【扩展名properties】、YamlPropertySourceLoader
【yml、yaml】。
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
if (profile != null) {
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);//加载默认配置文件
load(loader, profileSpecificFile, profile, profileFilter, consumer);//加载profiles对应的配置文件
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
//profiles没有激活
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
prefix + fileExtension:classpath:/application.properties。
- 如果profiles没有激活【profiles = null】:加载默认配置文件即可。
- 如果profiles已经激活,则同时加载默认配置文件以及profiles对应的配置文件。
defaultFilter & profileFilter:执行步骤#6.1.3时,参数profile的取值不同,过滤条件对应不同
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
DocumentConsumer consumer) {
Resource resource = this.resourceLoader.getResource(location);
if (resource == null || !resource.exists()) {
//表明本地磁盘没有对应的配置文件
return;
}
String name = "applicationConfig: [" + location + "]";
//读取项目中本地文件中配置信息,将配置文件抽象成Document
List<Document> documents = loadDocuments(loader, name, resource);
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
if (filter.match(document)) {
//true:表示读取到本地配置文件信息
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
// #6.1.4 将当前加载的配置信息添加到Map集合loaded中【Map<Profile, MutablePropertySources> loaded】
loaded.forEach((document) -> consumer.accept(profile, document));
}
}
将Document文件添加到缓存loadDocumentsCache
中。
6.1.3、DocumentFilter
Document中Profiles属性表示在配置文件显式的spring.profiles.active属性激活。
private DocumentFilter getPositiveProfileFilter(Profile profile) {
return (Document document) -> {
if (profile == null) {
return ObjectUtils.isEmpty(document.getProfiles());
}
return ObjectUtils.containsElement(document.getProfiles(), profile.getName())
&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles()));
};
}
过滤当前激活的profile配置文件:
- 返回true则表示当前激活的profile配置文件有效。
- 如果profile & document#profiles 均为null,则返回true。
- document#profiles集合包含profile并且profile已经被激活,则返回true。
private DocumentFilter getNegativeProfileFilter(Profile profile) {
return (Document document) -> (profile == null && !ObjectUtils.isEmpty(document.getProfiles())
&& this.environment.acceptsProfiles(Profiles.of(document.getProfiles())));
}
6.1.4、DocumentConsumer
private DocumentConsumer addToLoaded(BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
boolean checkForExisting) {
return (profile, document) -> {
if (checkForExisting) {
for (MutablePropertySources merged : this.loaded.values()) {
if (merged.contains(document.getPropertySource().getName())) {
return;
}
}
}
MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
(k) -> new MutablePropertySources());
// MutablePropertySources#addLast
// document.getPropertySource():当前加载的配置文件信息
addMethod.accept(merged, document.getPropertySource());
};
}
作用:
6.1.5、MutablePropertySources
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
public void addLast(PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(propertySource);
}
}
PropertySource:
6.1.6、Document
作用:将配置文件中的信息抽象为Document对象中propertySource属性持有。
Document(PropertySource<?> propertySource, String[] profiles, Set<Profile> activeProfiles,
Set<Profile> includeProfiles) {
this.propertySource = propertySource;
this.profiles = profiles;
this.activeProfiles = activeProfiles;
this.includeProfiles = includeProfiles;
}
profiles:配置文件显式配置spring.profiles
。
activeProfiles:配置文件显式配置spring.profiles.active
。