一文帶你理解@RefreshScope注解實(shí)現(xiàn)動(dòng)態(tài)刷新原理
注解的作用
@RefreshScope
注解是Spring Cloud中的一個(gè)注解,用來實(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用來比較Spring Bean的作用域,具體使用參考相關(guān)文章。
- 注解的屬性proxyMode默認(rèn)使用TARGET_CLASS作為代理。
實(shí)例
- controller中添加
@RefreshScope
- nacos配置中心中配置
- 驗(yàn)證, 修改配置中心后,可以不重啟動(dòng),刷新配置
- 去掉
@RefreshScope
就不會(huì)自動(dòng)刷新。
原理解析
為了實(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
注解的類,在被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è)過程就達(dá)到了動(dòng)態(tài)刷新配置的效果。
獲取RefreshScope注解的Bean
通過打上斷點(diǎn)查看堆??芍?/p>
- 因?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)類RefreshScope,這個(gè)類在哪里注入,我們后面講 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類,最終調(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é):
從這邊的代碼中可以印證了上面的說法,創(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)聽器會(huì)監(jiān)聽到這個(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類中的刷新方法 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)用父類的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)文章
使用Backoff策略提高HttpClient連接管理的效率
這篇文章主要為大家介紹了Backoff策略提高HttpClient連接管理的效率使用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Java模擬QQ實(shí)現(xiàn)聊天互動(dòng)程序
這篇文章主要介紹了如何利用Java語言模擬QQ實(shí)現(xiàn)一個(gè)簡(jiǎn)易的聊天互動(dòng)程序,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-06-06關(guān)于SpringCloud的微服務(wù)以及組件詳解
這篇文章主要介紹了關(guān)于SpringCloud的微服務(wù)以及組件詳解,是一個(gè)更高層次的、 架構(gòu)視角的綜合性大型項(xiàng)目, 他的目標(biāo)是構(gòu)建一套標(biāo)準(zhǔn)化的微服務(wù)解決方案,需要的朋友可以參考下2023-05-05Java設(shè)計(jì)模式之原型模式(Prototype模式)介紹
這篇文章主要介紹了Java設(shè)計(jì)模式之原型模式(Prototype模式)介紹,本文講解了如何使用原型模式并給出了代碼實(shí)例,需要的朋友可以參考下2015-03-03spring中@Transactional?注解失效的原因及解決辦法
面試中經(jīng)常會(huì)被問到事務(wù)失效的場(chǎng)景有哪些,本文主要介紹了spring中@Transactional?注解失效的原因及解決辦法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06完美解決Logback configuration error detected的問題
這篇文章主要介紹了完美解決Logback configuration error detected的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08java selenium 常見web UI 元素操作及API使用
本文主要介紹java selenium 常見web UI 元素操作,這里幫大家整理了相關(guān)資料并附示例代碼,有需要的小伙伴可以參考下2016-08-08MyBatis之傳入?yún)?shù)為list、數(shù)組、map的寫法
這篇文章主要介紹了MyBatis之傳入?yún)?shù)為list、數(shù)組、map的寫法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11