javascript
Spring Cloud @RefreshScope 原理是什么?
要清楚RefreshScope,先要了解Scope
Scope(org.springframework.beans.factory.config.Scope)是Spring 2.0開(kāi)始就有的核心的概念
RefreshScope(org.springframework.cloud.context.scope.refresh)是spring cloud提供的一種特殊的scope實(shí)現(xiàn),用來(lái)實(shí)現(xiàn)配置、實(shí)例熱加載。
Scope -> GenericScope -> RefreshScope
Scope與ApplicationContext生命周期
AbstractBeanFactory#doGetBean創(chuàng)建Bean實(shí)例
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實(shí)際上是自定義擴(kuò)展的接口,Scope?Bean實(shí)例交由Scope自己創(chuàng)建,例如SessionScope是從Session中獲取實(shí)例的,ThreadScope是從ThreadLocal中獲取的,而RefreshScope是在內(nèi)建緩存中獲取的。
@Scope 對(duì)象的實(shí)例化
@RefreshScope 是scopeName="refresh"的 @Scope
... @Scope("refresh") public?@interface?RefreshScope?{... }@Scope 的注冊(cè) AnnotatedBeanDefinitionReader#registerBean
public?void?registerBean(...){ ... ScopeMetadata?scopeMetadata?=?this.scopeMetadataResolver.resolveScopeMetadata(abd);abd.setScope(scopeMetadata.getScopeName());...definitionHolder?=?AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata,?definitionHolder,?this.registry); }讀取@Scope元數(shù)據(jù), 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實(shí)例對(duì)象通過(guò)ScopedProxyFactoryBean創(chuàng)建,其中通過(guò)AOP使其實(shí)現(xiàn)ScopedObject接口,這里不再展開(kāi)。
說(shuō)RefreshScope是如何實(shí)現(xiàn)配置和實(shí)例刷新的
RefreshScope注冊(cè)
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注冊(cè)自己
public?class?GenericScope?implements?Scope,?BeanFactoryPostProcessor...{@Overridepublic?void?postProcessBeanFactory(ConfigurableListableBeanFactory?beanFactory)throws?BeansException?{beanFactory.registerScope(this.name/*refresh*/,?this/*RefreshScope*/);...} }RefreshScope 刷新過(guò)程
入口在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.⑥r(nóng)efreshAll();}①提取標(biāo)準(zhǔn)參數(shù)(SYSTEM,JNDI,SERVLET)之外所有參數(shù)變量
②把原來(lái)的Environment里的參數(shù)放到一個(gè)新建的Spring Context容器下重新加載,完事之后關(guān)閉新容器
③提起更新過(guò)的參數(shù)(排除標(biāo)準(zhǔn)參數(shù))
④比較出變更項(xiàng)
⑤發(fā)布環(huán)境變更事件,接收:EnvironmentChangeListener/LoggingRebinder
⑥RefreshScope用新的環(huán)境參數(shù)重新生成Bean,重新生成的過(guò)程很簡(jiǎn)單,清除refreshscope緩存幷銷毀Bean,下次就會(huì)重新從BeanFactory獲取一個(gè)新的實(shí)例(該實(shí)例使用新的配置)
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 如何觸發(fā) Refresh
BusAutoConfiguration#BusRefreshConfiguration 發(fā)布一個(gè)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 會(huì)從http端口觸發(fā)廣播RefreshRemoteApplicationEvent事件
@Endpoint(id?=?"bus-refresh") public?class?RefreshBusEndpoint?extends?AbstractBusEndpoint?{public?void?busRefresh()?{publish(new?RefreshRemoteApplicationEvent(this,?getInstanceId(),?null));} }BusAutoConfiguration#refreshListener 負(fù)責(zé)接收事件(所有配置bus的節(jié)點(diǎn))
@Bean @ConditionalOnProperty(value?=?"spring.cloud.bus.refresh.enabled",?matchIfMissing?=?true) @ConditionalOnBean(ContextRefresher.class) public?RefreshListener?refreshListener(ContextRefresher?contextRefresher)?{return?new?RefreshListener(contextRefresher); }RefreshListener#onApplicationEvent 觸發(fā) ContextRefresher
public?void?onApplicationEvent(RefreshRemoteApplicationEvent?event)?{Set<String>?keys?=?contextRefresher.refresh(); }大部分需要更新的服務(wù)需要打上@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
總結(jié)
以上是生活随笔為你收集整理的Spring Cloud @RefreshScope 原理是什么?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 做一个完整的Java Web项目太难了,
- 下一篇: 微服务为什么一定要Zookeeper?