概述
RefeshScope這個(gè)注解想必大家都用過,在微服務(wù)配置中心的場景下經(jīng)常出現(xiàn),他可以用來刷新Bean中的屬性配置,那大家對他的實(shí)現(xiàn)原理了解嗎?它為什么可以做到動態(tài)刷新呢?
注解的作用
@RefreshScope注解是Spring Cloud中的一個(gè)注解,用來實(shí)現(xiàn)Bean中屬性的動態(tài)刷新。
/**
*Convenienceannotationtoputa@Bean
definitionin
*{@linkorg.springframework.cloud.context.scope.refresh.RefreshScoperefreshscope}.
*Beansannotatedthiswaycanberefreshedatruntimeandanycomponentsthatareusing
*themwillgetanewinstanceonthenextmethodcall,fullyinitializedandinjected
*withalldependencies.
*
*@authorDaveSyer
*
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public@interfaceRefreshScope{
/**
*@seeScope#proxyMode()
*@returnproxymode
*/
ScopedProxyModeproxyMode()defaultScopedProxyMode.TARGET_CLASS;
}
上面是RefreshScope的源碼,該注解被@Scope注解使用,@Scope用來比較Spring Bean的作用域,具體使用參考相關(guān)文章。
注解的屬性proxyMode默認(rèn)使用TARGET_CLASS作為代理。
實(shí)例
controller中添加@RefreshScope
nacos配置中心中配置
驗(yàn)證, 修改配置中心后,可以不重啟動,刷新配置
去掉@RefreshScope 就不會自動刷新。
原理解析
為了實(shí)現(xiàn)動態(tài)刷新配置,主要就是想辦法達(dá)成以下兩個(gè)核心目標(biāo):
讓Spring容器重新加載Environment環(huán)境配置變量
Spring Bean重新創(chuàng)建生成
@RefreshScope主要就是基于@Scope注解的作用域代理的基礎(chǔ)上進(jìn)行擴(kuò)展實(shí)現(xiàn)的,加了@RefreshScope注解的類,在被Bean工廠創(chuàng)建后會加入自己的refresh scope 這個(gè)Bean緩存中,后續(xù)會優(yōu)先從Bean緩存中獲取,當(dāng)配置中心發(fā)生了變更,會把變更的配置更新到spring容器的Environment中,并且同事bean緩存就會被清空,從而就會從bean工廠中創(chuàng)建bean實(shí)例了,而這次創(chuàng)建bean實(shí)例的時(shí)候就會繼續(xù)經(jīng)歷這個(gè)bean的生命周期,使得@Value屬性值能夠從Environment中獲取到最新的屬性值,這樣整個(gè)過程就達(dá)到了動態(tài)刷新配置的效果。
獲取RefreshScope注解的Bean
通過打上斷點(diǎn)查看堆??芍?/p>
因?yàn)镃lass被加上了@RefreshScope注解,那么這個(gè)BeanDefinition信息中的scope為refresh,在getBean的的時(shí)候會單獨(dú)處理邏輯。
publicabstractclassAbstractBeanFactoryextendsFactoryBeanRegistrySupportimplementsConfigurableBeanFactory{ protectedTdoGetBean( Stringname,@NullableClass requiredType,@NullableObject[]args,booleantypeCheckOnly) throwsBeansException{ //如果scope是單例的情況,這里不進(jìn)行分析 if(mbd.isSingleton()){ ..... } //如果scope是prototype的情況,這里不進(jìn)行分析 elseif(mbd.isPrototype()){ ...... } //如果scope是其他的情況,本例中是reresh else{ StringscopeName=mbd.getScope(); if(!StringUtils.hasLength(scopeName)){ thrownewIllegalStateException("Noscopenamedefinedforbean'"+beanName+"'"); } //獲取refreshscope的實(shí)現(xiàn)類RefreshScope,這個(gè)類在哪里注入,我們后面講 Scopescope=this.scopes.get(scopeName); if(scope==null){ thrownewIllegalStateException("NoScoperegisteredforscopename'"+scopeName+"'"); } try{ //這邊是獲取bean,調(diào)用的是RefreshScope中的的方法 ObjectscopedInstance=scope.get(beanName,()->{ beforePrototypeCreation(beanName); try{ returncreateBean(beanName,mbd,args); } finally{ afterPrototypeCreation(beanName); } }); beanInstance=getObjectForBeanInstance(scopedInstance,name,beanName,mbd); } catch(IllegalStateExceptionex){ thrownewScopeNotActiveException(beanName,scopeName,ex); } } } catch(BeansExceptionex){ beanCreation.tag("exception",ex.getClass().toString()); beanCreation.tag("message",String.valueOf(ex.getMessage())); cleanupAfterBeanCreationFailure(beanName); throwex; } finally{ beanCreation.end(); } } returnadaptBeanInstance(name,beanInstance,requiredType); } }
2.RefreshScope繼承成了GenericScope類,最終調(diào)用的的是GenericScope的get方法
publicclassGenericScope implementsScope,BeanFactoryPostProcessor,BeanDefinitionRegistryPostProcessor,DisposableBean{ @Override publicObjectget(Stringname,ObjectFactory>objectFactory){ //將bean添加到緩存cache中 BeanLifecycleWrappervalue=this.cache.put(name,newBeanLifecycleWrapper(name,objectFactory)); this.locks.putIfAbsent(name,newReentrantReadWriteLock()); try{ //調(diào)用下面的getBean方法 returnvalue.getBean(); } catch(RuntimeExceptione){ this.errors.put(name,e); throwe; } } privatestaticclassBeanLifecycleWrapper{ publicObjectgetBean(){ //如果bean為空,則創(chuàng)建bean if(this.bean==null){ synchronized(this.name){ if(this.bean==null){ this.bean=this.objectFactory.getObject(); } } } //否則返回之前創(chuàng)建好的bean returnthis.bean; } } }
小結(jié):
從這邊的代碼中可以印證了上面的說法,創(chuàng)建后的Bean會緩存到scope的cache中,優(yōu)先從緩存中獲取,如果緩存中是null, 則重新走一遍create bean的流程。
RefeshScope Bean的創(chuàng)建
上面的在getBean的時(shí)候依賴到RefreshScope這個(gè)Bean,那么這個(gè)Bean是在什么時(shí)候加入到Spring Bean中的呢?答案就是RefreshAutoConfiguration。
配置中心刷新后刷新Bean緩存
配置中心發(fā)生變化后,會收到一個(gè)RefreshEvent事件,RefreshEventListner監(jiān)聽器會監(jiān)聽到這個(gè)事件。
publicclassRefreshEventListenerimplementsSmartApplicationListener{ ........ publicvoidhandle(RefreshEventevent){ if(this.ready.get()){//don'thandleeventsbeforeappisready log.debug("Eventreceived"+event.getEventDesc()); //會調(diào)用refresh方法,進(jìn)行刷新 Setkeys=this.refresh.refresh(); log.info("Refreshkeyschanged:"+keys); } } } //這個(gè)是ContextRefresher類中的刷新方法 publicsynchronizedSet refresh(){ //刷新spring的envirionment變量配置 Set keys=refreshEnvironment(); //刷新其他scope this.scope.refreshAll(); returnkeys; }
refresh方法最終調(diào)用destroy方法,清空之前緩存的bean
publicclassRefreshScopeextendsGenericScope implementsApplicationContextAware,ApplicationListener,Ordered{ @ManagedOperation(description="Disposeofthecurrentinstanceofallbeans" +"inthisscopeandforcearefreshonnextmethodexecution.") publicvoidrefreshAll(){ //調(diào)用父類的destroy super.destroy(); this.context.publishEvent(newRefreshScopeRefreshedEvent()); } } @Override publicvoiddestroy(){ List errors=newArrayList (); Collection wrappers=this.cache.clear(); for(BeanLifecycleWrapperwrapper:wrappers){ try{ Locklock=this.locks.get(wrapper.getName()).writeLock(); lock.lock(); try{ //這里主要就是把之前的bean設(shè)置為null,就會重新走createBean的流程了 wrapper.destroy(); } finally{ lock.unlock(); } } catch(RuntimeExceptione){ errors.add(e); } } if(!errors.isEmpty()){ throwwrapIfNecessary(errors.get(0)); } this.errors.clear(); }
總結(jié)
上面是這個(gè)RefreshScope實(shí)現(xiàn)動態(tài)刷新大致的原理,其中里面還有很多細(xì)節(jié),可能需要留給大家自己debug去深入理解。
審核編輯:劉清
-
cache技術(shù)
+關(guān)注
關(guān)注
0文章
41瀏覽量
1064 -
null
+關(guān)注
關(guān)注
0文章
19瀏覽量
3974
原文標(biāo)題:Nacos+@RefreshScope 為什么配置能動態(tài)刷新?
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論