十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
小编这次要给大家分享的是SpringBoot外部化配置如何使用Plus版,文章内容丰富,感兴趣的小伙伴可以来了解一下,希望大家阅读完这篇文章之后能够有所收获。
创新互联坚持“要么做到,要么别承诺”的工作理念,服务领域包括:做网站、网站设计、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的安定网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!序言
上一篇博客记录,主要集中在具体的配置内容,也就是使用 @ConfigurationProperties 这个注解来进行配置与结构化对象的绑定,虽然也顺带说了下 @Value 的使用以及其区别。
在这篇记录中,打算从总览,鸟瞰的俯视视角,来从整体上对 SpringBoot ,乃至 Spring Framework 对于外部化配置文件处理,以及配置参数的绑定操作,是如果处理的、怎么设计的。
这里其实主要说的是 SpringBoot ,虽然 @Value 属于 Spring Framework 的注解,不过在 SpringBoot 中也被频繁使用。
SpringBoot 版本: 2.2.6.RELEASE
SpringBoot启动流程简介
在 SpringBoot 的启动过程中,大体上分为三步
第一步: prepareEnvironment ,准备 SpringBoot 执行时所有的配置。
第二步: prepareContext ,根据启动时的传入的配置类,创建其 BeanDefinition 。
第三步: refreshContext ,真正启动上下文。
在这上面三步中,第一步结束后,我们所需要的或者配置文件配置的内容,大部分已经被加载进来,然后在第三步中进行配置的注入或者绑定操作。
至于为什么是大部分,后面会有解释。
将配置从配置文件加载到Environment中,使用的是事件通知的方式。
本篇博客记录仅仅聚焦第一步中如何读取配置文件的分析,顺带介绍下第三步的注入和绑定。
受限于技术水平,仅能达到这个程度
外部化配置方式
如果有看到 SpringBoot 官网关于外部化配置的说明,就会惊讶的发现,原来 SpringBoot 有那么多的配置来源。
SpringBoot 关于外部化配置特性的文档说明,直达 地址 。
而实际使用中,通常可能会使用的比较多的是通过以下这些 方式
commandLine
通过在启动jar时,加上 -DconfigKey=configValue 或者 --configKey=configValue 的方式,来进行配置,多个配置项用空格分隔。
这种使用场景也多,只是一般用于一些配置内容很少且比较关键的配置,比如说可以决定运行环境的配置。
不易进行比较多的或者配置内容比较冗长的配置,容易出错,且不便于维护管理。
application
这种是 SpringBoot 提供的,用于简便配置的一种方式,只要我们将应用程序所用到的配置,直接写到 application.properties 中,并将文件放置于以下四个位置即可 。
以上配置文件类型也都可以使用yml
默认情况下,这种方式是 SpringBoot 约定好的一种方式,文件名必须为 application ,文件内容格式可以为 Yaml 或者 Properties ,也许支持 XML ,因为看源码是支持的,没有实践。
好处就是简单,省心省事,我们只需关注文件本身的内容就可,其他的无需关心,这也是 SpringBoot 要追求的结果。
缺点也很明显,如果配置内容比较冗长,为了便于管理维护,增加可读性,必须要对配置文件进行切分,通过功能等维度进行分类分组,使用多个配置文件来进行存放配置数据。
SpringBoot 也想到了这些问题,因此提供了下面两个比较方便的使用方式,来应对这种情况
profiles
profiles 本身是也是一个配置项,它提供一种方式将部分应用程序配置进行隔离,并且使得它仅在具体某一个环境中可用。
具体实践中常用的主要是针对不同的环境,有开发环境用到的特有配置值,有测试环境特有的配置,有生产环境特有的配置,包括有些 Bean 根据环境选择决定是否进行实例化,这些都是通过 profiles 来实现的。不过这里只关注配置这一块内容。
它的使用方式通常是 spring.profiles.active=dev,dev1 或者 spring.profiles.include=db1,db2
这里可以看到有两种不同的用法,这两种方式是有区别的。
如果在 application.properties 中定义了一个 spring.profiles.active=dev ,而后在启动时通过 命令行又写了个 --spring.profiles.active=test ,那么最终使用的是 test ,而不是 dev 。
如果同样的场景下,使用 spring.profiles.include 来替换 spring.profiles.active ,那么结果会是 dev 和 test 都会存在,而不是替换的行为 。
这就是两个之间的差别,这种差别也使得他们使用的场景并不一样, active 更适合那些需要互斥的环境,而 include 则是多个并存的配置。
仅仅配置了 profiles 是没有意义的,必须要有相应的配置文件配合一起使用,而且这些配置文件的命名要符合一定的规则,否则配置文件不会被加载进 Environment 的。
profiles 文件的命名规则为 application-*.properties ,同样的, application.properties 能放置的位置它也可以,不能的,它也不可以。
propery source
注解 @PropertySource 可以写在配置类上,并且指定要读取的配置文件路径,这个路径可以是绝对路径,也可以是相对路径。
它可以有以下几种配置
其中1和2两种方式是一样的,都是从 classpath 去开始查找的
3和4是使用文件系统的绝对和相对路径的方式,这里绝对路径比较好理解 ,相对路径则是从项目的根目录作为相对目录的
5是结合 SpEL 的表达式来使用的,可以直接从环境中获取配置好的路径。
以上几种方式在实际开发中遇到和 SpringBoot 相关的配置,基本都能应付过来了。
不过对于上面配置的一些原理性的内容,还没有提到 ,下面会简单说一下 SpringBoot 关于配置更详细的处理,以及配置的优先级的问题。
原理浅入浅出
带着问题去找原因,比较有目的性和针对性,效果也相对好一些。
所以这里描述几个会引起疑问的现象
默认情况下自动加载的配置文件命名必须要是 application
在使用 application.properties 时,可以同时在四个位置放置配置,配置的优先级就是上面罗列时显示的优先级。同样的配置,优先级高的生效,优先级低的忽略。
profiles 引入的配置,也准守同样的优先级规则
命令行配置具有最高优先级
有些配置不能使用 @PropertySource 的方式进行注入,比如日志的配置。
如果一个配置类使用了 @ConfigurationProperties ,然后字段使用了 @Value , @ConfigurationProperties 先被处理, @Value 后被处理。
源码简读
SpringBoot 读取 application.properties 配置
查看 org.springframework.boot.context.config.ConfigFileApplicationListener 的源码
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered { // Note the order is from least to most specific (last one wins) // 默认检索配置文件的路径,优先级越来越高, // 可以通过 spring.config.location重新指定,要早于当前类执行时配置好 private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/"; // 默认的配置名,可以通过命令行配置--spring.config.name=xxx来重新指定 // 不通过命令行也可以通过其他方式,环境变量这些。 private static final String DEFAULT_NAMES = "application"; private class Loader { // 找到配置的路径 private SetgetSearchLocations() { if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) { return getSearchLocations(CONFIG_LOCATION_PROPERTY); } Set locations = getSearchLocations(CONFIG_ADDITIONAL_LOCATION_PROPERTY); locations.addAll( asResolvedSet(ConfigFileApplicationListener.this.searchLocations, DEFAULT_SEARCH_LOCATIONS)); return locations; } // 解析成Set private Set asResolvedSet(String value, String fallback) { List list = Arrays.asList(StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray( (value != null) ? this.environment.resolvePlaceholders(value) : fallback))); // 这里会做一个反转,也就是配置的路径中,放在后面的优先级越高 Collections.reverse(list); return new LinkedHashSet<>(list); } private Set getSearchNames() { if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) { String property = this.environment.getProperty(CONFIG_NAME_PROPERTY); return asResolvedSet(property, null); } return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES); } } }
另外有需要云服务器可以了解下创新互联建站www.cdcxhl.com,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。