一、背景之前的项目中使用了Apollo配置中心。与Apollo配置中心对接后,即可在程序中使用配置中心的属性。那么这是如何实现的呢?配置中心的属性什么时候加载到程序中?那么如果我们弄清楚这是怎么实现的,是不是可以从任何地方加载配置属性和配置属性的加解密函数呢?2.需求从上图我们知道我们的需求很简单,就是我们自己定义的属性需要比配置文件中的属性有更高的优先级。三。分析1.什么时候给SpringBoot添加我们自己的配置属性当我们要在Bean中使用配置属性时,那么我们的配置属性必须在Bean实例化之前放入Spring和Environment中。即我们的接口需要在应用上下文刷新前被调用,而EnvironmentPostProcessor正好可以实现这个功能。2.获取配置属性的优先级我们知道在Spring中获取属性是有优先级的。比如我们有如下配置属性username├─application.properties│>>username=huan├─application-dev.properties│>>username=huan.fu那么此时username的值是多少呢?这里有一张Apollo的图片来解释这个问题。参考链接:https://www.apolloconfig.com/#/zh/design/apollo-designSpring从3.1版本开始加入了ConfigurableEnvironment和PropertySource:ConfigurableEnvironmentSpring的ApplicationContext会包含一个Environment(实现ConfigurableEnvironment接口)ConfigurableEnvironment本身包含很多PropertySourcePropertySource属性源可以理解为很多Key-Value属性配置。从上面的示意图可以看出,key在最先出现的PropertySource中具有更高的优先级。上面的例子中,SpringBoot中username的值为huan.fu。3.什么时候添加自己的配置从第二步获取配置属性的优先级可以看出,越早执行PropertySource,应该越早执行propertysource。如果我们的配置要生效,就必须尽早放置。从上图可以看出,SpringBoot通过EnvironmentPostProcessor加载各种配置,具体实现是ConfigDataEnvironmentPostProcessor。然后我们写一个EnvironmentPostProcessor的实现类,然后在ConfigDataEnvironmentPostProcessor之后执行,添加到Environment中的第一个地方。四、现实1、引入SpringBoot依赖4.0.0org.springframework.bootspring-boot-starter-parent2.6.6com.huan.springcloudspringboot-extension-point0.0.1-SNAPSHOTspringboot-extension-point1.8org.springframework.bootspring-boot-starter-web2。在application.properties中配置属性vimapplication.propertiesusername=huan。3.编写自定义属性并将其添加到SpringEnvironment。注意:1、如果发现程序中的日志没有输出,请检查是否使用slf4j输出日志。此时,由于日志系统没有初始化,无法输出日志解决方法如下:SpringBootversion>=2.4可以参考上图使用DeferredLogFactory输出日志<2.41.参考以下链接https://stackoverflow.com/questions/42839798/how-to-log-errors-in-a-environmentpostprocessor-execution2、核心代码:@ComponentpublicclassMyEnvironmentPostProcessorimplementsEnvironmentPostProcessor,ApplicationListener{privatestaticfinalDeferredLoglog=newDeferredLog();@OverridepublicvoidpostProcessEnvironment(ConfigurableEnvironmentenv,SpringApplicationapp){log.error("Thisshouldbeprinted");}@OverridepublicvoidonApplicationEvent(ApplicationEventevent){日志。replayTo(MyEnvironmentPostProcessor.class);}}4。通过SPI使自定义配置生效1.在src/main/resources下新建META-INF/spring.factories文件2.配置org.springframework.boot.env.EnvironmentPostProcessor=\com.huan.springcloud.extensionpoint.environmentpostprocessor.CustomEnvironmentPostProcessor5。写一个测试类输出定义的用户名属性的值@ComponentpublicclassPrintCustomizeEnvironmentPropertyimplementsApplicationRunner{@userValueate}"${private}"${private用户名;@Overridepublicvoidrun(ApplicationArgumentsargs){log.info("获取用户名的属性值为:{}",userName);}}六、运行结果五、注意事项1、日志无法输出。参考上面3.编写自定义属性,添加Spring环境2中提供的解决方案,配置不生效。查看EnvironmentPostProcessor的优先级,看看@Order或者Ordered返回的优先级值是否有误。看其他地方是否实现了EnvironmentPostProcessor或者ApplicationContextInitializer或者BeanFactoryPostProcessor或者BeanDefinitionRegistryPostProcessor等接口,修改PropertySource在这里面的顺序。了解Spring获取属性的顺序。参考2.获取配置属性的优先级。3.如何初始化日志系统。以下代码初始化日志系统org.springframework.boot.context.logging.LoggingApplicationListener。huan1993/spring-cloud-parent/tree/master/springboot/springboot-extension-point/src/main/java/com/huan/springcloud/extensionpoint/environmentpostprocessor七、参考链接1,https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java2,https://github.com/apolloconfig/apollo/blob/master/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/config/PropertySourcesProcessor.java3,https://www.apolloconfig.com/#/zh/design/apollo-design4,解决不能的问题在EnvironmentPostProcessor日志5中输出,https://docs.spring.io/spring-boot/docs/2.6.6/reference/htmlsingle/#howto.application.customize-the-environment-or-application-context