中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

@RefreshScope刷新機制是什么

發布時間:2022-01-06 15:26:51 來源:億速云 閱讀:211 作者:iii 欄目:大數據

這篇文章主要介紹“@RefreshScope刷新機制是什么”,在日常操作中,相信很多人在@RefreshScope刷新機制是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”@RefreshScope刷新機制是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

一、前言

用過Spring Cloud的同學都知道在使用動態配置刷新的我們要配置一個@RefreshScope 在類上才可以實現對象屬性的的動態更新,本著知其所以然的態度,晚上沒事兒又把這個點回顧了一下,下面就來簡單的說下自己的理解。

總覽下,實現@RefreshScope 動態刷新的就需要以下幾個:

  • @ Scope   

  • @RefreshScope

  • RefreshScope       

  • GenericScope   

  • Scope

  • ContextRefresher

二、@Scope

一句話,@RefreshScope 能實現動態刷新全仰仗著@Scope 這個注解,這是為什么呢?

@Scope 代表了Bean的作用域,我們來看下其中的屬性:

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

	/**
	 * Alias for {@link #scopeName}.
	 * @see #scopeName
	 */
	@AliasFor("scopeName")
	String value() default "";

	/**
	 *  singleton  表示該bean是單例的。(默認)
     *  prototype    表示該bean是多例的,即每次使用該bean時都會新建一個對象。
     *  request        在一次http請求中,一個bean對應一個實例。
     *  session        在一個httpSession中,一個bean對應一個實例
	 */
	@AliasFor("value")
	String scopeName() default "";

	/**
    *   DEFAULT			不使用代理。(默認)
	* 	NO				不使用代理,等價于DEFAULT。
	* 	INTERFACES		使用基于接口的代理(jdk dynamic proxy)。
	* 	TARGET_CLASS	使用基于類的代理(cglib)。
    */
	ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;

}

通過代碼我們可以清晰的看到兩個主要屬性value 和 proxyMode,value就不多說了,大家平時經常用看看注解就可以。proxyMode 這個就有意思了,而這個就是@RefreshScope  實現的本質了。

我們需要關心的就是ScopedProxyMode.TARGET_CLASS 這個屬性,當ScopedProxyMode 為TARGET_CLASS 的時候會給當前創建的bean 生成一個代理對象,會通過代理對象來訪問,每次訪問都會創建一個新的對象。

理解起來可能比較晦澀,那先來看下實現再回頭來看這句話。

三、RefreshScope 的實現原理

  1. 先來看下@RefreshScope

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
	/**
	 * @see Scope#proxyMode()
	 */
	ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}

2. 可以看出,它使用就是 @Scope ,其內部就一個屬性默認 ScopedProxyMode.TARGET_CLASS。知道了是通過Spring Scope 來實現的那就簡單了,我們來看下Scope 這個接口     

public interface Scope {

	/**
	 * Return the object with the given name from the underlying scope,
	 * {@link org.springframework.beans.factory.ObjectFactory#getObject() creating it}
	 * if not found in the underlying storage mechanism.
	 * <p>This is the central operation of a Scope, and the only operation
	 * that is absolutely required.
	 * @param name the name of the object to retrieve
	 * @param objectFactory the {@link ObjectFactory} to use to create the scoped
	 * object if it is not present in the underlying storage mechanism
	 * @return the desired object (never {@code null})
	 * @throws IllegalStateException if the underlying scope is not currently active
	 */
	Object get(String name, ObjectFactory<?> objectFactory);

 
	@Nullable
	Object remove(String name);

 
	void registerDestructionCallback(String name, Runnable callback);

 
	@Nullable
	Object resolveContextualObject(String key);

	 
	@Nullable
	String getConversationId();

}

看下接口,我們只看Object get(String name, ObjectFactory<?> objectFactory); 這個方法幫助我們來創建一個新的bean ,也就是說,@RefreshScope 在調用 刷新的時候會使用此方法來給我們創建新的對象,這樣就可以通過spring 的裝配機制將屬性重新注入了,也就實現了所謂的動態刷新。

  1. 那它究竟是怎么處理老的對象,又怎么除法創建新的對象呢?

在開頭我提過幾個重要的類,而其中 RefreshScope extends GenericScope, GenericScope  implements Scope。

所以通過查看代碼,是GenericScope 實現了 Scope 最重要的  get(String name, ObjectFactory<?> objectFactory) 方法,在GenericScope 里面 包裝了一個內部類 BeanLifecycleWrapperCache 來對加了 @RefreshScope 從而創建的對象進行緩存,使其在不刷新時獲取的都是同一個對象。(這里你可以把 BeanLifecycleWrapperCache 想象成為一個大Map 緩存了所有@RefreshScope  標注的對象)

知道了對象是緩存的,所以在進行動態刷新的時候,只需要清除緩存,重新創建就好了。 來看代碼,眼見為實,只留下關鍵方法:

// ContextRefresher 外面使用它來進行方法調用 ============================== 我是分割線

	public synchronized Set<String> refresh() {
		Set<String> keys = refreshEnvironment();
		this.scope.refreshAll();
		return keys;
	}

// RefreshScope 內部代碼  ============================== 我是分割線

	@ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.")
	public void refreshAll() {
		super.destroy();
		this.context.publishEvent(new RefreshScopeRefreshedEvent());
	}


// GenericScope 里的方法 ============================== 我是分割線

	//進行對象獲取,如果沒有就創建并放入緩存
	@Override
	public Object get(String name, ObjectFactory<?> objectFactory) {
		BeanLifecycleWrapper value = this.cache.put(name,
				new BeanLifecycleWrapper(name, objectFactory));
		locks.putIfAbsent(name, new ReentrantReadWriteLock());
		try {
			return value.getBean();
		}
		catch (RuntimeException e) {
			this.errors.put(name, e);
			throw e;
		}
	}
	//進行緩存的數據清理
	@Override
	public void destroy() {
		List<Throwable> errors = new ArrayList<Throwable>();
		Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
		for (BeanLifecycleWrapper wrapper : wrappers) {
			try {
				Lock lock = locks.get(wrapper.getName()).writeLock();
				lock.lock();
				try {
					wrapper.destroy();
				}
				finally {
					lock.unlock();
				}
			}
			catch (RuntimeException e) {
				errors.add(e);
			}
		}
		if (!errors.isEmpty()) {
			throw wrapIfNecessary(errors.get(0));
		}
		this.errors.clear();
	}

通過觀看源代碼我們得知,我們截取了三個片段所得之,ContextRefresher 就是外層調用方法用的,GenericScope 里面的 get 方法負責對象的創建和緩存,destroy 方法負責再刷新時緩存的清理工作。

到此,關于“@RefreshScope刷新機制是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

衡山县| 台南县| 崇州市| 东乡县| 陈巴尔虎旗| 渝北区| 宁阳县| 方正县| 越西县| 嘉善县| 长顺县| 连江县| 湄潭县| 洮南市| 大同县| 蕉岭县| 怀化市| 武威市| 昌平区| 绥芬河市| 定南县| 保山市| 客服| 天祝| 云和县| 铅山县| 崇义县| 辽宁省| 巢湖市| 周口市| 巴林左旗| 滨海县| 大埔区| 贺州市| 凌海市| 河曲县| 万山特区| 莱芜市| 合作市| 肇庆市| 灵丘县|