8000字+22张图探秘SpringCloud配置中心的核心原理

laomugua
发布于 2023-11-6 15:08
浏览
1收藏

如何动态刷新Bean的属性?

我们都知道,要想实现配置属性的动态刷新,需要在类上加上一个注解

@RefreshScope

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

重点来了

加了@RefreshScope注解的Bean,就拿上图中的UserService举例

Spring在生成的时候会生成两个UserService的Bean:

  • 第一个是UserService的代理动态代理的Bean,后面我称为第一个Bean
  • 第二个就是UserService这个Bean,后面我称为第二个Bean

当你在其它类中需要注入一个UserService时,真正注入的是第一个Bean,也就是动态代理的Bean

当你使用这个注入的动态代理的Bean的时候,他会去找第二个Bean,也就是真正的UserService这个Bean,然后调用对应的方法

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

比如你调用注入的UserService代理对象的​​getUsername​​方法,最终就会调用到第二个Bean​getUsername​​方法

获取到的username属性值自然也就是第二个Bean中的username值

那么为什么要生成两个Bean?

接着往下瞅

在SpringCloud中有这么一项规定

当配置中心客户端一旦感知到服务端的某个配置有变化的时候,需要发布一个RefreshEvent事件来告诉SpringCloud配置有变动

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

在SpringCloud中RefreshEventListener类会去监听这个事件

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

一旦监听到这个事件,SpringCloud会再次从配置中心拉取配置

这个拉取配置的核心逻辑跟启动时拉取配置的核心逻辑是一样的

也是通过 BootstrapApplicationListener 来实现的

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

这部分代码逻辑在ContextRefresher类中,顺着RefreshEventListener就能看到,有兴趣可以扒一扒

怕你忘了,我再把上面拉取配置的图拿过来

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

有了最新的配置之后,就会进行一步骚操作来移花接木”刷新“注入到对象的属性

这个骚操作就是销毁所有的前面提到的第二个Bean,但是第一个Bean,也就是代理对象保持不变

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

当程序运行调用代理对象的方法的时候,发现第二个Bean没有了,此时他就会去重新创建第二个Bean,也就是重新创建一个UserService对象

由于此时已经拉到最新的配置了,也就是这个被重新创建的UserService对象注入的就是最新的属性

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

之后再调用的这个新创建的第二个Bean,拿到的自然就是最新的配置

所以,给你的感觉是对象的属性发生了变化,实际上是真正被调用的对象重新创建了

所以这招移花接木还是有点意思的!

小总结

其实到这就弄明白了Bean的属性动态刷新的原理

其实就是当配置中心客户端发现服务端的配置有变化,需要发送一个RefreshEvent事件来告诉SpringCloud配置有变动

SpringCloud会去监听这个事件,按照项目启动的方式重新拉取配置中心最新的属性配置

当拉取完属性配置之后,就会销毁所有的第二个Bean,也就是真正被使用的Bean

之后当第一个Bean(动态代理的Bean)需要使用这个第二个Bean时,就会重新创建这个第二个Bean

此时由于已经有最新的配置了,那么创建的这个第二个Bean就会被注入最新的属性,这样就实现了属性的”刷新“

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

补充个东西:@RefreshScope的秘密

上面大致说了@RefreshScope动态刷新的原理

这里我补充一下@RefreshScope代码层面的实现原理

本来这部分原理我是写在前面的,但是我发现这块比较绕,怕打断文章的节奏,所以就准备删除了

但是想想既然都写了,那么就给放到补充里面吧,看不懂也不耽误前面的理解

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

这个注解是个衍生注解,真正起作用的就是@Scope注解

@Scope注解并不陌生,他其实是定义Bean的作用域

比如多例(原型),就可以加上@Scope("prototype")注解

还有一些八股文常背的作用域,比如session作用域等等

而@RefreshScope也可以看做是一种Bean的作用域,名字叫做refresh

这些除了单例和多例之外的作用域的底层实现逻辑都是一样的

这些带有作用域的Bean相比于普通的单例Bean主要有以下几点不同:

  • 会注册两个Bean,这个前面已经提到过
  • 保存的地方不同,比如单例Bean最终会存在三级缓存中的第一级缓存中,而不同作用域的Bean是存在不同的地方的

先说会注册两个Bean,还是以前面提到的UserService举个例子,这两个Bean分别是

  • 第一个Bean的Bean名称为​​userService​​​,Bean class为​​ScopedProxyFactoryBean.class​​,这个scope为默认,也就是单例
  • 第二个Bean的Bean名称为​​scopedTarget.userService​​​,Bean class为​​UserService.class​​,scope为refresh(如果是session作用域就是session)

第一个Bean的class为ScopedProxyFactoryBean,是个FactoryBean的实现

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

这个最终会生成一个代理对象,上面的例子就是为UserService生成一个代理对象,并且由于是单例的,所以最终这个对象会被放到一级缓存中,我们使用时注入的也就是这个对象

第二个Bean的class是UserService,所以生成的就是真正的UserService对象,但是由于scope为refresh,所以不会存在第一级缓存中

这部分注册两个Bean的代码是在ScopedProxyUtils#createScopedProxy方法中,有兴趣的可以扒扒

再来讲一讲保存的地方不同

不同的作用域都需要实现一个​​Scope​​接口来存放对应的Bean

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

比如refresh、session作用域都有对应的实现

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

也就是通过​​Scope​​就可以管理不同作用域的Bean

所以,对于refresh这个作用域来说,他的所有的Bean都在RefreshScope中

后面说的销毁,只需要移除RefreshScope中的Bean就可以了

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

代码也在ContextRefresher类中

开源配置中心是如何整合SpringCloud的?

首先我们再来梳理一下拉取配置和刷新配置的核心关键点

拉取配置关键点就是项目启动的时候(也包括重新拉取配置),会去创建一个容器

这个容器只读取bootstrap配置文件和spring.factories中的键为​​org.springframework.cloud.bootstrap.BootstrapConfiguration​​对应的配置类

之后会获取这个容器中的PropertySourceLocator,从而获取配置中心的配置

刷新配置关键点就是一旦配置中心配置变动,就需要发送RefreshEvent事件,之后一系列刷新操作都是由SpringCloud的来完成的

所以,配置中心整合到SpringCloud其实就很简单,就两点

第一点就是需要实现PropertySourceLocator,并且配置中心一些相关的Bean需要通过​​org.springframework.cloud.bootstrap.BootstrapConfiguration​​来装配到这个容器中

第二点,当配置发生变更需要发送RefreshEvent事件,这部分配置中心一些相关的Bean配置肯定是需要通过自动装配来完成

有了这两点我们来看看Nacos作为配置中心是如何整合到SpringCloud的

我们直接看Nacos的spring.factories文件

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

NacosConfigBootstrapConfiguration是用来实现第一点的

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

除了Nacos自己的一些Bean,他还声明了一个NacosPropertySourceLocator这个Bean

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

这个Bean就实现了PropertySourceLocator接口

第二点的实现就是通过NacosConfigAutoConfiguration配置类来实现的

这里面有这么一个Bean

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

这个Bean就实现了配置变化发送事件的操作

8000字+22张图探秘SpringCloud配置中心的核心原理-鸿蒙开发者社区

除了Nacos,比如说Consul作为配置中心的时候也是这么一套实现逻辑

但是值的注意的是,像Apollo配置中心,他并没有适配SpringCloud这套规范

当然,如果你有兴趣,可以自己实现Apollo适配SpringCloud这套规范

ok,本文就讲到这里,如果觉得对你有点帮助,欢迎点赞、在看、收藏、转发分享给其他需要的人,灰常滴感谢!


文章转载自公众号:三友的java日记

标签
已于2023-11-6 15:08:05修改
收藏 1
回复
举报
回复
    相关推荐