框架:SpringBoot之SpringApplication源码深度解析(下)
我们回到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开始分析,现在感觉思路乱没关系,最后我会从头到尾再理一遍。