我們回到SpringApplication的run方法繼續分析。
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;
}
上一篇介紹了SpringApplication的構造跟run方法介紹到執行ApplicationStartingEvent事件對應的listeners的相應方法執行。
我們繼續往下看。將傳入的參數構造成DefaultApplicationArguments實例:
public DefaultApplicationArguments(String[] args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
可以看到這裏根據啓動時傳入的參數構造了source實例:
Source(String[] args) {
super(args);
}
source僅僅調用了父類的構造:
public SimpleCommandLinePropertySource(String... args) {
super((new SimpleCommandLineArgsParser()).parse(args));
}
這裏實例化了SimpleCommandLineArgsParser的實例,並調用其parse方法解析啓動時的命令行參數,返回CommandLine繼續傳遞給父類的構造:
SimpleCommandLineArgsParser() {
}
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
String[] var3 = args;
int var4 = args.length;
for(int var5 = 0; var5 < var4; ++var5) {
String arg = var3[var5];
if(arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionValue = null;
String optionName;
if(optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf(61));
optionValue = optionText.substring(optionText.indexOf(61) + 1, optionText.length());
} else {
optionName = optionText;
}
if(optionName.isEmpty() || optionValue != null && optionValue.isEmpty()) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
} else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
首先初始化一個CommandLineArgs,然後遍歷args,如果args以“--”開頭,於是以“=”分割加入到OptionArg中,否則直接加入到NonOptionArg中。
我們一直看其繼承鏈上的的構造函數:
public CommandLinePropertySource(T source) {
super("commandLineArgs", source);
}
public EnumerablePropertySource(String name, T source) {
super(name, source);
}
public PropertySource(String name, T source) {
this.logger = LogFactory.getLog(this.getClass());
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
到這裏,DefaultApplicationArguments實例構造完成。繼續回到SpringApplication的run方法,調用prepareEnvironment方法並傳入listeners跟剛剛構造的applicationArguments:
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.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
調用getOrCreateEnviroment方法獲取或者創建ConfigurableEnvironment:
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
先判斷environment是否爲空,不爲空則直接返回,如果這裏是web環境那麼返回新構造的StandardServletEnvironment,否則返回新構造的StandardEnvironment。以一般情況StandardEnvironment介紹,其構造方法爲空,具體構造的邏輯在其父類AbstractEnvironment中:
public AbstractEnvironment() {
this.propertySources = new MutablePropertySources(this.logger);
this.propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
this.customizePropertySources(this.propertySources);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Initialized " + this.getClass().getSimpleName() + " with PropertySources " + this.propertySources);
}
}
這裏構造了Source、Resolver實例,調用customizePropertySources方法並傳入之前構造的Source(propertySource)。這個方法在這裏是空的。子類StandardEnvironment覆蓋了其父類方法,做了具體實現:
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource("systemProperties", this.getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
}
將系統數據與環境添加進來。
得到ConfigurableEnvironment後,然後調用configureEnvironment方法配置環境。
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
先看configurePropertySources方法:
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));
}
}
}
先得到之前生成的Sources,如果此時defaultProperties不爲空,那麼把defaultProperties添加到Sources尾部,如果addCommandLineProperties爲true,並且命令參數個數不爲0,那麼先得到CommandLineArgs字符串,如果該字符串存在於sources的鏈中,那麼將參數構造成composite,然後替換掉字符串在sources的鏈中的位置。否則直接構造SimpleCommandLinePropertySource添加到鏈的頭部。
此時sources中存在commandLineArgs、systemProperties、systemEnvironment、defaultProperties等
我們繼續看其Profiles的配置,調用configureProfiles方法:
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));
}
可以看到,先獲得activeProfiles配置,將環境中的activeProfiles存入到有序的linkedSet中,變成數組,存入迴環境中的activeProfiles。environment的具體邏輯在AbstractEnvironment中。
protected Set<String> doGetActiveProfiles() {
Set var1 = this.activeProfiles;
synchronized(this.activeProfiles) {
if (this.activeProfiles.isEmpty()) {
String profiles = this.getProperty("spring.profiles.active");
if (StringUtils.hasText(profiles)) {
this.setActiveProfiles(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(profiles)));
}
}
return this.activeProfiles;
}
}
由於spring boot外置配置屬性優先級高於代碼級。這裏先得到activeProfiles看其是否爲空,如果不爲空那麼直接返回,爲空再去獲得"spring.profiles.active"對應的屬性。
配置完成後調用listeners的environmentPrepared方法並傳入environment,通知所有的對其感興趣的listener。調用的是SpringApplicationRunListeners的environmentPrepared方法,內部調用EventPublishingRunListener的environmentPrepared 並且調用的事件類型爲ApplicationEnvironmentPreparedEvent.
查配置文件得到感興趣的類:
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
分別調用了他們的onApplicationEvent方法,先來看ConfigFileApplicationListener的:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
這裏調用了onApplicationEnvironmentPreparedEvent方法:
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
先調用loadPostProcessors()方法,得到配置的EveironmentPostProcessor集合:
List<EnvironmentPostProcessor> loadPostProcessors() {
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,
getClass().getClassLoader());
}
其實這個形式的方法我們已經很熟悉了,調用SpringFactoriesLoader的loadFactories方法,得到配置文件(\META-INF\spring.factories)中指定的EnvironmentPostProcessor類爲key的集合的類的實例:
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
回到onApplicationEnvironmentPreparedEvent方法,得到postProcessors後,把自己即ConfigFileApplicationListener實例也加進去,然後排序,分別調用其postProcessEnvironment方法。
先看CloudFoundryVcapEnvironmentPostProcessor的postProcessEnvironment方法:
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) {
Properties properties = new Properties();
JsonParser jsonParser = JsonParserFactory.getJsonParser();
addWithPrefix(properties,
getPropertiesFromApplication(environment, jsonParser),
"vcap.application.");
addWithPrefix(properties, getPropertiesFromServices(environment, jsonParser),
"vcap.services.");
MutablePropertySources propertySources = environment.getPropertySources();
if (propertySources.contains(
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
propertySources.addAfter(
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
new PropertiesPropertySource("vcap", properties));
}
else {
propertySources
.addFirst(new PropertiesPropertySource("vcap", properties));
}
}
}
先判斷環境是否是在CloudFoundry中,如果不在則不處理,直接返回。
CLOUD_FOUNDRY {
@Override
public boolean isActive(Environment environment) {
return environment.containsProperty("VCAP_APPLICATION")
|| environment.containsProperty("VCAP_SERVICES");
}
},
如果在,那麼繼續。將“vacp.application.”,"vamp.services."開頭的配置信息添加到propertis中,再判斷environment中的propertySources中是否有名爲“commandLineArgs”的source,有的話則把properties以名爲“vcap”的形式存入到“commandLineArgs”資源後面。如果沒有則直接放入隊首。
繼續看SpringApplicationJsonEnvironmentPostProcessor的postProcessEnvironment方法:
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.stream().map(JsonPropertyValue::get).filter(Objects::nonNull)
.findFirst().ifPresent((v) -> processJson(environment, v));
}
獲取propertySources,再遍歷其中,得到有JsonPropertyValue的配置,如果有則對每個JsonPropertyValue調用processJson方法處理:
private void processJson(ConfigurableEnvironment environment,
JsonPropertyValue propertyValue) {
try {
JsonParser parser = JsonParserFactory.getJsonParser();
Map<String, Object> map = parser.parseMap(propertyValue.getJson());
if (!map.isEmpty()) {
addJsonPropertySource(environment,
new JsonPropertySource(propertyValue, flatten(map)));
}
}
catch (Exception ex) {
logger.warn("Cannot parse JSON for spring.application.json: "
+ propertyValue.getJson(), ex);
}
}
解析json爲map,再添加到environment中。
接下來是SystemEnvironmentPropertySourceEnvironmentPostProcessor的:
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
PropertySource<?> propertySource = environment.getPropertySources()
.get(sourceName);
if (propertySource != null) {
replacePropertySource(environment, sourceName, propertySource);
}
}
@SuppressWarnings("unchecked")
private void replacePropertySource(ConfigurableEnvironment environment,
String sourceName, PropertySource<?> propertySource) {
Map<String, Object> originalSource = (Map<String, Object>) propertySource
.getSource();
SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(
sourceName, originalSource);
environment.getPropertySources().replace(sourceName, source);
}
對systemenvironment資源調用replacePropertySource方法,將該資源封裝成OriginAwareSystemEnvironmentPropertySource放入原資源位置。
接下來我們來看下ConfigFileApplicationListener的postProcessEnvironment方法:
@Override
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();
}
先調用了RandomValuePropertySource的addToEnbvironment方法:
public static void addToEnvironment(ConfigurableEnvironment environment) {
environment.getPropertySources().addAfter(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME));
logger.trace("RandomValuePropertySource add to Environment");
}
在systemEnvironment資源的後面添加了名爲random的RandomValuePropertySource資源。可以關注下其取數據的函數例如${random.int}。
@Override
public Object getProperty(String name) {
if (!name.startsWith(PREFIX)) {
return null;
}
if (logger.isTraceEnabled()) {
logger.trace("Generating random property for '" + name + "'");
}
return getRandomValue(name.substring(PREFIX.length()));
}
private Object getRandomValue(String type) {
if (type.equals("int")) {
return getSource().nextInt();
}
if (type.equals("long")) {
return getSource().nextLong();
}
String range = getRange(type, "int");
if (range != null) {
return getNextIntInRange(range);
}
range = getRange(type, "long");
if (range != null) {
return getNextLongInRange(range);
}
if (type.equals("uuid")) {
return UUID.randomUUID().toString();
}
return getRandomBytes();
}
可以看到其結果,random.nextInt()。
接下來構造了Loader實例,並調用其load方法加載資源:
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.resourceLoader = (resourceLoader != null ? resourceLoader
: new DefaultResourceLoader());
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
PropertySourceLoader.class, getClass().getClassLoader());
}
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();
if (profile != null && !profile.isDefaultProfile()) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
}
構造可以看到無非是一些賦值,其中需要注意的是propertySourceLoaders無非是:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
關注其load方法,調用了initializeProfiles方法初始化配置文件:
private void initializeProfiles() {
// The default profile for these purposes is represented as null. We add it
// first so that it is processed first and has lowest priority.
this.profiles.add(null);
Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty));
// Any pre-existing active profiles set via property sources (e.g.
// System properties) take precedence over those added in config files.
addActiveProfiles(activatedViaProperty);
if (this.profiles.size() == 1) { // only has null profile
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
this.profiles.add(defaultProfile);
}
}
}
一開始profiles是空的linkedList實例,這裏先調用getProfilesActivatedViaProperty方法得到activeProfiles:
private Set<Profile> getProfilesActivatedViaProperty() {
if (!this.environment.containsProperty(ACTIVE_PROFILES_PROPERTY)
&& !this.environment.containsProperty(INCLUDE_PROFILES_PROPERTY)) {
return Collections.emptySet();
}
Binder binder = Binder.get(this.environment);
Set<Profile> activeProfiles = new LinkedHashSet<>();
activeProfiles.addAll(getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
activeProfiles.addAll(getProfiles(binder, ACTIVE_PROFILES_PROPERTY));
return activeProfiles;
}
一開始,如果environment中沒有包含spring.profiles.active和spring.profiles.include的配置資源的話,直接返回空集合。如果含有配置,那麼通過傳入environment構造Binder,再通過binder得到spring.profiles.active和spring.profiles.include
有點長了,下次從load開始分析,現在感覺思路亂沒關係,最後我會從頭到尾再理一遍。