点击蓝色“程序猿DD”关注我

回复“资源”获取独家整理的学习资料!

前言

上文《一文掌握 Spring Boot Profiles》 是对 Spring Boot Profiles 的介绍和使用,因此本文将从源码角度探究 Spring Boot Profiles,让我们看下 Spring Boot 底层是如何应用 Profiles 进行环境配置的隔离与生效的。

正文

首先,我们先来看下一个简单的 Spring Boot 示例程序,


在主程序方法中,打印容器中获取到 User 对象,它只有一个 name 属性。


这里 name 属性引用了外部配置 user.username 的值,它是从配置文件中读取,这里我定义两个配置文件设置该属性,application.properties 和 application-prod.properties


有了配置文件之后,启动 SimapleSpringApplication 程序,我们首先可以看到日志输入:User Bean: User(name=one),由此可以看出程序读取了 application.properties 的 user.username配置。现在我们在 application.properties 中加入一行:


再次重启启动程序,可以看到控制台如下日志:


此时 User 对象的name属性变成了 application-prod.properties 中定义的值,并且日志提示 The following profiles are active: prod 表明了名称为 prod 的Profile 在程序中激活。接下来我们就从这个日志入手,探究下这一切是如何发生的。

首先,根据 IDE 的全局查找功能,直接搜索 The following profiles are active: 这些词出现的位置,进行定位,可以找到这个日志出现于 SpringApplication#logStartupProfileInfo 方法之中。



从日志方法可以看出打印的 activeProfiles 来自上下文关联的 environment 对象,再进一步查看 logStartupProfileInfo 的调用位置,可以在 SpringApplication#prepareContext 方法之中找到,这个方法从命名上就可以看出是主要负责 Spring Boot 运行前容器上下文的预备工作,


我们重新运行程序,通过断点方式拦截 SpringApplication#prepareContext 方法的指向, 获取 environment对象真实的类型为 StandardEnvironment,是 Environment 接口非Web环境的标准实现,存储着一些应用配置和 Profiles 信息,如果在Web环境下,context 关联的就是 StandardServletEnvironment 类型的对象。


知道了日志打印来自 StandardEnvironment 对象的 activeProfiles 属性之后,就需要来看它是在什么时间被赋值的了。继续从调用链的上一级查找,就到了 SpringApplication#run(java.lang.String...),这也是整个程序启动的主要方法。


从图中可以看出第一次获取到的 environment 对象来自 SpringApplication#prepareEnvironment内部生成, prepareEnvironment 方法内部首先通过 getOrCreateEnvironment 获取一个基础的 ConfigurableEnvironment 实例,然后对该实例对象初始化配置返回。


正在创建 environment 对象来自 SpringApplication#getOrCreateEnvironment,看它的实现就可以验证我们之前提到 environment 对象类型为 StandardEnvironment。


了解完 environment 的创建,接下来就关注 environment 的初始化了,这里我们需要关注 listeners.environmentPrepared(environment) 这行代码,这里的 listeners 为 SpringApplicationRunListeners 实例,是监听器 SpringApplicationRunListener 的集合对象, SpringApplicationRunListener#environmentPrepared 方法中就是对每个 SpringApplicationRunListener 对象遍历指向类似的 environmentPrepared 方法,当前集合中只有一个 EventPublishingRunListener 实例,查看其 environmentPrepared 方法就可以看到它主要就是用于发布包含 environment 实例的 ApplicationEnvironmentPreparedEvent 事件,让其他所有监听该事件的监听器进行 environment 实例的配置。


事件对象 ApplicationEnvironmentPreparedEvent 还有一个 getEnvironment 方法获取所传递的 environment实例,我们可以通过看这个方法被使用的地方,获取有哪些类在配置 environment 对象。


经过多次的查看,从上图可以定位到 ConfigFileApplicationListener 类内的方法对 environment 对象进行扩展,从命名可以看出这个监听器跟配置文件相关,比如它的一些常量属性:CONFIG_NAME_PROPERTYCONFIG_LOCATION_PROPERTY等。从类的注释可以看出,Spring Boot 程序启动所加载的 application.properties 或 application.yml 默认从四个路径下加载,我们最常用的就是最后一种,它也可以告诉我们还可以把配置文件放在哪,如何自定义加载配置文件的路径。

  • file:./config/:

  • file:./

  • classpath:config/

  • classpath:


将程序断点设置于 ConfigFileApplicationListener#onApplicationEvent 方法之内,重新运行程序就看到程序此时运行到了 ConfigFileApplicationListener 类之中,内部经过多个方法调用从onApplicationEvent 来到了 addPropertySources 方法,这个方法就是配置文件的属性源加载到 environment 环境去的。


这里的 Loader 是 ConfigFileApplicationListener类内部私有类,用于协调属性源和配置 Profiles,我们再进一步跟踪到它的 load 方法。


我们主要看这个方法中的是三个方法:

  • Loader#initializeProfiles

  • Loader#addProfileToEnvironment

  • Loader#load(Profile, DocumentFilterFactory, DocumentConsumer)

第一个方法 initializeProfiles 初始化 Profiles,给 profiles 属性添加两个元素,null 和 默认的Profile。

第二个方法 addProfileToEnvironment 就是将 Profile 添加到 environment 对象的 activeProfiles 里,也就是最开始日志打印的 activeProfiles

第三个方法就是加载配置文件的数据源和 Profies 相关的属性。

进入 load 方法,这个方法内部通过不同配置路径去尝试执行另一个 load 方法加载配置文件,这里 name 就是配所要搜索的配置文件名称,默认为 application


由于我们的配置文件在 ClassPath 下,所以只要留意当 location 为 classpath:/ 的程序执行情况即可。


由于SpringBoot 配置文件支持xmlproperties, yml 格式,就需要不同 PropertySourceLoader 支持其文件内容的加载:PropertiesPropertySourceLoader 支持 xmlproperties 文件,YamlPropertySourceLoader 支持 yml 文件,加载以 .yml 或 .yaml 后缀的文件,Loader#loadForFileExtension 方法就完成了对这些配置文件的加载。

我们示例程序只有 properties 文件,所以只需要关注当 loader 为 PropertiesPropertySourceLoader时的 Loader#loadForFileExtension 方法的执行情况。


loadForFileExtension 内部调用另外一个加载配置文件的 load 方法,当读取到ClassPath下的application.properties 时,会执行到 Loader#loadDocuments 方法,这个方法就是把配置文件作为文档进行加载,所有键值对配置都会存在 PropertySource 之中,存储到 Document 对象中。


并且 documents 对象经过 Loader#asDocuments 方法关联上 spring.profiles.active 属性,profiles 属性添加一个定义为 prod 的 Profile,为后面的 Environment 对象添加 Profile 做准备,到这里默认的配置文件 application.properties 加载完毕了,方法又回到了 Loader#load() 上。


有了新添加的 Profile,继续进入循环,就会通过 Loader#addProfileToEnvironment 方法,为 environment 对象保存激活的 Profile,并且按照之前的逻辑,读取名为 application-prod.properties 的配置文件,命名方式可以从之前的 Loader#loadForFileExtension 的第462行就可以看出:


在 Loader#load() 方法读取了所有配置文件后,执行 Loader#addLoadedPropertySources,将对应属性源 PropertySource 存储到 environment 对象中,并且 application-prod.properties 顺序先于默认配置文件,就是为了后面程序应用相同名称配置的时候,优先采用元素位置在前的配置。


至此,所有配置文件上的数据加载完存储到了与当前上下文关联的 environment 对象中,将 prod 作为 Active Profile 激活特定环境配置的工作就完成了。

小结

虽然只是探究 Spring Boot 程序如何加载和应用 Profile,但通过这次源码分析,我们可以发现 SpringBoot 虽简单易用,但是内部实现逻辑设计是比较复杂的,无论是资源的加载,数据的解析都有专门的组件类去处理,大量使用事件通知和设计模式,在分析源码时少不了一次又一次的运行断点,不过这需要我们充分利用DE工具调试功能,在错综复杂的代码中能更准确地定位目标。

>>阿里云8月最新优惠,点击查看<<

留言交流不过瘾?添加微信:zyc_enjoy

根据指引加入各种主题讨论群

每日一问

今日问题:赌局现在到了最后决出胜负的关键时刻。

蒋老大非常幸运地赢了700个金条,现居第名。第二名的贾老大稍微落后.赢了500个金条。其余的人都已经输光了。

蒋老大犹豫着,要将手上的筹码押部分在“偶数”或“奇数”上,赢的话赌金就可以变成两倍。另边,贾老大已经把所有筹码部押在“三的倍数”上,赢的话赌金可以变成三倍,运气好的话他就可以反败为胜。

请问:蒋老大应该怎么下注才能稳操胜券呢?

(留言说说你的答案和解析吧,关注公众号,发送口令:Q20190819,核对正确答案)

昨日问答:点击>>查看<<

推荐阅读

  • 狡猾的 AI 工程师,编个故事骗走 2 亿人民币...

  • 日均7亿交易量,如何设计高可用的MySQL架构?

  • 我只是下了个订单,鬼知道我在微服务里经历了什么?

  • 四个大点,搞懂 Redis 到底快在哪里?

  • 程序员干私活搞副业?个税问题搞清楚没?

签到计划

活动介绍:自律到极致-人生才精致:第12期

活动奖励:《Java微服务测试》* 10

扫描下方二维码,参与签到

点一点“阅读原文”小惊喜在等你

源码解读 Spring Boot Profiles相关推荐

  1. springboot启动过程_spring5/springboot2源码学习 -- spring boot 应用的启动过程

    推荐阅读: Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC 疫情期间"闭关修炼",吃透这本Java核心知识,跳槽面试不 ...

  2. (附源码)spring boot养老院系统 645488

    springboot养老院系统 摘 要 随着互联网趋势的到来,各行各业都在考虑利用互联网将自己推广出去,最好方式就是建立自己的互联网系统,并对其进行维护和管理.在现实运用中,应用软件的工作规则和开发步 ...

  3. (附源码)spring boot校园购物网站 毕业设计041037

    springboot校园购物网站APP 摘要 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所 ...

  4. (附源码)spring boot跨境电商系统 毕业设计211003

    摘 要 随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身的优势:对于跨境电商系统当然也不能排除在外,随着网络技术的不断成熟,带动了跨境电商系统,它彻底改变了过去传统的管 ...

  5. (附源码)spring boot大学生综合素质测评系统 毕业设计162308

    Springboot大学生综合素质测评系统 摘要 本论文主要论述了如何使用java语言开发一个Springboot大学生综合素质测评系统,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构 ...

  6. (附源码)Spring Boot 框架整合 OAuth2 实现单点登录 SSO 详细完整源码教程!

    1.  前言 技术这东西吧,看别人写的好像很简单似的,到自己去写的时候就各种问题,"一看就会,一做就错".网上关于实现SSO的文章一大堆,但是当你真的照着写的时候就会发现根本不是那 ...

  7. (附源码)spring boot火车票订票系统 毕业设计171538

    火车票订票系统的设计与实现 摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题 ...

  8. (附源码)spring boot社区养老医疗服务平台 毕业设计041148

    springboot社区养老医疗服务平台 摘  要 随着社会的发展,社会的各行各业都在利用信息化时代的优势.计算机的优势和普及使得各种信息系统的开发成为必需. 社区养老医疗服务平台,主要的模块包括查看 ...

  9. (附源码)spring boot校园二手销售网站 毕业设计161417

    目 录 摘要 1 1 绪论 1 1.1 研究背景 1 1.2国内外研究现状 1 1.3论文结构与章节安排 1 2开发工具及相关技术介绍 技术介绍 3 2.1 MVVM模式介绍 3 2.2 B/S体系工 ...

最新文章

  1. PICRUSt2软件
  2. 遍历系统中所有的进程,可根据名字或ID查找某一个线程
  3. dede织梦调用顶级二级栏目及下三级栏目方法(数据库实现)
  4. 不用中间变量交换a和b的值?
  5. django request对象和HttpResponse对象
  6. Rsync常见错误及命令详细参数
  7. 项目管理学习总结(6)——产品经理常用的工具有哪些?
  8. sl400升级win10_联想SL400全安装WIN7(官方最完美教程).doc
  9. 获取学信网学历电子版流程
  10. error:crosses initialization of
  11. !!!python 100行代码编写【Google 图片搜索爬取工具】多线程
  12. SEO-老域名的选择
  13. 使用32驱动1602液晶屏
  14. DTL常用过滤器详解
  15. 国内最常用的坐标系大全
  16. 前台获取服务器端的值
  17. 广义似然比检验matlab,第5章 资产定价模型的时间序列估计与检验.pptx
  18. imap java 接收_javamail实现邮件接收功能IMap和pop3方式
  19. 阻容感基础10:电感器分类(4)-变压器
  20. c++练习题,动物爱吃什么

热门文章

  1. java maven项目构建ssh工程 父工程与子模块的拆分与聚合
  2. php 一句话木马简介
  3. centos7 安装 killall 命令
  4. php 输出 echo、print_r、print、var_dump 、die 区别
  5. postgresql 数据库 客户端认证
  6. python3 运算符
  7. linux tcp阻塞socket recv接收数据 未达到指定长度返回问题
  8. VS与Win7 共舞:用户界面特权隔离
  9. C++ string类型占几个字节
  10. java设计模式---状态模式