springboot获取复杂的list配置文件

springboot获取复杂的list配置文件,第1张

首先,在SpringBoot中,有两种配置文件的方式。一种是applicationproperties,另一种applicationyaml(或者是applicationyml)。

yaml文件格式是SpringBoot支持的一种JSON超集文件格式,相对于传统的Properties配置文件,yaml文件以数据为核心,是一种更为直观且容易被计算机识别的数据序列化格式。applicationyaml配置文件的工作原理和applicationproperties是一样的,只是yaml格式配置文件看起来要跟简洁一些。

applicationyaml文件使用 key:(空格) value 格式配置属性,使用缩进控制层关系

注意:此时port和path属性,属于同一层级

其中缩进式写法有两种表示形式,一种为:

另一种为:

上述两种缩进式写法为person对象的hobby属性赋值,其中一种是通过“-(空格)属性值”的形式为属性赋值,另一种是直接赋值使用英文逗号分隔属性值。

行内式的写法显然比缩进式写法更加简洁。使用行内式写法设置属性值时,中括号“[ ]”是可以省略的,程序会自动匹配校对属性的值

在yaml配置的属性值为Map或对象类型时,缩进式的形式按照yaml文件格式编写即可,而行内式写法的属性值要用大括号“{ }”包含

在了解 Spring Boot 的启动流程的时候,我们先看一下一个Spring Boot 应用是如何启动的,如下是一个简单的 SpringBoot 程序,非常的简洁,他是如何做到的呢,我们接下来就将一步步分解。

我们追踪 SpringApplicationrun() 方法,其实最终它主要的逻辑是新建一个 SpringApplication ,然后调用他的 run 方法,如下:

我们先来看一下创建 SpringApplication 的方法:

在将Main class 设置 primarySources 后,调用了 WebApplicationTypededuceFromClasspath() 方法,该方法是为了检查当前的应用类型,并设置给 webApplicationType 。 我们进入 deduceFromClasspath 方法 :

这里主要是通过类加载器判断是否存在 REACTIVE 相关的类信息,假如有就代表是一个 REACTIVE 的应用,假如不是就检查是否存在 Servelt 和 ConfigurableWebApplicationContext ,假如都没有,就代表应用为非 WEB 类应用,返回 NONE ,默认返回 SERVLET 类型,我们这期以我们目前最常使用的 SERVLET 类型进行讲解,所以我们在应用中引入了 spring-boot-starter-web 作为依赖:

他会包含 Spring-mvc 的依赖,所以就包含了内嵌 tomcat 中的 Servlet 和 Spring-web 中的 ConfigurableWebApplicationContext ,因此返回了 SERVLET 类型。

回到刚才创建 SpringApplication 的构建方法中,我们设置完成应用类型后,就寻找所有的 Initializer 实现类,并设置到 SpringApplication 的 Initializers 中,这里先说一下 getSpringFactoriesInstances 方法,我们知道在我们使用 SpringBoot 程序中,会经常在 META-INF/springfactories 目录下看到一些 EnableAutoConfiguration ,来出发 config 类注入到容器中,我们知道一般一个 config 类要想被 SpringBoot 扫描到需要使用 @CompnentScan 来扫描具体的路径,对于 jar 包来说这无疑是非常不方便的,所以 SpringBoot 提供了另外一种方式来实现,就是使用 springfactories ,比如下面这个,我们从 Springboot-test 中找到的例子,这里先定义了一个ExampleAutoConfiguration,并加上了 Configuration 注解:

然后在 springfactories 中定义如下:

那这种方式是怎么实现的你,这就要回到我们刚才的方法 getSpringFactoriesInstances :

我们先来看一下传入参数,这里需要注意的是 args,这个是初始化对应 type 的时候传入的构造参数,我们先看一下 SpringFactoriesLoader#loadFactoryNames 方法:

首先是会先检查缓存,假如缓存中存在就直接返回,假如没有就调用 classLoader#getResources 方法,传入 META-INF/springfactories ,即获取所有 jar 包下的对应文件,并封装成 UrlResource ,然后使用 PropertiesLoaderUtils 将这些信息读取成一个对一对的 properties,我们观察一下 springfactories 都是按 properties 格式排版的,假如有多个就用逗号隔开,所以这里还需要将逗号的多个类分隔开来,并加到 result 中,由于 result 是一个 LinkedMultiValueMap 类型,支持多个值插入,最后放回缓存中。最终完成加载 META-INF/springfactories 中的配置,如下:

我们可以看一下我们找到的 initializer 有多少个:

在获取到所有的 Initializer 后接下来是调用 createSpringFactoriesInstances 方法进行初始化。

这里的 names 就是我们上面通过类加载器加载到的类名,到这里会先通过反射生成 class 对象,然后判断该类是否继承与 ApplicationContextInitializer ,最后通过发射的方式获取这个类的构造方法,并调用该构造方法,传入已经定义好的构造参数,对于 ApplicationContextInitializer 是无参的构造方法,然后初始化实例并返回,回到原来的方法,这里会先对所有的 ApplicationContextInitializer 进行排序,调用 AnnotationAwareOrderComparator#sort(instances) 方法,这里就是根据 @Order 中的顺序进行排序。

接下来是设置 ApplicationListener ,我们跟进去就会发现这里和上面获取 ApplicationContextInitializer 的方法如出一辙,最终会加载到如图的 15 个 listener (这里除了 EnableEncryptablePropertiesBeanFactoryPostProcessor 外,其他都是 SpringBoot 内部的 Listener):

在完成 SpringApplication 对象的初始化后,我们进入了他的 run 方法,这个方法几乎涵盖了 SpringBoot 生命周期的所有内容,主要分为九个步骤,每一个步骤这里都使用注解进行标识:

主要步骤如下:

第一步:获取 SpringApplicationRunListener, 然后调用他的 staring 方法启动监听器。

第二步:根据 SpringApplicationRunListeners以及参数来准备环境。

第三步:创建 Spring 容器。

第四步:Spring 容器的前置处理。

第五步:刷新 Spring 容器。

第六步: Spring 容器的后置处理器。

第七步:通知所有 listener 结束启动。

第八步:调用所有 runner 的 run 方法。

第九步:通知所有 listener running 事件。

我们接下来一一讲解这些内容。

我们首先看一下第一步,获取 SpringApplicationRunListener :

这里和上面获取 initializer 和 listener 的方式基本一致,都是通过 getSpringFactoriesInstances , 最终只找到一个类就是: orgspringframeworkbootcontexteventEventPublishingRunListener ,然后调用其构造方法并传入产生 args , 和 SpringApplication 本身:

我们先看一下构造函数,首先将我们获取到的 ApplicationListener 集合添加到initialMulticaster 中, 最后都是通过 *** 作 SimpleApplicationEventMulticaster 来进行广播,我,他继承于 AbstractApplicationEventMulticaster ,我们先看一下他的 addApplicationListener 方法:

我们可以看出,最后是放到了 applicationListenters 这个容器中。他是 defaultRetriever 的成员属性, defaultRetriever 则是 AbstractApplicationEventMulticaster 的私有类,我们简单看一下这个类:

我们只需要看一下这里的 getApplicationListeners 方法,它主要是到 beanFactory 中检查是否存在多的 ApplicationListener 和旧的 applicationListeners 组合并返回,接着执行 listener 的 start 方法,最后也是调用了 AbstractApplicationEventMulticaster 的 multicastEvent 查找支持对应的 ApplicationEvent 类型的通知的 ApplicationListener 的 onApplicationEvent 方法 ,这里除了会:

筛选的方法如下,都是调用了对应类型的 supportsEventType 方法 :

如图,我们可以看到对 orgspringframeworkbootcontexteventApplicationStartingEvent 感兴趣的有5个 Listener

环境准备的具体方法如下:

首先是调用 getOrCreateEnvironment 方法来创建 environment ,我们跟进去可以发现这里是根据我们上面设置的环境的类型来进行选择的,当前环境会创建 StandardServletEnvironment

我们先来看一下 StandardServletEnvironment 的类继承关系图,我们可以看出他是继承了 AbstractEnvironment :

他会调用子类的 customizePropertySources 方法实现,首先是 StandardServletEnvironment 的实现如下,他会添加 servletConfigInitParams , servletContextInitParams , jndiProperties 三种 properties,当前调试环境没有配置 jndi properties,所以这里不会添加。接着调用父类的 customizePropertySources 方法,即调用到了 StandardEnvironment 。

我们看一下 StandardEnvironment#customizePropertySources 方法,与上面的三个 properties 创建不同,这两个是会进行赋值的,包括系统环境变量放入 systemEnvironment 中,jvm 先关参数放到 systemProperties 中:

这里会添加 systemEnvironment 和 systemProperties 这两个 properties,最终拿到的 properties 数量如下 4个:

在创建完成 Environment 后,接下来就到了调用 configureEnvironment 方法:

我们先看一下 configurePropertySources 方法,这里主要分两部分,首先是查询当前是否存在 defaultProperties ,假如不为空就会添加到 environment 的 propertySources 中,接着是处理命令行参数,将命令行参数作为一个 CompositePropertySource 或则 SimpleCommandLinePropertySource 添加到 environment 的 propertySources 里面,

接着调用 ConfigurationPropertySources#attach 方法,他会先去 environment 中查找 configurationProperties , 假如寻找到了,先检查 configurationProperties 和当前 environment 是否匹配,假如不相等,就先去除,最后添加 configurationProperties 并将其 sources 属性设置进去。

回到我们的 prepareEnvironment 逻辑,下一步是通知观察者,发送 ApplicationEnvironmentPreparedEvent 事件,调用的是 SpringApplicationRunListeners#environmentPrepared 方法,最终回到了 SimpleApplicationEventMulticaster#multicastEvent 方法,我们通过 debug 找到最后对这个时间感兴趣的 Listener 如下:

其主要逻辑如下:

这个方法最后加载了 PropertySourceLoader , 这里主要是两种,一个是用于 Properties 的,一个是用于 YAML 的如下:

其中 apply 方法主要是加载 defaultProperties ,假如已经存在,就进行替换,而替换的目标 PropertySource 就是 load 这里最后的一个 consumer 函数加载出来的,这里列一下主要做的事情:

1、加载系统中设置的所有的 Profile 。

2、遍历所有的 Profile ,假如是默认的 Profile , 就将这个 Profile 加到 environment 中。

3、调用load 方法,加载配置,我们深入看一下这个方法:

他会先调用 getSearchLocations 方法,加载所有的需要加载的路径,最终有如下路径:

其核心方法是遍历所有的 propertySourceLoader ,也就是上面加载到两种 propertySourceLoader ,最红 loadForFileExtension 方法,加载配置文件,这里就不展开分析了,说一下主要的作用,因为每个 propertySourceLoader 都有自己可以加载的扩展名,默认扩展名有如下四个 properties, xml, yml, yaml,所以最终拿到文件名字,然后通过 - 拼接所有的真实的名字,然后加上路径一起加载。

接下来,我们分析 BackgroundPreinitializer ,这个方法在接收 ApplicationPrepareEnvironment 事件的时候真正调用了这份方法:

1、 ConversionServiceInitializer 主要负责将包括 日期,货币等一些默认的转换器注册到 formatterRegistry 中。

2、 ValidationInitializer 创建 validation 的匹配器。

3、 MessageConverterInitializer 主要是添加了一些 >

  Spring Boot 官方 提供了两种常用的配置文件格式,分别是 properties 、 YML 格式。相比于 properties 来说, YML 更加年轻,层级也是更加分明。 强烈推荐使用 YML 格式

  Spring Boot项目 启动会扫描以下位置的 applicationproperties 或者 applicationyml 作为默认的配置文件

徒手撕源码

内部类Loader的load方法

getSearchLocations()方法

asResolvedSet()

下面给出优先级 从高到低 的配置文件排列顺序:

以设置应用端口为例 初体验Spring Boot配置文件

properties后缀结尾(applicationproperties)

yml/yaml后缀结尾(applicationyml/applicationyaml)

数字,字符串,布尔,日期

对象、Map

数组

数字,字符串,布尔,日期

对象、Map

数组

@ConfigurationProperties(prefix = "person")详解

标注在类上

标注在方法上

综上所述

  @ConfigurationProperties 注解能够轻松的让配置文件跟实体类绑定在一起。

 值得关注的是: @ConfigurationProperties 这个注解仅仅是支持从 Spring Boot的默认配置文件 中取值,也就是 applicationproperties 、 applicationyml 、 applicationyaml ,那我们如何从自定义配置文件取值呢???

 别着急,有解决办法,那就是再加一个注解: @PropertySource(value = "classpath:custom-profileproperties") ,下面会有对 @PropertySource 注解的介绍。请耐心往下面看。

使用@PropertySource注解

对应配置文件

创建两个配置文件 custom-profileyml、custom-profile1yml ,如下去引入。

我们可以通过控制变量法进行测试,具体过程我这里就不赘述了。

直接说 结论 吧: Spring加载顺序 为 从左到右顺序加载 ,后加载的会 覆盖 先加载的属性值。

另外需要注意的是 : @PropertySource 默认加载 xxxproperties类型 的配置文件,不能加载 YML格式 的配置文件。如何解决呢?下面来解决这一问题

对应配置文件:

编写PropertiesController

扩展功能

applicationyml 主配置文件

application-devyml 开发配置文件

application-prodyml 生产配置文件

application-testyml 测试配置文件

(1)主配置文件:配置激活选项

(2)其他配置文件:指定属于哪个环境(同yml,只不过表现形式是 key=value 的,三个配置文件分别是: application-devproperties , application-prodproperties , application-testproperties )

 无论是使用上述 多文档块 的方式,还是新建 application-testyml 文件,都可以在配置文件中指定 springprofilesactive=test 激活指定的profile。

感谢阅读小生文章。祝大家早日富可敌国,实现财富自由。

写文不易 ,一定要 点赞、评论、收藏哦 , 感谢感谢感谢!!!

springboot 本身支持多种灵活的配置方式,为开发 springboot 程序带来了很大的灵活性和扩展性,但是同时由于太灵活,经常会导致明明配置了相关属性,却没有生效。

本文总结了 springboot 配置文件的原理以及多个配置文件生效的顺序。

springboot 配置文件支持灵活的路径,以及灵活的文件名,用一个变量表达式总结如下:

部分源码如下:

当满足上述变量表达式的配置文件有多个时,会有一个配置的优先级。假设

上面每个条件组合起来,则最多有配置文件如下,且顺序从上到下:

获取属性时,按从上到下的顺序遍历由上述文件生成的属性资源对象 PropertySource ,如果遇到匹配的key直接返回。

总结一下:就是如果同一个key的属性只出现一次,则直接取该值即可。如果同一个key的属性出现多次,则取顺序靠前的属性资源对象。另外其中每个文件都是可选的。

需要注意的一点是:如果在同一个 location 下配置了多个文件名一样的文件,则只会取一个,比如在 classpath:/ ,有如下两个文件 applicationyml :

则只会根据 classloader 的 classpath 列表,选取第一个出现的文件。因为 springboot 加载配置文件时最底层是使用的下面的方法:

这两个方法只会获取 classloader 类的 ucp 属性里面第一个匹配到的值。如果对 springboot 自身的机制不满意,想获取所有的classpath:/路径下面的 applicaitonyml 文件,可以使用下面的方法:

本文总结了 springboot 配置文件的原理以及多个配置文件生效的顺序。如果存在增加了配置文件或者在配置文件里面增加了属性却没有生效,可以参考上面的 springboot 配置文件表达式和配置文件生效顺序进行排查。

后面还会有一篇文章讨论基于 springboot 配置原理如何实现自定义的配置读取方式。

以上就是关于springboot获取复杂的list配置文件全部的内容,包括:springboot获取复杂的list配置文件、二、springboot配置文件、SpringBoot中yaml文件配置属性等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/web/9747685.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-01
下一篇2023-05-01

发表评论

登录后才能评论

评论列表(0条)

    保存