生活随笔
收集整理的這篇文章主要介紹了
spring boot实战(第六篇)加载application资源文件源码分析
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
前言
在上一篇中了解了spring配置資源的加載過(guò)程,本篇在此基礎(chǔ)上學(xué)習(xí)spring boot如何默認(rèn)加載application.xml等文件信息的。
?
?
ConfigFileApplicationListener
在spring boot實(shí)戰(zhàn)(第三篇)事件監(jiān)聽(tīng)源碼分析中可知在構(gòu)造SpringApplication時(shí)加載相關(guān)的監(jiān)聽(tīng)器,其中存在一個(gè)監(jiān)聽(tīng)器ConfigFileApplicationListener,其定義如下:
[html]?view plain?copy
public?class?ConfigFileApplicationListener?implements??????????ApplicationListener<ApplicationEvent>,?Ordered?{??@Override??????public?void?onApplicationEvent(ApplicationEvent?event)?{??????????if?(event?instanceof?ApplicationEnvironmentPreparedEvent)?{??????????????onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)?event);??????????}??????????if?(event?instanceof?ApplicationPreparedEvent)?{??????????????onApplicationPreparedEvent((ApplicationPreparedEvent)?event);??????????}??????}?????}??
監(jiān)聽(tīng)ApplicationEvent事件,在觸發(fā)所有其子類(lèi)以及本身事件時(shí)會(huì)執(zhí)行其onApplicationEvent方法。在執(zhí)行
[html]?view plain?copy
for?(SpringApplicationRunListener?runListener?:?runListeners)?{??????runListener.environmentPrepared(environment);??}??
時(shí)會(huì)觸發(fā)到
[html]?view plain?copy
if?(event?instanceof?ApplicationEnvironmentPreparedEvent)?{??????????????onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)?event);??????????}??
中;
?
[html]?view plain?copy
private?void?onApplicationEnvironmentPreparedEvent(??????????????ApplicationEnvironmentPreparedEvent?event)?{??????????Environment?environment?=?event.getEnvironment();??????????if?(environment?instanceof?ConfigurableEnvironment)?{??????????????onApplicationEnvironmentPreparedEvent((ConfigurableEnvironment)?environment,??????????????????????event.getSpringApplication());??????????}??????}??
?
在上一篇中可以知道enviroment為StandardServletEnvironment實(shí)例,因此執(zhí)行onApplicationEnvironmentPreparedEvent方法
?
[html]?view plain?copy
private?void?onApplicationEnvironmentPreparedEvent(??????????????ConfigurableEnvironment?environment,?SpringApplication?application)?{??????????addPropertySources(environment,?application.getResourceLoader());??????????bindToSpringApplication(environment,?application);??????}??
首先來(lái)看addPropertySources相關(guān)信息
?
[html]?view plain?copy
protected?void?addPropertySources(ConfigurableEnvironment?environment,??????????????ResourceLoader?resourceLoader)?{??????????RandomValuePropertySource.addToEnvironment(environment);??????????try?{??????????????new?Loader(environment,?resourceLoader).load();??????????}??????????catch?(IOException?ex)?{??????????????throw?new?IllegalStateException("Unable?to?load?configuration?files",?ex);??????????}??????}??
?
RandomValuePropertySource.addToEnvironment(environment)將隨機(jī)方法放入到PropertySources中
?
[html]?view plain?copy
public?static?void?addToEnvironment(ConfigurableEnvironment?environment)?{??????????environment.getPropertySources().addAfter(??????????????????StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,??????????????????new?RandomValuePropertySource("random"));??????????logger.trace("RandomValuePropertySource?add?to?Environment");??????}??
如何從Random中獲取值是需要看getProperty方法:
[html]?view plain?copy
public?Object?getProperty(String?name)?{??????????if?(!name.startsWith("random."))?{??????????????return?null;??????????}??????????if?(logger.isTraceEnabled())?{??????????????logger.trace("Generating?random?property?for?'"?+?name?+?"'");??????????}??????????if?(name.endsWith("int"))?{??????????????return?getSource().nextInt();??????????}??????????if?(name.startsWith("random.long"))?{??????????????return?getSource().nextLong();??????????}??????????if?(name.startsWith("random.int")?&&?name.length()?>?"random.int".length()?+?1)?{??????????????String?range?=?name.substring("random.int".length()?+?1);??????????????range?=?range.substring(0,?range.length()?-?1);??????????????return?getNextInRange(range);??????????}??????????byte[]?bytes?=?new?byte[32];??????????getSource().nextBytes(bytes);??????????return?DigestUtils.md5DigestAsHex(bytes);??????}??
其中的getSource()表示Random類(lèi)。
接下來(lái)看
[html]?view plain?copy
new?Loader(environment,?resourceLoader).load()??
看load方法
[html]?view plain?copy
public?void?load()?throws?IOException?{??????????????...//處理profiles信息????????????????????????????while?(!this.profiles.isEmpty())?{??????????????????String?profile?=?this.profiles.poll();??????????????????for?(String?location?:?getSearchLocations())?{??????????????????????if?(!location.endsWith("/"))?{??????????????????????????//?location?is?a?filename?already,?so?don't?search?for?more??????????????????????????//?filenames??????????????????????????load(location,?null,?profile);??????????????????????}??????????????????????else?{??????????????????????????for?(String?name?:?getSearchNames())?{??????????????????????????????load(location,?name,?profile);??????????????????????????}??????????????????????}??????????????????}??????????????}????????????????addConfigurationProperties(this.propertiesLoader.getPropertySources());??????????}??
看getSearchLocations()方法
?
[html]?view plain?copy
private?Set<String>?getSearchLocations()?{??????????????Set<String>?locations?=?new?LinkedHashSet<String>();??????????????//?User-configured?settings?take?precedence,?so?we?do?them?first??????????????if?(this.environment.containsProperty(CONFIG_LOCATION_PROPERTY))?{??????????????????for?(String?path?:?asResolvedSet(??????????????????????????this.environment.getProperty(CONFIG_LOCATION_PROPERTY),?null))?{??????????????????????if?(!path.contains("$"))?{??????????????????????????if?(!path.contains(":"))?{??????????????????????????????path?=?"file:"?+?path;??????????????????????????}??????????????????????????path?=?StringUtils.cleanPath(path);??????????????????????}??????????????????????locations.add(path);??????????????????}??????????????}??????????????locations.addAll(asResolvedSet(??????????????????????ConfigFileApplicationListener.this.searchLocations,??????????????????????DEFAULT_SEARCH_LOCATIONS));??????????????return?locations;??????????}??
首先看CONFIG_LOCATION_PROPERTY(spring.config.location)是否存在配置,無(wú)則走默認(rèn)配置路徑DEFAULT_SEARCH_LOCATIONS(classpath:/,classpath:/config/,file:./,file:./config/)
?
繼續(xù)來(lái)看getSearchNames()方法
?
[html]?view plain?copy
private?Set<String>?getSearchNames()?{??????????if?(this.environment.containsProperty(CONFIG_NAME_PROPERTY))?{??????????????return?asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY),??????????????????????null);??????????}??????????return?asResolvedSet(ConfigFileApplicationListener.this.names,?DEFAULT_NAMES);??????}??
優(yōu)先看CONFIG_NAME_PROPERTY(spring.config.name)配置,否則走DEFAULT_NAMES(application)
?
解析完路徑和配置文件名以后,將開(kāi)始判斷路徑+名稱(chēng)組合是否存在 ?執(zhí)行l(wèi)oad(...)方法
[html]?view plain?copy
private?void?load(String?location,?String?name,?String?profile)??????????????????throws?IOException?{??????????????String?group?=?"profile="?+?(profile?==?null???""?:?profile);??????????????if?(!StringUtils.hasText(name))?{??????????????????//?Try?to?load?directly?from?the?location??????????????????loadIntoGroup(group,?location,?profile);??????????????}??????????????else?{??????????????????//?Search?for?a?file?with?the?given?name??????????????????for?(String?ext?:?this.propertiesLoader.getAllFileExtensions())?{??????????????????????if?(profile?!=?null)?{??????????????????????????//?Try?the?profile?specific?file??????????????????????????loadIntoGroup(group,?location?+?name?+?"-"?+?profile?+?"."?+?ext,??????????????????????????????????null);??????????????????????????//?Sometimes?people?put?"spring.profiles:?dev"?in??????????????????????????//?application-dev.yml?(gh-340).?Arguably?we?should?try?and?error??????????????????????????//?out?on?that,?but?we?can?be?kind?and?load?it?anyway.??????????????????????????loadIntoGroup(group,?location?+?name?+?"-"?+?profile?+?"."?+?ext,??????????????????????????????????profile);??????????????????????}??????????????????????//?Also?try?the?profile?specific?section?(if?any)?of?the?normal?file??????????????????????loadIntoGroup(group,?location?+?name?+?"."?+?ext,?profile);??????????????????}??????????????}??????????}??
this.propertiesLoader.getAllFileExtensions()方法獲取文件后綴
?
[html]?view plain?copy
public?Set<String>?getAllFileExtensions()?{??????????Set<String>?fileExtensions?=?new?HashSet<String>();??????????for?(PropertySourceLoader?loader?:?this.loaders)?{??????????????fileExtensions.addAll(Arrays.asList(loader.getFileExtensions()));??????????}??????????return?fileExtensions;??????}??
loader.getFileExtensions()獲取所有支持的文件后綴,其中l(wèi)oader在執(zhí)行l(wèi)oad方法時(shí)實(shí)例化
?
[html]?view plain?copy
public?void?load()?throws?IOException?{??????????????this.propertiesLoader?=?new?PropertySourcesLoader();??...}??
調(diào)用其構(gòu)造方法
[html]?view plain?copy
public?PropertySourcesLoader(MutablePropertySources?propertySources)?{??????Assert.notNull(propertySources,?"PropertySources?must?not?be?null");??????this.propertySources?=?propertySources;??????this.loaders?=?SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,??????????????null);??}??
可以看出this.loaders是由SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,null)得到
?
[html]?view plain?copy
public?static?<T>?List<T>?loadFactories(Class<T>?factoryClass,?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<T>(factoryNames.size());??????for?(String?factoryName?:?factoryNames)?{??????????result.add(instantiateFactory(factoryName,?factoryClass,?classLoaderToUse));??????}??????AnnotationAwareOrderComparator.sort(result);??????return?result;??}??
加載META-INF/spring.factories文件下對(duì)應(yīng)內(nèi)容
[html]?view plain?copy
#?PropertySource?Loaders??org.springframework.boot.env.PropertySourceLoader=\??org.springframework.boot.env.PropertiesPropertySourceLoader,\??org.springframework.boot.env.YamlPropertySourceLoader??
?
因此加載了PropertiesPropertySourceLoader以及YamlPropertySourceLoader類(lèi)實(shí)例;
- PropertiesPropertySourceLoader 支持文件后綴格式 "properties","xml"?
[html]?view plain?copy
@Override??????public?String[]?getFileExtensions()?{??????????return?new?String[]?{?"properties",?"xml"?};??????}??
- YamlPropertySourceLoader 支持文件后綴格式?"yml","yaml"
[html]?view plain?copy
@Override??????public?String[]?getFileExtensions()?{??????????return?new?String[]?{?"yml",?"yaml"?};??????}??
兩者覆寫(xiě)的load方法實(shí)現(xiàn)如何處理資源為PropertySource對(duì)象。
?
獲取完文件后綴后調(diào)用loadIntoGroup方法將資源信息轉(zhuǎn)化為PropertySource,其實(shí)質(zhì)為調(diào)用PropertySourcesLoader中l(wèi)oad方法
[html]?view plain?copy
private?PropertySource<?>?loadIntoGroup(String?identifier,?String?location,??????????????????String?profile)?throws?IOException?{??????????????Resource?resource?=?this.resourceLoader.getResource(location);??????????????PropertySource<?>?propertySource?=?null;??????????????if?(resource?!=?null)?{??????????????????String?name?=?"applicationConfig:?["?+?location?+?"]";??????????????????String?group?=?"applicationConfig:?["?+?identifier?+?"]";??????????????????propertySource?=?this.propertiesLoader.load(resource,?group,?name,??????????????????????????profile);??????????????????if?(propertySource?!=?null)?{??????????????????????maybeActivateProfiles(propertySource??????????????????????????????.getProperty(ACTIVE_PROFILES_PROPERTY));??????????????????????addIncludeProfiles(propertySource??????????????????????????????.getProperty(INCLUDE_PROFILES_PROPERTY));??????????????????}??????????????}??????????????StringBuilder?msg?=?new?StringBuilder();??????????????msg.append(propertySource?==?null???"Skipped?"?:?"Loaded?");??????????????msg.append("config?file?");??????????????msg.append("'").append(location).append("'");??????????????if?(StringUtils.hasLength(profile))?{??????????????????msg.append("?for?profile"?+?profile);??????????????}??????????????if?(resource?==?null?||?!resource.exists())?{??????????????????msg.append("?resource?not?found");??????????????}??????????????this.debug.add(msg);??????????????return?propertySource;??????????}??
最后調(diào)用addConfigurationProperties(this.propertiesLoader.getPropertySources())方法將解析過(guò)后的資源信息放置進(jìn)Enviroment中propertySources屬性集合中
[html]?view plain?copy
private?void?addConfigurationProperties(MutablePropertySources?sources)?{??????????????List<PropertySource<?>>?reorderedSources?=?new?ArrayList<PropertySource<?>>();??????????????for?(PropertySource<?>?item?:?sources)?{??????????????????reorderedSources.add(item);??????????????}??????????????//?Maybe?we?should?add?before?the?DEFAULT_PROPERTIES?if?it?exists???????????????this.environment.getPropertySources().addLast(??????????????????????new?ConfigurationPropertySources(reorderedSources));??????????}??
至此 application.xml等文件的加載分析結(jié)束。
?
時(shí)序圖
簡(jiǎn)單的畫(huà)了一下時(shí)序圖,可能和實(shí)際調(diào)用存在出入,僅作參考使用?
?
總結(jié)
以上是生活随笔為你收集整理的spring boot实战(第六篇)加载application资源文件源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。