美文网首页java jvmJava技术升华java技术分享
如何在旧项目中实现从配置中心加载配置

如何在旧项目中实现从配置中心加载配置

作者: Chandler_珏瑜 | 来源:发表于2018-03-29 11:19 被阅读267次

前言

    spring cloud相关组件很强大,可以轻松实现配置中心。但是会遇到一个很实际的问题:spring cloud config所依赖的spring相关组件包版本比较高,而公司中很多重要的工程都是以前开发的,不是spring boot工程,而且spring版本可能为3.X,如何解决这个问题呢?

配置中心

如何获取配置中心的配置信息

配置信息调用测试

    先来看一下如何在配置中心访问,如上图所示,通过URL路径来获取对应的配置文件。

    ▪️/{application}/{profile}[/{label}]

    ▪️/{application}-{profile}.yml

    ▪️/{application}-{profile}.properties

    ▪️/{label}/{application}-{profile}.properties

    上面的url会映射{application}-{profile}.properties对应的配置文件,其中{label}对应Git上不同的分支,默认为master。我们可以尝试构造不同的url来访问不同的配置内容,比如要访问kyle分支,didispace应用的prod不同的配置内容,比如,要访问kyle分支,didispace应用的prod环境,就可以访问这个url:http://localhost:8888/didispace/prod/kyle,并获得如下返回信息。

调用测试

    如果我们能写一个组件,它可以实现如上http调用,不就能实现在非spring boot工程实现配置获取了嘛?

LocalPropertyPlaceholderConfigurer LocalPropertyPlaceholderConfigurer

    ▪️继承PropertyPlaceholderConfigurer重写mergeProperties(),PropertyPlaceholderConfigurer主要做了两件事:

         ▪️读取某个地方的配置信息,到底读取哪里的配置信息,由mergeProperties()决定。

        ▪️在bean实例获取之前,逐个替换${}形式的参数。实现这一点意义很大,比如可以直接使用@Value("${}")获取配置中心的配置信息,也可以在spring的XML配置文件中使用"${}"进行赋值。

    ▪️使用BasicAuthorizationInterceptor在restTemplate中放入Basic验证信息,与配置中心中心权限管理相呼应。在低版本spring(3.1.4)并不存在,可以根据高版本spring中的类,自己创建。

BasicAuthorizationInterceptor

    ▪️通过restTemplate封装http请求,从配置中心服务端获取配置信息。

    ▪️将配置中心放入本地Properties,方便客户端使用。

补充:

config-center.properties spring.xml

    简单来说就是创建了一个自定义的property-placeholder,专职于获取配置中心的配置信息。

如何实现旧工程热更新

ContextRefresher

    上图所示为spring容器的refresh方法。

总结

    当然还有其他事情需要完成,比如更新前后配置差异反馈,监控数据获取端点,在此我就不累赘讲述了,大家自行研究。如此将公司旧系统纳入spring cloud config配置中心管理,以后还要纳入集中监控(spring boot admin),慢慢将旧系统(低版本,非spring boot,未拆分)向新系统(spring boot,微服务化)平滑迁移。提供旧系统过渡方案,组件包支持,可以减少系统迁移改造成本,比用一堆新组件进行微服务化,服务治理,容器化等改造等意义更加重大。

以下为spring相关源码阅读和理解,有兴趣的可以自行观赏。

Spring容器的refresh方法

AbstractApplicationContext

Spring容器的refresh方法

AbstractApplicationContext

prepareRefresh函数

此方法主要记录一下初始化的时候或active的标记,并对一些properties进行初始化

prepareRefresh()

obtainFreshBeanFactory函数

obtainFreshBeanFactory()

此方法全部由子类实现。

    ▪️refreshBeanFactory()

refreshBeanFactory()的实现类

    在AbstractRefreshApplicationContext实现了refreshBeanFactory方法,此方法最主要的就是loadBeanDefinitions(beanFactory),由子类去实现load所有的beanDefinition。Spring在启动时会通过AbstractApplicationContext#refresh启动容器初始化工作,期间会委托loadBeanDefinitions配置文件。

refreshBeanFactory()默认实现

    loadBeanDefinitions通过层层委托,找到DefaultBeanDefinitionDocumentReader #parseBeanDefinitions解析具体的bean。

parseBeanDefinitions()

    这边由于不是标准类定义,所以委托BeanDefinitionParserDelegate解析通过NamespaceHandler查找到对应的处理器是ContextNamespaceHandler,再通过id找到PropertyPlaceholderBeanDefinitionParser解析器解析

BeanDefinitionParserDelegate ContextNamespaceHandler

    创建一个默认的BeanFactory。使用了DefaultListableBeanFactory。

    ▪️getBeanFactory()

getBeanFactory()的实现类 getBeanFactory()默认实现

prepareBeanFactory(beanFactory)函数

prepareBeanFactory(beanFactory)

    预处理beanFactory容器,配置一些beanFactory的classLoader和post-processor等。

postProcessBeanFactory(beanFactory)

    此方法由子类执行beanFactory之前的操作。

invokeBeanFactoryPostProcessors(beanFactory)

    用于执行BeanFactoryPostProcessors中的postProcessBeanFactory,beanFactoryPostProcessor是优先于BeanPostProcessor执行。

    先执行BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry。再执行BeanFactoryPostProcessor.postProcessBeanFactory。

registerBeanPostProcessors(beanFactory)

    注册beanPostProcessor,注意要和beanFactoryPostProcessor区分开,   beanFactoryPostProcessor是执行。此时这里只是注册beanPostProcessor,beanFactoryPostProcessory优先于beanPostProcessor执行beanPostProcessor的流程与beanFactoryPostProcessor执行流程类似。

        ▪️先查找所有BeanPostProcessor类型的bean

        ▪️注册属于PriorityOrdered的beanPostProcessor

        ▪️注册属于Ordered类型的beanPostProcessor

        ▪️注册剩余的beanPostProcessor

        ▪️增加一个继承了ApplicationListener的beanPostProcessor检查ApplicationListenerDetector。

TestBeanPostProcessor

initMessageSource()函数

initMessageSource()

        ▪️初始化一个MessageSource

                ▪️判断beanFactory是否有name=messageSource的MessageSource

                ▪️生成一个默认的MessageSource DelegatingMessageSource并注册到beanFactory中

initApplicationEventMulticaster()

initApplicationEventMulticaster()

        ▪️初始化ApplicationEvent类

                ▪️判断beanFactory是否有name=applicationEventMulticaster的ApplicationEventMulticaster

                ▪️生成默认的SimpleApplicationEventMulticaster并注册到beanFactory中

onRefresh()

onRefresh()

此方法由子类实现

onRefresh()的实现

registerListeners()

registerListeners()

    加载所有ApplicationListener并注册到ApplicationEventMulticaster中。

finishBeanFactoryInitialization(beanFactory)

finishBeanFactoryInitialization(beanFactory)

        ▪️处理beanFactory初始化结束的操作

        ▪️beanFactory.preInstantiateSingletons开始实例化所有未实例化的bean

finishRefresh()

        ▪️生成一个LifecycleProcessor默认为DefaultLifecycleProcessor

        ▪️执行LifecycleProcessor的onrefresh方法,执行所有属于Lifecycle的bean

        ▪️推送ApplicationEvent

Spring加载Java Properties

    Spring中PropertyPlaceholderConfigurer类,它是用来解析Java Properties文件属性值,并提供在Spring期间替换使用属性值。常用的使用方式:

        ▪️使用注解@Value("${}"),加载Java Properties文件属性值

        ▪️在spring.xml中使用"${}"赋值,加载Java Properties文件属性值

Spring容器加载Java Properties文件

    上图为核心类UML图,涉及类:PropertyPlaceholderConfigurer,PlaceholderConfigurerSupport,PropertyResourceConfigurer,PropertiesLoaderSupport;涉及接口:BeanNameAware,BeanFactoryAware,BeanFactoryPostProcessor,PriorityOrdered。

PropertiesLoaderSupport

PropertiesLoaderSupport

        ▪️localOverride:加载的数据是否覆盖spring之前加载的同名属性

        ▪️locations:配置properties文件路径,例如"classpath:xxxx";"file:xxxx"

        ▪️ignoreResourceNotFound:加载多个properties文件,是否忽略不存在的文件。

    看了上图,应该立马能够理解PropertiesLoaderSupport相关属性的作用了。当我们需要扩展PropertyPlaceholderConfigurer的实现时,需要重写mergeProperties(),然后将配置加载到PropertiesLoaderSupport。

 ▪️mergeProperties()

mergeProperties()

        ▪️作用一:返回包含合并多个Properties属性实例

        ▪️作用二:在这个FactoryBean上设置了加载的属性

▪️loadProperties(Properties props)

loadProperties

        ▪️作用一:加载路径locations的Java Properties文件

PropertyPlaceholderConfigurer

PropertyPlaceholderConfigurer

    你也许会好奇,为什么使用"${}"就能获取properties文件中的属性值?PropertyPlaceholderConfigurer会在在创建bean之前,将"${}"替换成对应的属性,并赋值给成员变量。

▪️parseStringValue(String strVal, Properties props, Set visitedPlaceholders)

parseStringValue

        ▪️作用:解析给定字符串的占位符,并赋值。

        现在我们看看红框中的都是什么?答案在PlaceholderConfigurerSupport的相关成员变量中。

PlaceholderConfigurerSupport

        看到这里你就能立马明白,需要进行处理的"${}"在这里进行了判断和替换。到此spring如何加载Java Properties文件,如何进行"${}"替换属性都已经十分清晰明了。

如果需要給我修改意见的发送邮箱:erghjmncq6643981@163.com

本博客的代码示例已上传GitHub:分布式配置中心

GitHub

转发博客,请注明,谢谢。

相关文章

  • 如何在旧项目中实现从配置中心加载配置

    前言 spring cloud相关组件很强大,可以轻松实现配置中心。但是会遇到一个很实际的问题:spring cl...

  • flask - config 配置

    Flask 项目中,我们会加载很多配置 :譬如:设置秘钥: 设置数据库: 同时也会有一些自定义的配置项: 然而配置...

  • Java学习:反射的应用,依赖加载

    使用反射实现从配置文件加载类以及根据配置文件解决特定依赖关系。 配置文件reflect.properties: 对...

  • thinkphp5学习笔记(二)配置文件

    配置文件 配置文件格式 场景配置 模块配置 加载其他位置的配置文件 如何正确读取配置项 动态配置 如何正确设置配置...

  • 使用nacos做配置中心

    nacos 作为配置中心能够监听到配置中心的数据更新,但是并不能自动重新加载ApplicationContext ...

  • Spring框架探秘:XML配置文件和解析原理和Bean的实例化

    Spring的XML解析原理 Spring的作用:定位: 寻找XML配置文件加载: 将解析的XML配置加载到内存实...

  • 2019-07-17

    Ueditor 报错:后端配置项没有正常加载,上传插件不能正常使用! 是不是各个配置都弄好了,就是显示后端配置项没...

  • secret

    secret ConfigMap 和 secretPod中容器提供配置信息配置中心Pod加载ConfigMap 资...

  • jasypt 3.0.4 Bug 分析与解决

    背景 目前项目中使用 jasypt 来做配置项的加解密,但是在实际使用中发现 3.0.4 版本中 ,在配置中心动态...

  • SpringBoot扩展点EnvironmentPostProc

    一、背景 之前项目中用到了Apollo配置中心,对接Apollo配置中心后,配置中心的属性就可以在程序中使用了,那...

网友评论

本文标题:如何在旧项目中实现从配置中心加载配置

本文链接:https://www.haomeiwen.com/subject/uffmcftx.html