Spring Cloud @RefreshScope 原理是什么?
要清楚RefreshScope,先要了解Scope
Scope(org.springframework.beans.factory.config.Scope)是Spring 2.0開始就有的核心的概念
RefreshScope(org.springframework.cloud.context.scope.refresh)是spring cloud提供的一種特殊的scope實現,用來實現配置、實例熱加載。
Scope -> GenericScope -> RefreshScope
Scope與ApplicationContext生命周期
AbstractBeanFactory#doGetBean創建Bean實例
protected?<T>?T?doGetBean(...){final?RootBeanDefinition?mbd?=?...if?(mbd.isSingleton())?{...}?else?if?(mbd.isPrototype())...}?else?{String?scopeName?=?mbd.getScope();final?Scope?scope?=?this.scopes.get(scopeName);Object?scopedInstance?=?scope.get(beanName,?new?ObjectFactory<Object>()?{...});...}... }Singleton和Prototype是硬編碼的,并不是Scope子類。
Scope實際上是自定義擴展的接口,Scope?Bean實例交由Scope自己創建,例如SessionScope是從Session中獲取實例的,ThreadScope是從ThreadLocal中獲取的,而RefreshScope是在內建緩存中獲取的。
@Scope 對象的實例化
@RefreshScope 是scopeName="refresh"的 @Scope
... @Scope("refresh") public?@interface?RefreshScope?{... }@Scope 的注冊 AnnotatedBeanDefinitionReader#registerBean
public?void?registerBean(...){ ... ScopeMetadata?scopeMetadata?=?this.scopeMetadataResolver.resolveScopeMetadata(abd);abd.setScope(scopeMetadata.getScopeName());...definitionHolder?=?AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata,?definitionHolder,?this.registry); }讀取@Scope元數據, AnnotationScopeMetadataResolver#resolveScopeMetadata
public?ScopeMetadata?resolveScopeMetadata(BeanDefinition?definition)?{AnnotationAttributes?attributes?=?AnnotationConfigUtils.attributesFor(annDef.getMetadata(),?Scope.class);if?(attributes?!=?null)?{metadata.setScopeName(attributes.getString("value"));ScopedProxyMode?proxyMode?=?attributes.getEnum("proxyMode");if?(proxyMode?==?null?||?proxyMode?==?ScopedProxyMode.DEFAULT)?{proxyMode?=?this.defaultProxyMode;}metadata.setScopedProxyMode(proxyMode);} }Scope實例對象通過ScopedProxyFactoryBean創建,其中通過AOP使其實現ScopedObject接口,這里不再展開。
說RefreshScope是如何實現配置和實例刷新的
RefreshScope注冊
RefreshAutoConfiguration#RefreshScopeConfiguration
@Component @ConditionalOnMissingBean(RefreshScope.class) protected?static?class?RefreshScopeConfiguration?implements?BeanDefinitionRegistryPostProcessor{ ...registry.registerBeanDefinition("refreshScope",BeanDefinitionBuilder.genericBeanDefinition(RefreshScope.class).setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition()); ... }RefreshScope extends GenericScope, 大部分邏輯在 GenericScope 中。
GenericScope#postProcessBeanFactory 中向AbstractBeanFactory注冊自己
public?class?GenericScope?implements?Scope,?BeanFactoryPostProcessor...{@Overridepublic?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)throws?BeansException?{beanFactory.registerScope(this.name/*refresh*/,?this/*RefreshScope*/);...} }RefreshScope 刷新過程
入口在ContextRefresher#refresh
refresh()?{Map<String,?Object>?before?=?①extract(this.context.getEnvironment().getPropertySources());②addConfigFilesToEnvironment();Set<String>?keys?=?④changes(before,③extract(this.context.getEnvironment().getPropertySources())).keySet();this.context.⑤publishEvent(new?EnvironmentChangeEvent(keys));this.scope.⑥refreshAll();}①提取標準參數(SYSTEM,JNDI,SERVLET)之外所有參數變量
②把原來的Environment里的參數放到一個新建的Spring Context容器下重新加載,完事之后關閉新容器
③提起更新過的參數(排除標準參數)
④比較出變更項
⑤發布環境變更事件,接收:EnvironmentChangeListener/LoggingRebinder
⑥RefreshScope用新的環境參數重新生成Bean,重新生成的過程很簡單,清除refreshscope緩存幷銷毀Bean,下次就會重新從BeanFactory獲取一個新的實例(該實例使用新的配置)
RefreshScope#refreshAll
public?void?refreshAll()?{<b>super.destroy();</b>this.context.publishEvent(new?RefreshScopeRefreshedEvent()); }GenericScope#destroy
public?void?destroy()?{...Collection<BeanLifecycleWrapper>?wrappers?=?<b>this.cache.clear()</b>;for?(BeanLifecycleWrapper?wrapper?:?wrappers)?{<b>wrapper.destroy();</b>} }Spring Cloud Bus 如何觸發 Refresh
BusAutoConfiguration#BusRefreshConfiguration 發布一個RefreshBusEndpoint
@Configuration @ConditionalOnClass({?Endpoint.class,?RefreshScope.class?}) protected?static?class?BusRefreshConfiguration?{@Configuration@ConditionalOnBean(ContextRefresher.class)@ConditionalOnProperty(value?=?"endpoints.spring.cloud.bus.refresh.enabled",?matchIfMissing?=?true)protected?static?class?BusRefreshEndpointConfiguration?{@Beanpublic?RefreshBusEndpoint?refreshBusEndpoint(ApplicationContext?context,BusProperties?bus)?{return?new?RefreshBusEndpoint(context,?bus.getId());}} }RefreshBusEndpoint 會從http端口觸發廣播RefreshRemoteApplicationEvent事件
@Endpoint(id?=?"bus-refresh") public?class?RefreshBusEndpoint?extends?AbstractBusEndpoint?{public?void?busRefresh()?{publish(new?RefreshRemoteApplicationEvent(this,?getInstanceId(),?null));} }BusAutoConfiguration#refreshListener 負責接收事件(所有配置bus的節點)
@Bean @ConditionalOnProperty(value?=?"spring.cloud.bus.refresh.enabled",?matchIfMissing?=?true) @ConditionalOnBean(ContextRefresher.class) public?RefreshListener?refreshListener(ContextRefresher?contextRefresher)?{return?new?RefreshListener(contextRefresher); }RefreshListener#onApplicationEvent 觸發 ContextRefresher
public?void?onApplicationEvent(RefreshRemoteApplicationEvent?event)?{Set<String>?keys?=?contextRefresher.refresh(); }大部分需要更新的服務需要打上@RefreshScope, EurekaClient是如何配置更新的
EurekaClientAutoConfiguration#RefreshableEurekaClientConfiguration
@Configuration @ConditionalOnRefreshScope protected?static?class?RefreshableEurekaClientConfiguration{@Bean@RefreshScopepublic?EurekaClient?eurekaClient(...)?{return?new?CloudEurekaClient(manager,?config,?this.optionalArgs,this.context);}@Bean@RefreshScopepublic?ApplicationInfoManager?eurekaApplicationInfoManager(...)?{...return?new?ApplicationInfoManager(config,?instanceInfo);}}作者:黃大海
https://www.jianshu.com/p/188013dd3d02
總結
以上是生活随笔為你收集整理的Spring Cloud @RefreshScope 原理是什么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 做一个完整的Java Web项目太难了,
- 下一篇: 厉害了!SpringBoot是如何动起来