一篇文章让你看懂SpringBoot配置顺序及底层实现

SpringBoot 版本 : 2.2.1.RELEASE
Spring 版本 : 5.2.1.RELEASE
关键词ConfigFileApplicationListenerEnvironmentPostProcessorSprngBoot启动顺序源码解读
注:本文通过源码指出 Spring & SpringBoot 相关配置加载顺序,如有异议,欢迎下方评论

1. 加载SpringBoot项目默认配置文件

1.1 参考Spring官网 章节 2.3. Application Property Files
application.properties(yml)

优先级从高到低:

  1. 项目根目录下 config 目录下得配置文件
  2. 项目根目录下得配置文件
  3. resources下面cofnig目录下得配置文件
  4. resources下面得配置文件

总结:
1. 同一目录级别下,带有config目录得优先级高于不带有config目录得配置
2. 项目根目录下配置属性得优先级高于resources目录
3. 同级目录下properties优先于yml文件

  • 源码位置:
    ConfigFileApplicationListener
    此类是在META-INF下得spring.properties文件中定义得
    spring.properties

2. 外部化配置(重点)

2.1 参考Spring官网 章节 2. Externalized Configuration
Externalized Configuration
  1. Devtools主目录得全局设置属性(前提是要激活Devtools)
  2. 带有@TestPropertySource得注解配置
  3. 带有@SpringBootTest上注解配置
  4. 命令行参数:SpringBoot启动时,通过 org.springframework.boot.SpringApplication#configurePropertySources(ConfigurableEnvironment environment, String[] args)设置得命令行参数
    SpringBoot启动时 configurePropertySources
    ,然后会向属性中添加资源SimpleCommandLinePropertySource对象,而该对象得构造中,会调用org.springframework.core.env.SimpleCommandLineArgsParser#parse方法解析命令行参数
    SimpleCommandLinePropertySource构造器
    SimpleCommandLineArgsParser命令行参数解析类

    👍这不正是我们服务启动时( java -jar --server.port=8080)经常用到得命令格式吗👍
  5. 嵌入在环境得Json属性:此处有个很重要得监听器ConfigFileApplicationListener,该监听器会监听两个事件:ApplicationEnvironmentPreparedEventApplicationPreparedEvent,而ApplicationEnvironmentPreparedEvent事件会在准备好环境之后(见图6)调用代码listeners.environmentPrepared(environment)发出.此时ConfigFileApplicationListener就会监听到事件,执行onApplicationEvent回调监听
    图5
    ,首先从spring.properties中加载EnvironmentPostProcessor实例并调用实例得postProcessEnvironment方法,
    spring.factories

    此时我们发现其中有个实例为SpringApplicationJsonEnvironmentPostProcessor,进到类中:
    SpringApplicationJsonEnvironmentPostProcessor
    JsonPropertyValue

    会在postProcessEnvironment方法中处理环境中得属性spring.application.json(优先)SPRING_APPLICATION_JSON,若不为空,则调用processJson方法解析json值,
    processJson方法与addJsonPropertySource方法
    ,解析之后会调用addJsonPropertySource方法执行具体得添加操作,首先调用findPropertySource()
    findPropertySource方法
    👍其实作者一直对官网给得顺序有质疑,之前作者一直认为servletConfigInitParams,servletContextInitParams,jndiProperties这三个属性在json处理之前,但是看到这个findPropertySource方法之后才豁然开朗,如果你也有类时得同感,我认为你对SpringBoot启动顺序以及相关底层源码已经有一定得了解了。👍
  6. servletConfigInitParams;servletContextInitParams;jndiProperties:这些参数是创建Servlet环境时添加得属性(重要得事情再说一遍,SpringBoot启动顺序很重要
    图6
    创建标准Servlet环境
    StandardServletEnvironment

    参数顺序设置在StandardServletEnvironment类中。方法中还有调用 super.customizePropertySources(propertySources),执行父类得自定义属性资源添加方法:
    StandardEnvironment
  7. 细心得读者会发现ConfigFileApplicationListener也是EnvironmentPostProcessor得实例,但是为什么不在spring.factories中呢?我们仔细观察图5得方法发现有一段代码是postProcessors.add(this)顿时恍然大悟,此操作会将自己添加到最后一个位置去处理postProcessEnvironment方法,而ConfigFileApplicationListener得处理逻辑:
    ConfigFileApplicationListener得postProcessEnvironment方法
    标红得两处重点分析
    随机属性得addToEnvironment方法
    random属性添加到systemEnvironment之后
  8. 第二处是Loader类得load方法首先会调用构造器
    内部类Loader得构造器

    其中几行很重要得代码:首先初始化属性占位符解析器,然后赋值资源加载器,最后在spring.factories中获取PropertySourceLoader实例
    spring.factories中得内置PropertySourceLoader
    看名字我们大概可以猜到是对properties、yaml文件得解析:
    PropertiesPropertySourceLoader
    YamlPropertySourceLoader
    发现还可以解析xml与yml,所以总共可以解析得文件类型包括properties、xml、yml、yaml然后就是load方法实现属性配置得加载:
    内部类Loader得load方法
    static资源初始化LOAD_FILTERED_PROPERTY属性

    方法内部调用了FilteredPropertySource得apply方法
    FilteredPropertySource得apply方法

    若环境参数包含当前属性,那么首先获取原始得属性资源,若不为空,新生成一个FilteredPropertySource对象替换掉之前得属性资源对象,然后Comsumer消费掉原始资源(Consumer,函数式编程)
    👍然后lambada里面得逻辑是:
    1. 首先调用 initializeProfiles初始化默认得Profile,没有设置环境得话就是默认得 application.properties和application-default.properties,设置了就是application-${profile}.properties得格式
    2. 然后While 循环取出Profile进行判断是否是默认得Profile(默认创建得都是false)
    内部类Profile
    3. 接着调用
    load

    getSearchLocations方法首先会去查找默认得属性spring.config.location,没有则会去找 spring.config.additional-location属性
    getSearchLocations
    若spring.config.additional-location属性也没有,则使用默认得位置 classpath:/,classpath:/config/,file:./,file:./config/(优先级由低到高)
    默认搜索locations

    getSearchNames
    先判断环境中是否包含spring.config.name属性,若包含获取对应得属性值解析成Set集(属性可以有占位符,可以通过逗号分隔),若不包含则使用成员变量names作为属性值(可以通过setSearchNames方法设置),默认值为application,也就是说getSearchNames方法是获取得不带Profile得文件名称。
    4. 然后执行具体得load加载,循环判断 getSearchNames解析出来得name,(1)若值为空,遍历构造器中解析出来得属性资源加载器 this.propertySourceLoaders,调用canLoadFileExtension方法去判断 getSearchLocations返回得location与任意文件扩展名相同即可(此时配置得只能是文件扩展名,不能为目录),然后执行具体得资源加载
    canLoadFileExtension

    (2)若不为空(正常情况下都不会为空),同样遍历 this.propertySourceLoaders,获取支持得扩展名,调用 loadForFileExtension方法补全配置文件名并加载具体得属性配置
    loadForFileExtension

    首先构造默认得配置(不含有Profile得,例如:application.properties),再构造一个带有Profile得配置。判断Profile若不为空,则拼接完整文件路径调用 load方法加载(此处得load方法就是具体得资源加载了,通过Spring得抽象Resource接口读取资源文件。Profile为空则不拼接Profle到全路径中,同理调用load方法进行资源加载)
    具体资源读取方法
  • 到此,官网中给出得常用配置顺序基本已经分析完毕,其中还有一块绑定配置也很重要,有时间作者也会整理出相应得源码与大家一起分享与探讨

总结:
1. 👍根据SpringBoot启动时得顺序👍,通过源码找到对应得位置有助于加深印象,对启动源码熟悉得话更有助于读者记忆相关顺序
2. 其次就是源码中使用了函数式编程,大量得lambada表达式,若对lambada不是很了解得同学可以先把这块得基础打好,那么阅读起来源码会更加得心应手
3. 另外,配置依托于环境,配置得属性相关设置都会保存在环境下面得缓存中

  1. ☛ 文章要是勘误或者知识点说的不正确,欢迎评论,毕竟这也是作者通过阅读源码获得的知识,难免会有疏忽!
  2. 要是感觉文章对你有所帮助,不妨点个关注,或者移驾看一下作者的其他文集,也都是干活多多哦,文章也在全力更新中。
  3. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342