一文带你理解@RefreshScope注解实现动态刷新原理

概述

RefeshScope这个注解想必大家都用过,在微服务配置中心的场景下经常出现,他可以用来刷新Bean中的属性配置,那大家对他的实现原理了解吗?它为什么可以做到动态刷新呢?

注解的作用

@RefreshScope注解是Spring Cloud中的一个注解,用来实现Bean中属性的动态刷新。

/**
 * Convenience annotation to put a <code>@Bean</code> definition in
 * {@link org.springframework.cloud.context.scope.refresh.RefreshScope refresh scope}.
 * Beans annotated this way can be refreshed at runtime and any components that are using
 * them will get a new instance on the next method call, fully initialized and injected
 * with all dependencies.
 *
 * @author Dave Syer
 *
 */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
 
	/**
	 * @see Scope#proxyMode()
	 * @return proxy mode
	 */
	ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
 
}
  • 上面是RefreshScope的源码,该注解被@Scope注解使用,@Scope用来比较Spring Bean的作用域,具体使用参考相关文章。
  • 注解的属性proxyMode默认使用TARGET_CLASS作为代理。

实例

  1. controller中添加@RefreshScope
  2. nacos配置中心中配置
  3. 验证, 修改配置中心后,可以不重启动,刷新配置
  4. 去掉@RefreshScope 就不会自动刷新。

代码地址: github.com/alvinlkk/aw… 

原理解析

为了实现动态刷新配置,主要就是想办法达成以下两个核心目标:

  1. 让Spring容器重新加载Environment环境配置变量
  2. Spring Bean重新创建生成

@RefreshScope主要就是基于@Scope注解的作用域代理的基础上进行扩展实现的,加了@RefreshScope注解的类,在被Bean工厂创建后会加入自己的refresh scope 这个Bean缓存中,后续会优先从Bean缓存中获取,当配置中心发生了变更,会把变更的配置更新到spring容器的Environment中,并且同事bean缓存就会被清空,从而就会从bean工厂中创建bean实例了,而这次创建bean实例的时候就会继续经历这个bean的生命周期,使得@Value属性值能够从Environment中获取到最新的属性值,这样整个过程就达到了动态刷新配置的效果。

如果对Scope注解不清楚的可以阅读这篇文章:【Spring注解必知必会】@Scope注解源码解析

获取RefreshScope注解的Bean

通过打上断点查看堆栈可知:

  1. 因为Class被加上了@RefreshScope注解,那么这个BeanDefinition信息中的scope为refresh,在getBean的的时候会单独处理逻辑。
    public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
     
    protected <T> T doGetBean(
    			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    			throws BeansException {
     
    				// 如果scope是单例的情况, 这里不进行分析
    				if (mbd.isSingleton()) {
    				 .....
    				}
                    // 如果scope是prototype的情况, 这里不进行分析
    				else if (mbd.isPrototype()) {
    					......
    				}
                    // 如果scope是其他的情况,本例中是reresh
    				else {
    					String scopeName = mbd.getScope();
    					if (!StringUtils.hasLength(scopeName)) {
    						throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
    					}
                        // 获取refresh scope的实现类RefreshScope,这个类在哪里注入,我们后面讲
    					Scope scope = this.scopes.get(scopeName);
    					if (scope == null) {
    						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    					}
    					try {
                            // 这边是获取bean,调用的是RefreshScope中的的方法
    						Object scopedInstance = scope.get(beanName, () -> {
    							beforePrototypeCreation(beanName);
    							try {
    								return createBean(beanName, mbd, args);
    							}
    							finally {
    								afterPrototypeCreation(beanName);
    							}
    						});
    						beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    					}
    					catch (IllegalStateException ex) {
    						throw new ScopeNotActiveException(beanName, scopeName, ex);
    					}
    				}
    			}
    			catch (BeansException ex) {
    				beanCreation.tag("exception", ex.getClass().toString());
    				beanCreation.tag("message", String.valueOf(ex.getMessage()));
    				cleanupAfterBeanCreationFailure(beanName);
    				throw ex;
    			}
    			finally {
    				beanCreation.end();
    			}
    		}
     
    		return adaptBeanInstance(name, beanInstance, requiredType);
    	}
        
    }

  2. RefreshScope继承成了GenericScope类,最终调用的的是GenericScope的get方法

    public class GenericScope
    		implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean {
                     @Override
    	
      public Object get(String name, ObjectFactory<?> objectFactory) {
    		// 将bean添加到缓存cache中
            BeanLifecycleWrapper value = this.cache.put(name, new BeanLifecycleWrapper(name, objectFactory));
    		this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
    		try {
                // 调用下面的getBean方法
    			return value.getBean();
    		}
    		catch (RuntimeException e) {
    			this.errors.put(name, e);
    			throw e;
    		}
    	}       
     
    private static class BeanLifecycleWrapper {
            
    		public Object getBean() {
                // 如果bean为空,则创建bean
    			if (this.bean == null) {
    				synchronized (this.name) {
    					if (this.bean == null) {
    						this.bean = this.objectFactory.getObject();
    					}
    				}
    			}
                // 否则返回之前创建好的bean
    			return this.bean;
    		}
                }
            }
    

    小结:

    从这边的代码中可以印证了上面的说法,创建后的Bean会缓存到scope的cache中,优先从缓存中获取,如果缓存中是null, 则重新走一遍create bean的流程。

RefeshScope Bean的创建

上面的在getBean的时候依赖到RefreshScope这个Bean,那么这个Bean是在什么时候加入到Spring Bean中的呢?答案就是RefreshAutoConfiguration

配置中心刷新后刷新Bean缓存

  1. 配置中心发生变化后,会收到一个RefreshEvent事件,RefreshEventListner监听器会监听到这个事件。
    public class RefreshEventListener implements SmartApplicationListener {
     
    	
    ........
     
    	public void handle(RefreshEvent event) {
    		if (this.ready.get()) { // don't handle events before app is ready
    			log.debug("Event received " + event.getEventDesc());
                // 会调用refresh方法,进行刷新
    			Set<String> keys = this.refresh.refresh();
    			log.info("Refresh keys changed: " + keys);
    		}
    	}
     
    }
     
    // 这个是ContextRefresher类中的刷新方法
    public synchronized Set<String> refresh() {
            // 刷新spring的envirionment 变量配置
    		Set<String> keys = refreshEnvironment();
            // 刷新其他scope
    		this.scope.refreshAll();
    		return keys;
    	}
    

  2. refresh方法最终调用destroy方法,清空之前缓存的bean
public class RefreshScope extends GenericScope
		implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, Ordered {
 
	@ManagedOperation(description = "Dispose of the current instance of all beans "
			+ "in this scope and force a refresh on next method execution.")
	public void refreshAll() {
		// 调用父类的destroy
        super.destroy();
		this.context.publishEvent(new RefreshScopeRefreshedEvent());
	}
}
 
 
@Override
	public void destroy() {
		List<Throwable> errors = new ArrayList<Throwable>();
		Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
		for (BeanLifecycleWrapper wrapper : wrappers) {
			try {
				Lock lock = this.locks.get(wrapper.getName()).writeLock();
				lock.lock();
				try {
                    // 这里主要就是把之前的bean设置为null, 就会重新走createBean的流程了
					wrapper.destroy();
				}
				finally {
					lock.unlock();
				}
			}
			catch (RuntimeException e) {
				errors.add(e);
			}
		}
		if (!errors.isEmpty()) {
			throw wrapIfNecessary(errors.get(0));
		}
		this.errors.clear();
	}

总结

上面是这个RefreshScope实现动态刷新大致的原理,其中里面还有很多细节,可能需要留给大家自己debug去深入理解。

————————————————
版权声明:本文为CSDN博主「肥肥技术宅」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_71777195/article/details/126319418

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
操作系统是计算机系统中的核心组成部分,负责管理和协调计算机硬件和软件资源,提供程序运行环境。在CSDN上有很多关于操作系统的专题文章,以下将从操作系统的基本概念、功能和常见类型等方面简要介绍一下。 首先是操作系统的基本概念。操作系统是一种系统软件,它是计算机硬件和应用软件之间的桥梁,提供给应用程序一系列的服务和资源,同时负责调度和管理系统资源。它为用户屏蔽了底层的硬件差异,提供了一个统一的、易于使用的界面。 操作系统主要有四个基本功能。首先是处理器管理,负责将处理器分配给系统中的各个进程,并进行进程切换,实现多道程序并发执行。其次是内存管理,管理计算机的内存资源,包括分配、回收和保护等操作。再次是文件管理,负责管理文件的存储、命名和保护等操作,提供了文件操作的接口。最后是设备管理,负责管理计算机的各种设备,包括输入输出设备和存储设备等。 常见的操作系统有多种类型。最主流的是Windows、Linux和Mac OS等桌面操作系统。此外还有服务器操作系统,如Windows Server和Linux等,用于管理和部署服务器。还有嵌入式操作系统,如Android和iOS等,用于移动设备和物联网设备。操作系统也有实时操作系统,用于需要实时控制和响应的系统,如工控系统和航空航天系统等。 总之,操作系统是计算机系统中不可或缺的重要组成部分,通过CSDN上的相关文章,我们可以更深入了解操作系统的基本概念、功能和不同类型。这些知识对于理解计算机系统的工作原理和提升编程能力都有着重要意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值