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

前言

    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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,063评论 6 510
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,805评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,403评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,110评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,130评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,877评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,533评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,429评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,947评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,078评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,204评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,894评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,546评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,086评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,195评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,519评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,198评论 2 357

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,675评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,832评论 6 342
  • Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof阅读 2,817评论 1 24
  • 文章作者:Tyan博客:noahsnail.com | CSDN | 简书 3.8 Container Exten...
    SnailTyan阅读 1,235评论 0 6
  • 人承认自己不能,比承认自己能要困难的多。只有看到不足,才会虚心向他人请教,让自己收获多多。而不是在错的路上越走越远。
    斐丽希娅阅读 87评论 0 0