Springboot启动流程(三)
之前我们研究了springboot启动流程中的listener的运行流程,接下来就该是各种环境的出事话包括environment和context这两个非常重要的spring环境
首先还是run方法的代码:
run方法代码如下:
public ConfigurableApplicationContext run(String... args) {
// listener的流程
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;
}
由于listener的流程已经研究过了代码就坐了省略
接下来Environment的流程:
首先先包装了一个DefaultApplicationArguments把我们启动的参数放入类中.
然后prepareEnvironment方法准备了一个Environment
configureIgnoreBeanInfo放入需要ignore的bean
printBanner打印Banner(SpringBoot启动时打印的logo)
1.DefaultApplicationArguments 类:
这个类包装了我们启动的main函数上面的String[] args,在我们启动jar包的时候我们可以给args赋值,这个值我们同样传到后面的run方法中并做了
一系列的操作
具体代码:
public DefaultApplicationArguments(String[] args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
先new了一个Source的对象,并传入args,
Source 类是DefaultApplicationArguments的内部类继承了 SimpleCommandLinePropertySource 类构造方法直接调用了super(args);所以我们只
需要看 SimpleCommandLinePropertySource 类的处理流程就行了:
SimpleCommandLinePropertySource 类构造方法:
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
super 类的构造方法同样调用了父类 CommandLinePropertySource 的构造方法,并传入处理之后的对象,我们来看下new SimpleCommandLineArgsParser().parse(args)
是如何处理args的:
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;
}
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 对象,这个对象主要是为了存储我们启动jar包是传入的配置如:--spring.profiles.active=dev 这些与spring有关的配置
并出入key,value键值对.代码非常简单,如果已--开头则进入处理拿到name和value,并放入commandLineArgs中并放回 CommandLineArgs 对象.之后调用父类
CommandLinePropertySource 的构造方法:
public CommandLinePropertySource(T source) {
super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source);
}
其中COMMAND_LINE_PROPERTY_SOURCE_NAME是上面写死的成员变量 public static final String COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs";
然后还是调用父类的构造方法 EnumerablePropertySource,然后直接一个透传接着调用 PropertySource 的构造方法:
public PropertySource(String name, T source) {
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;
}
到这儿终于调到处理的方法了:
给name和source赋值
获取了applicationArguments之后,调用prepareEnvironment方法,从名字看就是准备一个environment对象给之后用:
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.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
首先调用了getOrCreateEnvironment方法,如果有environment则返回,如果没有就创建:
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();
}
}
webApplicationType的赋值是在创建SpringApplication对象中调用 WebApplicationType.deduceFromClasspath() 方法实现的:
WebApplicationType 类:
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
由于ClassLoader都是空,所以直接返回一个WebApplicationType.SERVLET,然后由getOrCreateEnvironment方法创建一个 StandardServletEnvironment 对象
在创建 StandardServletEnvironment 时,因为没有构造函数,所以直接调用父类构造方法,而他的父类 StandardEnvironment 也没有构造方法,继续调用父类
构造方法:
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
调用了 customizePropertySources 方法,这个方法在 StandardEnvironment 和 StandardServletEnvironment 中都有实现:
StandardServletEnvironment:
@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:
@Override
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()));
}
最后的效果是里面有四个值分别是:propertySources里面有四个key:servletConfigInitParams,servletContextInitParams,
systemEnvironment,systemProperties,前两个key的value是一个object();systemProperties 的value是System.getProperties();加载了一堆系统变量
回到prepareEnvironment方法中,调用configureEnvironment方法来处理environment:
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
this.addConversionService == true,所以会调用 ApplicationConversionService.getSharedInstance();方法:
ApplicationConversionService 类:
public static ConversionService getSharedInstance() {
ApplicationConversionService sharedInstance = ApplicationConversionService.sharedInstance;
if (sharedInstance == null) {
synchronized (ApplicationConversionService.class) {
sharedInstance = ApplicationConversionService.sharedInstance;
if (sharedInstance == null) {
sharedInstance = new ApplicationConversionService();
ApplicationConversionService.sharedInstance = sharedInstance;
}
}
}
return sharedInstance;
}
他会先new 一个ApplicationConversionService当做sharedInstance并返回,所以上面的 ConversionService 的实际的对象是 ApplicationConversionService
然后把conversionService强转成 ConfigurableConversionService 并赋值给environment的setConversionService方法.因为 ConfigurableConversionService
是一个继承 ApplicationConversionService 的空接口所以可以直接强转,之后调用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));
}
}
}
environment的propertySources在构造StandardServletEnvironment是赋值,里面有四个值分别是:servletConfigInitParams,servletContextInitParams,
systemEnvironment,systemProperties,因为我们启动没有加参数,所以这个方法做任何处理,如果加入了参数会走下面的if把 COMMAND_LINE_PROPERTY_SOURCE_NAME
为key,args为value的 SimpleCommandLinePropertySource 放入composite并把composite放入sources中
我们在回到configureEnvironment中,configureProfiles方法会继续处理environment对象:
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));
}
调用了environment.getActiveProfiles()方法:
@Override
public String[] getActiveProfiles() {
return StringUtils.toStringArray(doGetActiveProfiles());
}
在getActiveProfiles方法中有调用了 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;
}
}
调用getProperty方法获取profiles:
@Override
@Nullable
public String getProperty(String key) {
return this.propertyResolver.getProperty(key);
}
我们看下propertyResolver是什么:
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
在成员变量中直接创建了 PropertySourcesPropertyResolver 的对象,传入propertySources,这个propertySources就是我们之前创建
severlet对象是赋值的propertySources,里面有四个值,但是没有ACTIVE_PROFILES_PROPERTY_NAME,所以获取的是空.
这里有个问题,ACTIVE_PROFILES_PROPERTY_NAME==spring.profiles.active,我们在spring的配置文件中会使用这个key配置读取得环境
但是现在并没有读取出来,这个配置是在后面读取的(ConfigFileApplicationListener).
回到configureProfiles方法,在获取完profiles后,把读到的东西放入environment中configurePropertySources就处理完成了.
回到prepareEnvironment方法中继续处理,接下来被调用的是listeners.environmentPrepared(environment)方法,这个方法和前面研究的
listeners.starting()方法相似,同样是调用multicastEvent方法,不过是传入的event不同:
EventPublishingRunListener 类:
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
创建了 ApplicationEnvironmentPreparedEvent 对象:
ApplicationEnvironmentPreparedEvent 类:
public ApplicationEnvironmentPreparedEvent(SpringApplication application,
String[] args, ConfigurableEnvironment environment) {
super(application, args);
this.environment = environment;
}
先调用了父类(SpringApplicationEvent)构造方法,然后把environment赋值给成员变量.
SpringApplicationEvent 类:
public SpringApplicationEvent(SpringApplication application, String[] args) {
super(application);
this.args = args;
}
调用父类(ApplicationEvent)构造方法,然后把args赋值.
ApplicationEvent 类:
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
同样调用了父类(EventObject)构造方法,然后赋值时间戳
EventObject 类:
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
赋值了source,同样的source是 SpringApplication 对象,同样的调用multicastEvent方法,同样获取Executor,同样invoke,就不在赘述了
值得注意的是,这里执行的listener是为environment赋值使用的,例如前面我们提到的读取spring.profiles.active配置,就是在
ConfigFileApplicationListener 中实现的:
在调用listener时会直接调用 ConfigFileApplicationListener 的onApplicationEvent方法
ConfigFileApplicationListener 类:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
显然event就是ApplicationEnvironmentPreparedEvent,所以会进入
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event)方法
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方法做一些处理:
List<EnvironmentPostProcessor> loadPostProcessors() {
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class,
getClass().getClassLoader());
}
调用了 SpringFactoriesLoader 的loadFactories方法,这个方法与之前对的loadFactoriesNames相似,不同的是方法返回的是具体对象:
SpringFactoriesLoader 类:
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList<>(factoryNames.size());
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
传入的classLoader是ConfigFileApplicationListener的classLoader,然后还是调用了loadFactoryNames方法获取所有需要加载的类名
这里类名有三个:
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
然后创建了这三个对象返回 onApplicationEnvironmentPreparedEvent 方法,
onApplicationEnvironmentPreparedEvent 接下来来的处理是在postProcessors添加了自己,所以现在postProcessors理由四个对象:
以上三个类的对象和SpringFactoriesLoader的对象.然后循环调用postProcessEnvironment方法做处理,其中
systemEnvironmentPropertySourceEnvironmentPostProcessor 的 postProcessEnvironment 主要是更新系统的配置,
springApplicationJsonEnvironmentPostProcessor 的 postProcessEnvironment 处理一些json相关的东西
cloudFoundryVcapEnvironmentPostProcessor 的 postProcessEnvironment 处理springcloud的东西对于我们都没有什么用,
但是 configFileApplicationListener 的 postProcessEnvironment 处理的东西对于我们相当重要!
ConfigFileApplicationListener 类:
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
首先调用了 postProcessEnvironment 处理,这个方法直接调用 addPropertySources:
protected void addPropertySources(ConfigurableEnvironment environment,
ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
从字面上看第一步加入一个什么东西到environment中,然后在load个什么东西具体加入和load的内容是什么呢?
RandomValuePropertySource 类:
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");
}
这个方法特别简单,创建了个RandomValuePropertySource的类然后放在environment中的propertySources里SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
key的后面,至于RandomValuePropertySource对象的创建,不用看了特别简单就是获取一个随机数.
经过这样的处理environment中的source里加了一个对象:RandomValuePropertySource {name='random',value=随机数}
这个东西现在还不知道是干什么用的...
加完随机数,我们要继续处理load一个什么东西:
首先创建Load对象:
Loader 类:
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(
this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader
: new DefaultResourceLoader();
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
PropertySourceLoader.class, getClass().getClassLoader());
}
把environment中的属性赋值一份放在Loader里,resourceLoader就是 DefaultResourceLoader ,获取的是当前线程的classLoader
然后propertySourceLoaders还是调用loadFactories方法获取对象,获取完的对象 有两个:
PropertiesPropertySourceLoader
YamlPropertySourceLoader
一个负责处理.properties一个负责处理.yml
然后调用load方法:
ublic 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);
}
resetEnvironmentProfiles(this.processedProfiles);
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
}
先调用 initializeProfiles 初始化,然后从profiles中取出profiles进行处理
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中添加一个null,然后调用 getProfilesActivatedViaProperty
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中的resouce获取 ACTIVE_PROFILES_PROPERTY(spring.profiles.active)和INCLUDE_PROFILES_PROPERTY(spring.profiles.include)
因为environment中没有所以直接返回一个空的set集合.
回到初始化方法initializeProfiles中,在获取activatedViaProperty后有调用this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty))
方法把activatedViaProperty先处理然后放入profiles中:
private List<Profile> getOtherActiveProfiles(Set<Profile> activatedViaProperty) {
return Arrays.stream(this.environment.getActiveProfiles()).map(Profile::new)
.filter((profile) -> !activatedViaProperty.contains(profile))
.collect(Collectors.toList());
}
调用了environment.getActiveProfiles()方法获取,这个方法在之前研究过,获取不到任何东西,所以返回依旧是null,这些操作之后profiles内的
对象依然只有一个null,之后调到了addActiveProfiles(activatedViaProperty)方法:
void addActiveProfiles(Set<Profile> profiles) {
if (profiles.isEmpty()) {
return;
}
if (this.activatedProfiles) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Profiles already activated, '" + profiles
+ "' will not be applied");
}
return;
}
this.profiles.addAll(profiles);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Activated activeProfiles "
+ StringUtils.collectionToCommaDelimitedString(profiles));
}
this.activatedProfiles = true;
removeUnprocessedDefaultProfiles();
}
因为profiles是empty的,所以直接返回,,没有记日志.
之后判断profiles的size是否等于1,进入for循环处理
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
this.profiles.add(defaultProfile);
}
首先调用了environment.getDefaultProfiles()方法:
@Override
public String[] getDefaultProfiles() {
return StringUtils.toStringArray(doGetDefaultProfiles());
}
protected Set<String> doGetDefaultProfiles() {
synchronized (this.defaultProfiles) {
if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
if (StringUtils.hasText(profiles)) {
setDefaultProfiles(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(profiles)));
}
}
return this.defaultProfiles;
}
}
protected Set<String> getReservedDefaultProfiles() {
return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
}
doGetDefaultProfiles首先判断了this.defaultProfiles.equals(getReservedDefaultProfiles(),this.defaultProfiles在成员变量里直接赋值了:
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
所以肯定是equals的,然后调用getProperty方法直接返回了environment中的propertyResolver中的spring.profiles.default为key
的value值,返回的依旧是空,然后方法直接返回defaultProfiles即default,然后for循环会创建Profile对象:
Profile 类:
Profile(String name, boolean defaultProfile) {
Assert.notNull(name, "Name must not be null");
this.name = name;
this.defaultProfile = defaultProfile;
}
把defaultProfileName储存起来,然后add加入defaultProfile对象并返回完成初始化流程.
随后load方法会取出profile进行处理,调用load方法:
load(profile, this::getPositiveProfileFilter,addToLoaded(MutablePropertySources::addLast, false));
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获取所有前缀
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> getSearchLocations(String propertyName) {
Set<String> locations = new LinkedHashSet<>();
if (this.environment.containsProperty(propertyName)) {
for (String path : asResolvedSet(
this.environment.getProperty(propertyName), null)) {
if (!path.contains("$")) {
path = StringUtils.cleanPath(path);
if (!ResourceUtils.isUrl(path)) {
path = ResourceUtils.FILE_URL_PREFIX + path;
}
}
locations.add(path);
}
}
return locations;
}
一串代码下来获取的是DEFAULT_SEARCH_LOCATIONS("classpath:/,classpath:/config/,file:./,file:./config/"),在我们之后读取配置文件时
这些前缀会帮助我们拼接地址
然后获取DEFAULT_NAMES(application),然后循环调用load
private void load(String location, String name, Profile profile,
DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
if (!StringUtils.hasText(name)) {
for (PropertySourceLoader loader : this.propertySourceLoaders) {
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile,
filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
}
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);
}
}
}
}
因为StringUtils.hasText(name)返回true,走下面的代码:
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一个个处理,propertySourceLoaders就是之前获取的yml和properties的loader
for循环中获取loader.getFileExtensions()返回{ "properties", "xml" }和{ "yml", "yaml" },然后调用loadForFileExtension
方法:
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) {
// Try profile-specific file & profile section in profile file (gh-340)
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile
+ fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// Also try the profile-specific section (if any) of the normal file
// 加载其他application-test,application-prod...没有用到的配置文件
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
private void load(PropertySourceLoader loader, String location, Profile profile,
DocumentFilter filter, DocumentConsumer consumer) {
try {
Resource resource = this.resourceLoader.getResource(location);
if (resource == null || !resource.exists()) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription(
"Skipped missing config ", location, resource, profile);
this.logger.trace(description);
}
return;
}
if (!StringUtils.hasText(
StringUtils.getFilenameExtension(resource.getFilename()))) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription(
"Skipped empty config extension ", location, resource,
profile);
this.logger.trace(description);
}
return;
}
String name = "applicationConfig: [" + location + "]";
List<Document> documents = loadDocuments(loader, name, resource);
if (CollectionUtils.isEmpty(documents)) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription(
"Skipped unloaded config ", location, resource, profile);
this.logger.trace(description);
}
return;
}
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
if (filter.match(document)) {
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
loaded.forEach((document) -> consumer.accept(profile, document));
if (this.logger.isDebugEnabled()) {
StringBuilder description = getDescription("Loaded config file ",
location, resource, profile);
this.logger.debug(description);
}
}
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load property "
+ "source from location '" + location + "'", ex);
}
}
只有在拼接成classpath:application.properties时才能真正加载上面的代码:
会走这一段代码:
String name = "applicationConfig: [" + location + "]";
List<Document> documents = loadDocuments(loader, name, resource);
if (CollectionUtils.isEmpty(documents)) {
if (this.logger.isTraceEnabled()) {
StringBuilder description = getDescription(
"Skipped unloaded config ", location, resource, profile);
this.logger.trace(description);
}
return;
}
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
if (filter.match(document)) {
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
loaded.forEach((document) -> consumer.accept(profile, document));
if (this.logger.isDebugEnabled()) {
StringBuilder description = getDescription("Loaded config file ",
location, resource, profile);
this.logger.debug(description);
}
}
调用loadDocuments方法加载配置文件:
private List<Document> loadDocuments(PropertySourceLoader loader, String name,
Resource resource) throws IOException {
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
if (documents == null) {
List<PropertySource<?>> loaded = loader.load(name, resource);
documents = asDocuments(loaded);
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}
先从cache中获取documents,如果没有就loader.load(name, resource)读取,这是初始化肯定在cache中没有数据
则调用loader.load:
@Override
public List<PropertySource<?>> load(String name, Resource resource)
throws IOException {
Map<String, ?> properties = loadProperties(resource);
if (properties.isEmpty()) {
return Collections.emptyList();
}
return Collections
.singletonList(new OriginTrackedMapPropertySource(name, properties));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<String, ?> loadProperties(Resource resource) throws IOException {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
return (Map) PropertiesLoaderUtils.loadProperties(resource);
}
return new OriginTrackedPropertiesLoader(resource).load();
}
直接使用PropertiesLoaderUtils获取了properties中所有的配置信息然后转换成document对象返回
返回到load方法继续执行
for (Document document : documents) {
if (filter.match(document)) {
addActiveProfiles(document.getActiveProfiles());
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
直接循环调用addActiveProfiles和addIncludedProfiles赋值了profiles和 include
返回最初的load:
再次循环发现第二次取回取出配置文件中填写的spring.profiles.active=xxx 中的xxx
然后接着load和loadForFileExtension
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);
}
private void addProfileToEnvironment(String profile) {
for (String activeProfile : this.environment.getActiveProfiles()) {
if (activeProfile.equals(profile)) {
return;
}
}
this.environment.addActiveProfile(profile);
}
因为现在environment中还没有activeProfile,所以调用this.environment.addActiveProfile(profile);方法把profiles放入environment中:
AbstractEnvironment 类:
@Override
public void addActiveProfile(String profile) {
if (logger.isDebugEnabled()) {
logger.debug("Activating profile '" + profile + "'");
}
validateProfile(profile);
doGetActiveProfiles();
synchronized (this.activeProfiles) {
this.activeProfiles.add(profile);
}
}
调用 doGetActiveProfiles 方法,前面知道返回空,然后把profile加入profiles中,
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) {
// Try profile-specific file & profile section in profile file (gh-340)
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile
+ fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
// Also try the profile-specific section (if any) of the normal file
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
这一次传入的profile不为空,所以会走loadForFileExtension的
if (profile != null) {
// Try profile-specific file & profile section in profile file (gh-340)
String profileSpecificFile = prefix + "-" + profile + fileExtension;
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
load(loader, profileSpecificFile, profile, profileFilter, consumer);
// Try profile specific sections in files we've already processed
for (Profile processedProfile : this.processedProfiles) {
if (processedProfile != null) {
String previouslyLoaded = prefix + "-" + processedProfile
+ fileExtension;
load(loader, previouslyLoaded, profile, profileFilter, consumer);
}
}
}
一段代码:
再次load配置文件,加载application-xxx.properties的文件,之后推出循环运行:
resetEnvironmentProfiles(this.processedProfiles);// 重新set一遍processedProfiles
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));// 再次加载
addLoadedPropertySources();
private void addLoadedPropertySources() {
MutablePropertySources destination = this.environment.getPropertySources();
List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
Collections.reverse(loaded);
String lastAdded = null;
Set<String> added = new HashSet<>();
for (MutablePropertySources sources : loaded) {
for (PropertySource<?> source : sources) {
if (added.add(source.getName())) {
addLoadedPropertySource(destination, lastAdded, source);
lastAdded = source.getName();
}
}
}
}
这段代码把loaded中的变量放入environment.getPropertySources()中,放入了两个:application-dvep.properties和 application.properties
现在environment.getPropertySources中一共有7个对象了
代码执行到这里,整个 ConfigFileApplicationListener 类已经全部完成加载,现在我们总结一下这个监听器都做了什么:
总的来说就是加载配置文件.
但是健在的流程是获取所有的loader包括properties和yml的loader,然后在加载default的配置文件名获得application和default的配置文件路径获得
classpath:/,classpath:/config/,file:./,file:./config/ 文件路径,遍历拼装配置文件路径并获取配置文件里的内容,把他们放入environment中
这点儿的代码稍微有点儿复杂,接下来我们要看的是bind的操作.
现在的问题:
配置文件中的${xxx}在什么时候会被替换,现在我暂时没有找到...