最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文帶你理解@RefreshScope注解實(shí)現(xiàn)動(dòng)態(tài)刷新原理

 更新時(shí)間:2023年07月16日 14:33:08   作者:JAVA旭陽(yáng)  
RefeshScope這個(gè)注解想必大家都用過(guò),在微服務(wù)配置中心的場(chǎng)景下經(jīng)常出現(xiàn),他可以用來(lái)刷新Bean中的屬性配置,那大家對(duì)他的實(shí)現(xiàn)原理了解嗎,它為什么可以做到動(dòng)態(tài)刷新呢,所以本文小編將給大家詳細(xì)介紹@RefreshScope注解實(shí)現(xiàn)動(dòng)態(tài)刷新原理

注解的作用

@RefreshScope注解是Spring Cloud中的一個(gè)注解,用來(lái)實(shí)現(xiàn)Bean中屬性的動(dòng)態(tài)刷新。

/**
 * 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用來(lái)比較Spring Bean的作用域,具體使用參考相關(guān)文章。
  • 注解的屬性proxyMode默認(rèn)使用TARGET_CLASS作為代理。

實(shí)例

  • controller中添加@RefreshScope

  • nacos配置中心中配置

  • 驗(yàn)證, 修改配置中心后,可以不重啟動(dòng),刷新配置

  • 去掉@RefreshScope 就不會(huì)自動(dòng)刷新。

 代碼地址:awesome-springcloud-demo/springcloud-nacos/springcloud-nacos-config at master · alvinlkk/awesome-springcloud-demo · GitHub

原理解析

為了實(shí)現(xiàn)動(dòng)態(tài)刷新配置,主要就是想辦法達(dá)成以下兩個(gè)核心目標(biāo):

  • 讓Spring容器重新加載Environment環(huán)境配置變量
  • Spring Bean重新創(chuàng)建生成

@RefreshScope主要就是基于@Scope注解的作用域代理的基礎(chǔ)上進(jìn)行擴(kuò)展實(shí)現(xiàn)的,加了@RefreshScope注解的類(lèi),在被Bean工廠創(chuàng)建后會(huì)加入自己的refresh scope 這個(gè)Bean緩存中,后續(xù)會(huì)優(yōu)先從Bean緩存中獲取,當(dāng)配置中心發(fā)生了變更,會(huì)把變更的配置更新到spring容器的Environment中,并且同事bean緩存就會(huì)被清空,從而就會(huì)從bean工廠中創(chuàng)建bean實(shí)例了,而這次創(chuàng)建bean實(shí)例的時(shí)候就會(huì)繼續(xù)經(jīng)歷這個(gè)bean的生命周期,使得@Value屬性值能夠從Environment中獲取到最新的屬性值,這樣整個(gè)過(guò)程就達(dá)到了動(dòng)態(tài)刷新配置的效果。

獲取RefreshScope注解的Bean

通過(guò)打上斷點(diǎn)查看堆棧可知:

  • 因?yàn)镃lass被加上了@RefreshScope注解,那么這個(gè)BeanDefinition信息中的scope為refresh,在getBean的的時(shí)候會(huì)單獨(dú)處理邏輯。
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是單例的情況, 這里不進(jìn)行分析
				if (mbd.isSingleton()) {
				 .....
				}
                // 如果scope是prototype的情況, 這里不進(jìn)行分析
				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的實(shí)現(xiàn)類(lèi)RefreshScope,這個(gè)類(lèi)在哪里注入,我們后面講
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
                        // 這邊是獲取bean,調(diào)用的是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);
	}
}
  • RefreshScope繼承成了GenericScope類(lèi),最終調(diào)用的的是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 {
            // 調(diào)用下面的getBean方法
			return value.getBean();
		}
		catch (RuntimeException e) {
			this.errors.put(name, e);
			throw e;
		}
	}       
private static class BeanLifecycleWrapper {
		public Object getBean() {
            // 如果bean為空,則創(chuàng)建bean
			if (this.bean == null) {
				synchronized (this.name) {
					if (this.bean == null) {
						this.bean = this.objectFactory.getObject();
					}
				}
			}
            // 否則返回之前創(chuàng)建好的bean
			return this.bean;
		}
            }
        }

小結(jié):

從這邊的代碼中可以印證了上面的說(shuō)法,創(chuàng)建后的Bean會(huì)緩存到scope的cache中,優(yōu)先從緩存中獲取,如果緩存中是null, 則重新走一遍create bean的流程。

RefeshScope Bean的創(chuàng)建

上面的在getBean的時(shí)候依賴到RefreshScope這個(gè)Bean,那么這個(gè)Bean是在什么時(shí)候加入到Spring Bean中的呢?答案就是RefreshAutoConfiguration。

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

  • 配置中心發(fā)生變化后,會(huì)收到一個(gè)RefreshEvent事件,RefreshEventListner監(jiān)聽(tīng)器會(huì)監(jiān)聽(tīng)到這個(gè)事件。
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());
            // 會(huì)調(diào)用refresh方法,進(jìn)行刷新
			Set<String> keys = this.refresh.refresh();
			log.info("Refresh keys changed: " + keys);
		}
	}
}
// 這個(gè)是ContextRefresher類(lèi)中的刷新方法
public synchronized Set<String> refresh() {
        // 刷新spring的envirionment 變量配置
		Set<String> keys = refreshEnvironment();
        // 刷新其他scope
		this.scope.refreshAll();
		return keys;
	}
  • refresh方法最終調(diào)用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() {
		// 調(diào)用父類(lèi)的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設(shè)置為null, 就會(huì)重新走createBean的流程了
					wrapper.destroy();
				}
				finally {
					lock.unlock();
				}
			}
			catch (RuntimeException e) {
				errors.add(e);
			}
		}
		if (!errors.isEmpty()) {
			throw wrapIfNecessary(errors.get(0));
		}
		this.errors.clear();
	}

總結(jié)

上面是這個(gè)RefreshScope實(shí)現(xiàn)動(dòng)態(tài)刷新大致的原理,其中里面還有很多細(xì)節(jié),可能需要留給大家自己debug去深入理解。

以上就是一文帶你理解@RefreshScope注解實(shí)現(xiàn)動(dòng)態(tài)刷新原理的詳細(xì)內(nèi)容,更多關(guān)于@RefreshScope注解實(shí)現(xiàn)動(dòng)態(tài)刷新的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 解決struts2 攔截器修改request的parameters參數(shù)失敗的問(wèn)題

    解決struts2 攔截器修改request的parameters參數(shù)失敗的問(wèn)題

    這篇文章主要介紹了解決struts2 攔截器修改request的parameters參數(shù)失敗的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 關(guān)于ReentrantLock原理全面解讀

    關(guān)于ReentrantLock原理全面解讀

    這篇文章主要介紹了關(guān)于ReentrantLock原理全面解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • mybatis如何使用truncate清空表

    mybatis如何使用truncate清空表

    這篇文章主要介紹了mybatis如何使用truncate清空表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java時(shí)間工具類(lèi)Date的常用處理方法

    Java時(shí)間工具類(lèi)Date的常用處理方法

    在Java中獲取當(dāng)前時(shí)間,可以使用 java.util.Date 類(lèi)和 java.util.Calendar 類(lèi)完成。其中,Date 類(lèi)主要封裝了系統(tǒng)的日期和時(shí)間的信息,下面將詳細(xì)介紹Date類(lèi)的常用處理方法,需要的可以參考一下
    2022-05-05
  • Java開(kāi)發(fā)之內(nèi)部類(lèi)對(duì)象的創(chuàng)建及hook機(jī)制分析

    Java開(kāi)發(fā)之內(nèi)部類(lèi)對(duì)象的創(chuàng)建及hook機(jī)制分析

    這篇文章主要介紹了Java開(kāi)發(fā)之內(nèi)部類(lèi)對(duì)象的創(chuàng)建及hook機(jī)制,結(jié)合實(shí)例形式分析了java基于hook機(jī)制內(nèi)部類(lèi)對(duì)象的創(chuàng)建與使用,需要的朋友可以參考下
    2018-01-01
  • Java實(shí)現(xiàn)猜數(shù)字小游戲(有次數(shù)限制)

    Java實(shí)現(xiàn)猜數(shù)字小游戲(有次數(shù)限制)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)猜數(shù)字小游戲,有次數(shù)限制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • 一文了解為什么Java中只有值傳遞

    一文了解為什么Java中只有值傳遞

    Java?傳參是值傳遞還是引用傳遞?這個(gè)問(wèn)題很基礎(chǔ),但是許多人都有點(diǎn)懵。本文就來(lái)通過(guò)一些示例帶大家詳細(xì)了解一下,需要的可以參考一下
    2022-07-07
  • SpringBoot松散綁定/寬松綁定(Relaxed Binding)的實(shí)現(xiàn)

    SpringBoot松散綁定/寬松綁定(Relaxed Binding)的實(shí)現(xiàn)

    本文主要介紹了SpringBoot松散綁定/寬松綁定(Relaxed Binding)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-08-08
  • IDEA 搭建maven 安裝、下載、配置的圖文教程詳解

    IDEA 搭建maven 安裝、下載、配置的圖文教程詳解

    這篇文章主要介紹了IDEA 搭建maven 安裝、下載、配置,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • Windows10系統(tǒng)下JDK1.8環(huán)境變量的配置

    Windows10系統(tǒng)下JDK1.8環(huán)境變量的配置

    今天帶大家學(xué)習(xí)在Windows10系統(tǒng)下怎么配置JDK1.8環(huán)境變量,文中有非常詳細(xì)的安裝及配置教程,對(duì)正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05

最新評(píng)論

乐安县| 武鸣县| 稻城县| 瓦房店市| 古交市| 图们市| 共和县| 万年县| 灵川县| 湘阴县| 织金县| 奈曼旗| 图们市| 井冈山市| 石首市| 江油市| 丰台区| 福安市| 綦江县| 钟山县| 陆良县| 凉山| 大安市| 齐齐哈尔市| 宜州市| 凤台县| 即墨市| 乌拉特中旗| 柘荣县| 武宁县| 集安市| 古交市| 习水县| 泌阳县| 武义县| 中阳县| 阜康市| 施秉县| 崇礼县| 伊宁市| 沁源县|