好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

SpringBoot扩展外部化配置的原理解析

Environment实现原理

在基于SpringBoot开发的应用中,我们常常会在 application.properties 、 application-xxx.properties 、 application.yml 、 application-xxx.yml 等配置文件中设置一些属性值,然后通过 @Value 、 @ConfigurationProperties 等注解获取,或者采用编码的方式通过 Environment 获取。

# application.properties my . config . appId = demo @RestController public class WebController {   @Value ( "${my.config.appId}" ) private String appId ;   @Autowired private Environment env ;   @Autowired private ConfigurableEnvironment environment ;   @GetMapping ( "/appInfo" ) public String appInfo () { System . out . println ( environment . getProperty ( "my.config.appId" )); System . out . println ( env . getProperty ( "my.config.appId" )); System . out . println ( appId ); System . out . println ( env == environment ); //true return appId ; } }

实际上 env 和 environment 是同一个对象,在Spring中 ConfigurableEnvironment 是 Environment 的子类,具体实现类全部是通过 implements ConfigurableEnvironment 接口来实现,所以所有可以拿到 Environment 接口地方都可以强制转换为 ConfigurableEnvironment 。

ConfigurableEnvironment 继承 Environment , Environment 继承 PropertyResolver ,主要提供了对属性获取方法, AbstractEnvironment 做为抽象类实现了 ConfigurableEnvironment 接口方法,其内部是通过 org.springframework.core.env.MutablePropertySources 来保存不同类型的属性资源。而 MutablePropertySources 内部实际上就是 List<PropertySource<?>>集合 。

public interface ConfigurableEnvironment extends Environment , ConfigurablePropertyResolver { void setActiveProfiles ( String ... profiles ); void addActiveProfile ( String profile ); void setDefaultProfiles ( String ... profiles );   //MutablePropertySources 内部实际上就是**List<PropertySource<?>>集合 MutablePropertySources getPropertySources ();   Map < String , Object > getSystemProperties (); Map < String , Object > getSystemEnvironment (); void merge ( ConfigurableEnvironment parent ); }

PropertySource 是什么呢?

其实就是一个key-value集合,key就是一个配置项,value就是配置的值。
例如: 通过 System.getProperties() 得到的系统属性就是一种类型的 PropertySource ,通过 application.yml 配置的属性是另一种属性资源。当调用 env.getProperty() 获取属性值时,会 遍历PropertySource集合 ,只要有一个 PropertySource 中有对应属性值则不再继续遍历查找, 所以在集合中越靠前的属性优先级越高 。

获取某个配置项值的访问方式,源码如下:
org.springframework.core.env.PropertySourcesPropertyResolver#getProperty(java.lang.String, java.lang.Class<T>, boolean)

protected < T > T getProperty ( String key , Class < T > targetValueType , boolean resolveNestedPlaceholders ) { if ( this . propertySources != null ) { for ( PropertySource <?> propertySource : this . propertySources ) { if ( logger . isTraceEnabled ()) { logger . trace ( "Searching for key '" + key + "' in PropertySource '" + propertySource . getName () + "'" ); } Object value = propertySource . getProperty ( key ); if ( value != null ) { if ( resolveNestedPlaceholders && value instanceof String ) { value = resolveNestedPlaceholders (( String ) value ); } logKeyFound ( key , propertySource , value ); return convertValueIfNecessary ( value , targetValueType ); } } } if ( logger . isTraceEnabled ()) { logger . trace ( "Could not find key '" + key + "' in any property source" ); } return null ; }

如何扩展自己的外部化配置?

实际上我们可以利用SpringBoot中的扩展点,拿到 ConfigurableEnvironment 对象来获取到 MutablePropertySources ,添加自己的 PropertySource 就行,例如可以访问一个http接口,获取外部化配置。

扩展接口及优先级如下

梯形缩进表示内部调用了下面的接口实现

1.org.springframework.boot.SpringApplicationRunListener#environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment)

1. ApplicationListener< org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent> EnvironmentPostProcessorApplicationListener

1. org.springframework.boot.env.EnvironmentPostProcessor 1.org.springframework.boot.context.config.ConfigDataLoader 1.org.springframework.boot.env.PropertySourceLoader 1.org.springframework.context.ApplicationContextInitializer#initialize

1.org.springframework.boot.SpringApplicationRunListener#contextPrepared 4.org.springframework.boot.context.event.ApplicationPreparedEvent 5.org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory

但是在4. BeanDefinitionRegistryPostProcessor 和5. BeanFactoryPostProcessor 中扩展时机比较晚,这个时候已经执行完包扫描,如果在这个时机添加自己的外部化配置,对于注解 @ConditionalOnProperty 可能 大部分不会生效 。

Apollo配置中心客户端和SpringBoot的整合实现

Apollo配置中心客户端是如何与SpringBoot整合的?

开源的Apollo配置中心默认启动就是通过 BeanFactoryPostProcessor 来扩展apollo上的配置到Spring的 Environment 中,
@EnableApolloConfig 注解向Spring中导入了bean com.ctrip.framework.apollo.spring.config.PropertySourcesProcessor , PropertySourcesProcessor 同时实现了 org.springframework.core.PriorityOrdered 并设置了最高的执行优先级 Ordered.HIGHEST_PRECEDENCE ,但是由于包扫描已经在 PropertySourcesProcessor 之前执行完成,所以即使设置了最高优先级,同样 无法解决在Spring执行包扫描阶段访问不到apllo上的配置问题 。

因此在SpringBoot项目中,apollo提供了另一种启动方式,使用配置项 apollo.bootstrap.enabled = true 来解决,实现类为 com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer ,其主要是通过实现第2个扩展接口 org.springframework.context.ApplicationContextInitializer 来提前将apollo的 PropertySource 添加到Spring的 Environment 中。
这样我们就可以通过 Environment 来获取到apollo中的配置项值。而 @ConditionalOnProperty 则是从 Environment 获取属性值来判断的条件是否成立,因此使用该接口扩展Environment, @ConditionalOnProperty 注解则可以在启动阶段正常访问到apollo中的配置项。

到此这篇关于SpringBoot扩展外部化配置的原理解析的文章就介绍到这了,更多相关SpringBoot扩展外部化配置内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

原文链接:https://blog.csdn.net/u013202238/article/details/114746779

查看更多关于SpringBoot扩展外部化配置的原理解析的详细内容...

  阅读:14次