专栏首页Java3ySpringBoot 看似复杂的Environment,其实很简单

SpringBoot 看似复杂的Environment,其实很简单

作者:编程新说李新杰

喜欢写代码,讨厌配环境 我相信这十个字的小标题代表了大多数码农的心声。 十年前读大学时,学校开设了C语言还有C++。但是学习这两种语言,对于新手来说非常没有成就感。 于是我就在校门口买个光盘,装个VS(宇宙第一IDE),还有离线中文版MSDN(最牛的帮助文档),万事已俱备。 学习C#语法,看类的API,然后从WinForm(窗口)开始,用鼠标拖拽控件,设置控件属性,观察自动生成的代码,开启人生的编程之路。 大四的时候接触到Java,首先就是配置环境变量,那时觉得是一个巨复杂的东西,每次都要网上搜好一会儿才能配好。 我学习微软的东西从来就不要配什么环境变量,心里很讨厌这个Java的环境变量,这就导致十年后的今天,我依然要去网上搜如何配置,惭愧惭愧,哈哈。 后来发现,基本上软件都要配置一些环境变量,只不过有的是在安装时已经自动配好了而已,但是对于免安装版(直接解压)的则需要自己配。 我们也来尝试下抽象 熟悉Java的都知道,Java里面有两个内置的配置集合,就是System.getenv()和System.getProperties()。 它们分别是系统环境和系统属性,如下图01:

一个是Map类型,一个是Properties类型,说明它们都是一些key-value形式的值。 而且Properties类型是Java里的标准配置方式,它就对应于*.properties文件。 至此,我们已经发现两个问题: 1)配置项都是以key-value形式存在的。 2)配置项的来源是多样化的,如现在的系统环境、系统属性、配置文件等,后期还可能会有其它。 对于配置项的多来源问题,有以下两种方式解决: 1)可以把所有来源都暴露给用户,这样使用起来更加精细,但是也会带来困扰,可能用户也会迷糊到底该去哪个来源取值。 2)在所有来源前面加一个“门面”,只把它暴露出去,用户看到的只是“单一来源”,就从这里取值,其它的啥也不用知道。 Spring选择的是第二种方案,拿到key后,只需依次去每个来源中查找,这时只需规定下多个来源之间的优先级顺序即可。 整体可以用一个图形表示,如下图02:

这整体也是一种封装变化的思想,底层的多来源问题被封装起来,对用户不可见。 最终用户传过来一个key,我给返回一个和它对应的value就行了。 来来来,认识两个朋友 配置项在Java中通常叫做属性,即Property。每一个来源其实就是一个源泉,即Source。 所以在Spring中就用PropertySource类来表示一个来源,如下图03:

注意两个字段,name和source,name就是为每个源起个名字。source表示真正的资源,是能从中取出value的东西。 然后需要一个门面把多个来源封装起来,如下图04:

可以看到它里面有一个来源的列表。这就是封装。而且还是有顺序的。 根据key取值就依次遍历所有源即可,如下图05:

如果所有源中都没找到,返回null就行了。 这样配置项(或配置属性)的问题就已经解决了,很简单吧。 除了配置属性外,还有Profile 配置属性是一个很泛化的概念,说白了它就表示以非写代码的方式从外界向程序中传递特定的值。 它的好处就是修改起来很容易,只需修改下配置文件或命令行参数,然后最多重启一下就可以了。 不用修改代码,自然不用重新编译,当然也不用重新打包发布。 泛化其实就表示囊括所有的意思,但是总会有一些特殊情形,值得单独拿出来特别对待。 如每个软件都会至少经历开发、测试、上线这三个阶段,同样也会有三套环境,即开发环境、测试环境、生产环境。 这里的“环境”其实就是一个特殊情况,我们把它单独拿出来,就叫做Profile。 对不同的环境设置不同的Profile,程序中可以读取到Profile,这样程序就可以适应不同的Profile,展示不同的特性。 最终就可以一套代码打天下。就像华为的一套操作系统适应所有的终端设备。就像Java的一份字节码运行在不同的操作系统上。 在不指定Profile时,通常应该有一个默认的Profile。就像汽车默认是运行在城市道路上一样。 在Spring中,默认的Profile就叫做default。如下图06:

这个default可能没有什么意义,所以Spring提供了修改它的机会,如下图07:

可以使用图中的参数名称去指定默认的Profile,以符合自己的使用习惯。 比如对于汽车这种情况,可以这样:

spring.profiles.default=city

我们也可以为不同的环境激活不同的Profile,Spring也提供了方法,如下图08:

比如汽车上了高速,我们想狂野一下,可以激活运动模式:

spring.profiles.active=sports

最后要说的就是,这个Profile可以指定多个,用逗号分隔即可。 因为Spring是用集合存储的,所以支持多个,如下图09:

程序在判断哪些Profile被激活时,可以使用逻辑表达式,这样就更加灵活了。 支持与、或、非、括号,如下图10:

比如我们让程序运行在单节点debug模式,可以这样设置:

spring.profiles.active=standalone,debug

那么下面这些判断将都返回true:

standalone -> true

debug -> true

standalone | debug -> true

standalone & debug -> true

!other -> true

!unknown -> true

下面这些将都返回false:

!standalone -> false

!debug -> false

standalone & other -> false

debug & unknown -> false

(standalone & other) | (debug & unknown) -> false

(standalone | debug) & other & unknown -> false

:当同时出现与(&)、或(|)时,一定要使用括号。 那什么是Environment呢? 很简单,就是这个公式:

Environment = Properties + Profiles

表示Properties的接口,主要就是处理一些key-value,如下图11:

Environment继承了这个接口,又加入处理Profile的内容,如下图12:

由于要支持key-value数据类型的转换和${..}表达式的解析,所以需要能够配置,如下图13:

由于需要能够以编程的方式激活Profile或设置默认Profile,所以也需要能够配置,如下图14:

所以,这四个接口就是Spring环境的全部了。 在SpringBoot中Environment的真面目 下面是非web环境:

StandardEnvironment {activeProfiles=[], defaultProfiles=[default], 
propertySources=[
ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, 
SimpleCommandLinePropertySource {name='commandLineArgs'}, 
PropertiesPropertySource {name='systemProperties'}, 
OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, 
RandomValuePropertySource {name='random'}, 
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}, 
ResourcePropertySource {name='class path resource [mode.properties]'}, 
ResourcePropertySource {name='class path resource [greeting.properties]'}]}

可以看到配置属性有多个来源,包括命令行参数,系统属性,系统环境,随机数,yml配置文件,properties配置文件等。 以--开头的参数会出现在命令行参数这个源里,如下图15:

以-D开头的参数会出现在系统属性这个源里,如下图16:

这些源在上面的顺序就是它们的优先级,可见命令行的最高,properties文件的最低。 注意源中的第一个,即名称为configurationProperties的,主要是为了适应SpringBoot的属性名的“松散”绑定而专门用来处理属性名称的。 它并不真正提供属性值,它的值来源于除它之外的其它源。 如果不明白什么是属性名的松散绑定的,看这个示例:

user-name, user_name, userName

这三个属性名称都可以绑定到一个类的userName属性上。 下面是基于Servlet的web环境:

StandardServletEnvironment {activeProfiles=[], defaultProfiles=[default], 
propertySources=[
ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, 
SimpleCommandLinePropertySource {name='commandLineArgs'}, 
StubPropertySource {name='servletConfigInitParams'}, 
ServletContextPropertySource {name='servletContextInitParams'}, 
PropertiesPropertySource {name='systemProperties'}, 
OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, 
RandomValuePropertySource {name='random'}, 
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}, 
ResourcePropertySource {name='class path resource [mode.properties]'}, 
ResourcePropertySource {name='class path resource [greeting.properties]'}]}

和上面的唯一区别就是多了两个和web相关的源,就是ServletConfig和ServletContext。 可以从它们两个里面取出初始化参数,而且它们的优先级仅次于命令行参数。 每一个源里面其实都是key-value,内容较多,不再展示。可以自己运行下试试。 本文示例代码: https://github.com/coding-new-talking/playing-spring.git

本文分享自微信公众号 - Java3y(java3y),作者:编程新说李新杰

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 移动商城第一篇【搭建项目环境+数据模型】

    前言 本次该项目使用的技术如下: ? 这里写图片描述 搭建Oracle数据库环境 本次我们用Oracle作为我们的服务器,我们一般开发并不是把数据表放在我们练习...

    Java3y
  • Docker入门资源【整理】

    Java3y
  • SpringMVC【参数绑定、数据回显、文件上传】

    前言 本文主要讲解的知识点如下: 参数绑定 数据回显 文件上传 参数绑定 我们在Controller使用方法参数接收值,就是把web端的值给接收到Control...

    Java3y
  • Intellij如何设置编译后自动重新加载class文件?

    前段时间突然发现Intellij不能自动重新加载类了,每次编译后都要重新启动项目,才能显示更新效果,后来网上查询Intellij下如何配置热部署,都...

    程序员一一涤生
  • 什么样的程序员,才能让那些让猎头、名企HR们主动来找你

    作为一名程序员,如何更快地提高自己从而让自己向更高级别飞跃是每个人都要关心的问题。质的飞跃是一个慢慢的过程,哲学上说:大量的量变会带来质变,也就是说,程序员想要...

    一墨编程学习
  • 【Python】基于某些列删除数据框中的重复值

    Python按照某些列去重,可用drop_duplicates函数轻松处理。本文致力用简洁的语言介绍该函数。

    阿黎逸阳
  • 设计模式-组合模式(Composite)

    定义 将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性.

    高广超
  • 使用Kotlin 和 Jsoup库实现一个极简的HTML Parser库《Kotlin极简教程》正式上架:

    当我们有了一个网页的源代码HTML,这个时候我们很想像在JavaScript中的DOM API一样操作解析这个页面的元素。

    一个会写诗的程序员
  • Maven install [WARNING] The artifact aspectj:aspectjrt:jar:1.5.4 has been relocated to org.aspectj:a

      最近在给项目打包的时候,在控制台老是出现一行警告:[WARNING] The artifact aspectj:aspectjrt:jar:1.5.4 ha...

    阿豪聊干货
  • 查询ElasticSearch:用SQL代替DSL

    233酱工作中使用了一点ELK,偶尔使用Kibana拼接ES DSL简直要命。如果你和我一样「熟悉SQL,但不咋会写DSL」 or 「想要用SQL简化查询」,本...

    猿天地

扫码关注云+社区

领取腾讯云代金券