@RefreshScope源码解析

本文深入探讨了@RefreshScope在Spring Cloud中的作用,分析了Bean的创建过程,包括invokeBeanFactoryPostProcessors、ContextRefreshedEvent事件和doGetBean。详细解释了动态刷新Bean配置变量值的机制,涉及RefreshEventListener、ContextRefresher和EnvironmentChangeEvent。文章总结了@RefreshScope如何实现配置的动态更新,并清理缓存以确保获取最新值。

前言

@RefeshScope这个注解想必大家都用过,在微服务配置中心的场景下经常出现,它可以用来刷新Bean中的属性配置,那么它是如何做到的呢?让我们来一步步揭开它神秘的面纱。

RefreshScope介绍

就是说我们在修改了bean属性的时候项目不需要重新启动,就可以拿到最新的值。

我们先来看下@RefreshScope的接口

@Target({
   
    ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
   
   

	/**
	 * @see Scope#proxyMode()
	 * @return proxy mode
	 */
	 //创建基于类的代理(使用 CGLIB)
	ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}

可以看出其是一个复合注解,被标注了 @Scope(“refresh”),@RefreshScope 是scopeName="refresh"的 @Scope

我们来看下org.springframework.cloud.context.scope.refresh.RefreshScope类的关系图

image.png

  • Scope -> GenericScope -> RefreshScope

RefreshScope管理了Scope=Refresh的Bean的生命周期,提供了get(获取),refreshAll(刷新)、destory(销毁)等方法

源码解析

版本说明

		<spring-boot.version>2.6.3</spring-boot.version>
		<spring-cloud.version>2021.0.1</spring-cloud.version>

Bean创建过程

我们创建一个带@RefreshScope注解的类

@Data
@Component
@RefreshScope
@ConfigurationProperties(prefix = "test")
public class TestProperties {

	private String name ;

}

启动项目进行debugger跟踪

invokeBeanFactoryPostProcessors

AbstractApplicationContext#invokeBeanFactoryPostProcessors方法中

调用Bean Factory的后置处理器,从上面的类图中我们可以看到RefreshScope就是一个BeanFactoryPostProcessors

image.png

然后调用父类GenericScope的postProcessBeanDefinitionRegistry方法

该方法遍历所有的bean定义 如果当前的bean的scope为refresh,那么就把当前的bean设置为 LockedScopedProxyFactoryBean的代理对象。

@RefreshScope标注的类还有一个特点:会使用代理对象并进行延迟加载。我们来看一下postProcessBeanDefinitionRegistry方法

@RefreshScope 注解的 bean,除了会生成一个beanName的 bean,同时会生成 scopedTarget.beanName的 bean

image.png

所以如果有@ConditionalOnSingleCandidate 注解的 bean,就不能在使用@RefreshScope的注解了。因为@ConditionalOnSingleCandidate全局只能有一个此类型的 bean

RefreshScope还会监听一个ContextRefreshedEvent,该事件会在ApplicationContext初始化或者refreshed时触发

ContextRefreshedEvent事件

AbstractApplicationContext#finishRefresh方法中

		// 上下文刷新事件
		publishEvent(new ContextRefreshedEvent(this));

我们来看一下RefreshScope中的代码:

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
   
   
		start(event);
	}

	public void start(ContextRefreshedEvent event) {
   
   
		if (event.getApplicationContext() == this.context && this.eager && this.registry != null) {
   
   
			eagerlyInitialize();
		}
	}

	private void eagerlyInitialize() {
   
   
		for (String name : this.context.getBeanDefinitionNames()) {
   
   
			BeanDefinition definition = this.registry.getBeanDefinition(name);
			if (this.getName().equals(definition.getScope()) && !definition.isLazyInit()) {
   
   
				Object bean = this.context.getBean(name);
				if (bean != null) {
   
   
					bean.getClass();
				}
			}
		}
	}

如果这个bean的scope = refresh的话就会去执行getBean方法,我们可以看到bean的名字为scopedTarget.testProperties这是一个被代理过的bean

image.png

doGetBean

上面的this.context.getBean(name)中会使用BeanFactory的doGetBean方法创建Bean,不同scope有不同的创建方式:

	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
   
   

		String beanName = transformedBeanName(name);
		Object beanInstance;
		...

				// 创建单例bean
				if (mbd.isSingleton()) {
   
   
					sharedInstance = getSingleton(beanName, () -> {
   
   
						try {
   
   
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
   
   
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}
                // 创建原型bean
				else if (mbd.isPrototype()) {
   
   
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
   
   
					   
						beforePrototypeCreation(beanName)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

原飞木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值