源码:
run
|- 1 stopWatch.start()
|- 2 configureHeadlessProperty
|- 3 listeners.starting()
|- 4 prepareEnvironment(...)
|- 5 printBanner(environment)
|- 6 createApplicationContext()
|- 7 new FailureAnalyzers(context)
|- 8 prepareContext(...)
|- 9 refreshContext(context)
|- 10 afterRefresh
|- 11 listeners.finished
|- 12 stopWatch.stop()
|- 13 new StartupInfoLogger(...).logStarted(...)
|- 14 handleRunFailure(...)
public ConfigurableApplicationContext run(String... args) {
// 略 1~3 ...
try {
// 4、
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 略 5~13 ...
return context;
}
catch (Throwable ex) {
// 略 14 ...
}
}
1、创建DefaultApplicationArguments实例
DefaultApplicationArguments 是啥?说白了,它就是用来封装运行参数并提供一些方便访问参数的方法。
public DefaultApplicationArguments(String[] args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
创建 DefaultApplicationArguments 实例时,初始化两个实例变量 ,最终的结果如下图所示(运行参数设置了 --server.port=8001
),大家可以自行查看源码,简单不赘述
2、prepareEnvironment
这个方法做了三件事情:
1、获取或者创建ConfigurableEnvironment - StandardServletEnviroment
web环境会创建StandardServletEnviroment
,
2、
3、
2.1 获取或者创建ConfigurableEnvironment
/****** o.s.boot.SpringApplication ******/
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webEnvironment) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
ConfigurableEnvironment 是个接口,主要作用是提供当前运行环境的公开接口,其中 StandardServletEnviroment
和 StandardEnvironment
都是其实现类,类图如下:
StandardServletEnviroment 类图 |
---|
我们在创建 StandardServletEnviroment
实例拥有从父类 AbstractEnviroment
继承来的 MutablePropertySources 类型属性 propertySources ,在创建实例时,调用 customizePropertySources(...)
方法向propertySources放入一些PropertySource对象。
调用关系 |
---|
源码参考:
/****** o.s.core.env.AbstractEnvironment ******/
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
if (logger.isDebugEnabled()) {
logger.debug("Initialized " + getClass().getSimpleName()
+ " with PropertySources " + this.propertySources);
}
}
/****** o.s.web.context.support.StandardEnvironment ******/
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME
, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
, getSystemEnvironment()));
}
/****** o.s.web.context.support.StandardServletEnvironment ******/
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);
}
执行完这些代码后的内存状态如下图:
StandardServletEnvironment 内存状态图 |
---|
点击查看:System.getProperties()内容
点击查看:System.getenv()内容
2.2 配置ConfigurableEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
2.2.1 配置PropertySources
所谓的配置PropertySource ,其实就是向环境中添加 PropertSource ,画个图看一下
StandardServletEnvironment 内存状态图 |
---|
private boolean addCommandLineProperties = true;
// 在environment中添加、删除或重新排序所有的propertysource。
protected void configurePropertySources(ConfigurableEnvironment environment,
String[] args) {
MutablePropertySources sources = environment.getPropertySources();
// 我的 defaultProperties 为null,什么时候有值? 硬编码设置默认属性时有值!参考:[1、硬编码]
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
// 放在List的最后位置,表示优先级最低
sources.addLast(
new MapPropertySource("defaultProperties", this.defaultProperties));
}
if (this.addCommandLineProperties && args.length > 0) {
// COMMAND_LINE_PROPERTY_SOURCE_NAME 值为 "commandLineArgs"
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
// 从 MutablePropertySources 的变量 List<PropertySource<?>> propertySourceList 中
// 查找有没有名称为 commandLineArgs 的 PropertySource,我们看一下上面的“内存状态图”,里面是没有的
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(new SimpleCommandLinePropertySource(
name + "-" + args.hashCode(), args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
// 将 命令行参数封装为SimpleCommandLinePropertySource 放入MutablePropertySources 中
// 放在第一个表示优先级最高
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
参考链接: [1、硬编码]
我们来看看StandardServletEnvironment 中 MutablePropertySources 的 List<PropertySource<?>> propertySourceList 变量的结果:
/****************** StandardServletEnvironment ******************/
environment = {StandardServletEnvironment@1914} "..."
ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active"
activeProfiles = {LinkedHashSet@1998} size = 0
DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default"
defaultProfiles = {LinkedHashSet@2000} size = 1
IGNORE_GETENV_PROPERTY_NAME = "spring.getenv.ignore"
JNDI_PROPERTY_SOURCE_NAME = "jndiProperties"
logger = {SLF4JLocationAwareLog@1978}
propertyResolver = {PropertySourcesPropertyResolver@2003}
/****************** MutablePropertySources ******************/
propertySources = {MutablePropertySources@1931} "[...]"
logger = {SLF4JLocationAwareLog@1978}
/****************** MutablePropertySources#List<PropertySource>propertySourceList ******************/
propertySourceList = {CopyOnWriteArrayList@1979} size = 5
0 = {SimpleCommandLinePropertySource@..} "SimpleCommandLinePropertySource {name='commandLineArgs'}"
1 = {PropertySource$StubPropertySource@..} "StubPropertySource {name='servletConfigInitParams'}"
2 = {PropertySource$StubPropertySource@..} "StubPropertySource {name='servletContextInitParams'}"
3 = {MapPropertySource@1984} "MapPropertySource {name='systemProperties'}"
4 = {SystemEnvironmentPropertySource@..} "SystemEnvironmentPropertySource {name='systemEnvironment'}"
RESERVED_DEFAULT_PROFILE_NAME = "default"
SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams"
SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams"
SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"
SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties"
2.2.2 配置 profiles
设置应用程序的 profiles,profiles 的作用就是将不同的配置参数作用于不同的的环境。
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<String>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
}
2.2.3
onApplicationEvent:167, ConfigFileApplicationListener (o.s.boot.context.config)
doInvokeListener:172, SimpleApplicationEventMulticaster (o.s.context.event)
invokeListener:165, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:139, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:122, SimpleApplicationEventMulticaster (o.s.context.event)
environmentPrepared:74, EventPublishingRunListener (o.s.boot.context.event)
environmentPrepared:54, SpringApplicationRunListeners (o.s.boot)
prepareEnvironment:325, SpringApplication (o.s.boot)
run:296, SpringApplication (o.s.boot)
main:20, PropertyTestMainSpringApplication (com.yh.stu.springboot.property)
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
// getApplicationListeners(event, type)的结果:
result = {LinkedList@1976} size = 7
0 = {ConfigFileApplicationListener@1864}
1 = {AnsiOutputApplicationListener@1978}
2 = {LoggingApplicationListener@1979}
3 = {ClasspathLoggingApplicationListener@1980}
4 = {BackgroundPreinitializer@1981}
5 = {DelegatingApplicationListener@1982}
6 = {FileEncodingApplicationListener@1983}
Q&A :
1、❓ // TODO 查看其它 Listener 是如何判断是否监听特定event的
ConfigFileApplicationListener也监听了 ApplicationEnvironmentPreparedEvent 事件 ,是因为实现了 SmartApplicationListener 接口的方法supportsEventType,这里会判断是否支持特定的 event
从所有Listener中获取监听特定event 的ListenersupportsEventType
supportsEventType:156, ConfigFileApplicationListener (o.s.boot.context.config)
supportsEventType:64, GenericApplicationListenerAdapter (o.s.context.event)
supportsEvent:289, AbstractApplicationEventMulticaster (o.s.context.event)
retrieveApplicationListeners:221, AbstractApplicationEventMulticaster (o.s.context.event)
getApplicationListeners:192, AbstractApplicationEventMulticaster (o.s.context.event)
multicastEvent:128, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:122, SimpleApplicationEventMulticaster (o.s.context.event)
environmentPrepared:74, EventPublishingRunListener (o.s.boot.context.event)
environmentPrepared:54, SpringApplicationRunListeners (o.s.boot)
prepareEnvironment:325, SpringApplication (o.s.boot)
run:296, SpringApplication (o.s.boot)
run:1118, SpringApplication (o.s.boot)
run:1107, SpringApplication (o.s.boot)
main:20, SpringBootSourceStuApplication (com.yh.stu.springboot.hello)
2、如何解析application.yml/properties 文件的时机?
搜索:application.properties,找到 ConfigFileApplicationListener ,搜索yml 找到load方法,打上断点
调用栈:
getAllFileExtensions:205, PropertySourcesLoader (o.s.boot.env)
load:447, ConfigFileApplicationListener$Loader (o.s.boot.context.config)
load:386, ConfigFileApplicationListener$Loader (o.s.boot.context.config)
addPropertySources:225, ConfigFileApplicationListener (o.s.boot.context.config)
postProcessEnvironment:195, ConfigFileApplicationListener (o.s.boot.context.config)
onApplicationEnvironmentPreparedEvent:182, ConfigFileApplicationListener (o.s.boot.context.config)
onApplicationEvent:168, ConfigFileApplicationListener (o.s.boot.context.config)
doInvokeListener:172, SimpleApplicationEventMulticaster (o.s.context.event)
invokeListener:165, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:139, SimpleApplicationEventMulticaster (o.s.context.event)
multicastEvent:122, SimpleApplicationEventMulticaster (o.s.context.event)
environmentPrepared:74, EventPublishingRunListener (o.s.boot.context.event)
environmentPrepared:54, SpringApplicationRunListeners (o.s.boot)
prepareEnvironment:325, SpringApplication (o.s.boot)
run:296, SpringApplication (o.s.boot)
run:1118, SpringApplication (o.s.boot)
run:1107, SpringApplication (o.s.boot)
main:20, SpringBootSourceStuApplication (com.yh.stu.springboot.hello)
public PropertySourcesLoader(MutablePropertySources propertySources) {
Assert.notNull(propertySources, "PropertySources must not be null");
this.propertySources = propertySources;
// 两个:YamlPropertySourceLoader PropertiesPropertySourceLoader
this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
附-YamlPropertySourceLoader
public class YamlPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
@Override
public PropertySource<?> load(String name, Resource resource, String profile)
throws IOException {
if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
Processor processor = new Processor(resource, profile);
Map<String, Object> source = processor.process();
if (!source.isEmpty()) {
return new MapPropertySource(name, source);
}
}
return null;
}
/**
* {@link YamlProcessor} to create a {@link Map} containing the property values.
* Similar to {@link YamlPropertiesFactoryBean} but retains the order of entries.
*/
private static class Processor extends YamlProcessor {
Processor(Resource resource, String profile) {
if (profile == null) {
setMatchDefault(true);
setDocumentMatchers(new SpringProfileDocumentMatcher());
}
else {
setMatchDefault(false);
setDocumentMatchers(new SpringProfileDocumentMatcher(profile));
}
setResources(resource);
}
@Override
protected Yaml createYaml() {
return new Yaml(new StrictMapAppenderConstructor(), new Representer(),
new DumperOptions(), new Resolver() {
@Override
public void addImplicitResolver(Tag tag, Pattern regexp,
String first) {
if (tag == Tag.TIMESTAMP) {
return;
}
super.addImplicitResolver(tag, regexp, first);
}
});
}
public Map<String, Object> process() {
final Map<String, Object> result = new LinkedHashMap<String, Object>();
process(new MatchCallback() {
@Override
public void process(Properties properties, Map<String, Object> map) {
result.putAll(getFlattenedMap(map));
}
});
return result;
}
}
}
附-PropertiesPropertySourceLoader
public class PropertiesPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "properties", "xml" };
}
@Override
public PropertySource<?> load(String name, Resource resource, String profile)
throws IOException {
if (profile == null) {
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
if (!properties.isEmpty()) {
return new PropertiesPropertySource(name, properties);
}
}
return null;
}
}