Spring Cloud @RefreshScope 原理及使用
@RefreshScope那些事
要说清楚RefreshScope,先要了解Scope
- Scope(org.springframework.beans.factory.config.Scope)是Spring2.0开始就有的核心的概念
- RefreshScope(org.springframework.cloud.context.scope.refresh)是springcloud提供的一种特殊的scope实现,用来实现配置、实例热加载。
- Scope->GenericScope->RefreshScope
Scope与ApplicationContext生命周期
AbstractBeanFactory#doGetBean创建Bean实例
protectedTdoGetBean(...){ finalRootBeanDefinitionmbd=... if(mbd.isSingleton()){ ... }elseif(mbd.isPrototype()) ... }else{ StringscopeName=mbd.getScope(); finalScopescope=this.scopes.get(scopeName); ObjectscopedInstance=scope.get(beanName,newObjectFactory
Singleton和Prototype是硬编码的,并不是Scope子类。Scope实际上是自定义扩展的接口
ScopeBean实例交由Scope自己创建,例如SessionScope是从Session中获取实例的,ThreadScope是从ThreadLocal中获取的,而RefreshScope是在内建缓存中获取的。
@Scope对象的实例化
@RefreshScope是scopeName="refresh"的@Scope
...
@Scope("refresh")
public@interfaceRefreshScope{
...
}
@Scope的注册AnnotatedBeanDefinitionReader#registerBean
publicvoidregisterBean(...){
...
ScopeMetadatascopeMetadata=this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
...
definitionHolder=AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata,definitionHolder,this.registry);
}
读取@Scope元数据,AnnotationScopeMetadataResolver#resolveScopeMetadata
publicScopeMetadataresolveScopeMetadata(BeanDefinitiondefinition){
AnnotationAttributesattributes=AnnotationConfigUtils.attributesFor(
annDef.getMetadata(),Scope.class);
if(attributes!=null){
metadata.setScopeName(attributes.getString("value"));
ScopedProxyModeproxyMode=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)
protectedstaticclassRefreshScopeConfigurationimplementsBeanDefinitionRegistryPostProcessor{
...
registry.registerBeanDefinition("refreshScope",
BeanDefinitionBuilder.genericBeanDefinition(RefreshScope.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.getBeanDefinition());
...
}
RefreshScopeextendsGenericScope,大部分逻辑在GenericScope中
GenericScope#postProcessBeanFactory中向AbstractBeanFactory注册自己
publicclassGenericScopeimplementsScope,BeanFactoryPostProcessor...{
@Override
publicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)
throwsBeansException{
beanFactory.registerScope(this.name/*refresh*/,this/*RefreshScope*/);
...
}
}
RefreshScope刷新过程
入口在ContextRefresher#refresh
refresh(){
Mapbefore=①extract(
this.context.getEnvironment().getPropertySources());
②addConfigFilesToEnvironment();
Setkeys=④changes(before,
③extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.⑤publishEvent(newEnvironmentChangeEvent(keys));
this.scope.⑥refreshAll();
}
①提取标准参数(SYSTEM,JNDI,SERVLET)之外所有参数变量
②把原来的Environment里的参数放到一个新建的SpringContext容器下重新加载,完事之后关闭新容器
③提起更新过的参数(排除标准参数)
④比较出变更项
⑤发布环境变更事件,接收:EnvironmentChangeListener/LoggingRebinder
⑥RefreshScope用新的环境参数重新生成Bean
重新生成的过程很简单,清除refreshscope缓存幷销毁Bean,下次就会重新从BeanFactory获取一个新的实例(该实例使用新的配置)
RefreshScope#refreshAll
publicvoidrefreshAll(){
super.destroy();
this.context.publishEvent(newRefreshScopeRefreshedEvent());
}
GenericScope#destroy
publicvoiddestroy(){
...
Collectionwrappers=this.cache.clear();
for(BeanLifecycleWrapperwrapper:wrappers){
wrapper.destroy();
}
}
SpringCloudBus如何触发Refresh
BusAutoConfiguration#BusRefreshConfiguration发布一个RefreshBusEndpoint
@Configuration
@ConditionalOnClass({Endpoint.class,RefreshScope.class})
protectedstaticclassBusRefreshConfiguration{
@Configuration
@ConditionalOnBean(ContextRefresher.class)
@ConditionalOnProperty(value="endpoints.spring.cloud.bus.refresh.enabled",matchIfMissing=true)
protectedstaticclassBusRefreshEndpointConfiguration{
@Bean
publicRefreshBusEndpointrefreshBusEndpoint(ApplicationContextcontext,
BusPropertiesbus){
returnnewRefreshBusEndpoint(context,bus.getId());
}
}
}
RefreshBusEndpoint会从http端口触发广播RefreshRemoteApplicationEvent事件
@Endpoint(id="bus-refresh")
publicclassRefreshBusEndpointextendsAbstractBusEndpoint{
publicvoidbusRefresh(){
publish(newRefreshRemoteApplicationEvent(this,getInstanceId(),null));
}
}
BusAutoConfiguration#refreshListener负责接收事件(所有配置bus的节点)
@Bean
@ConditionalOnProperty(value="spring.cloud.bus.refresh.enabled",matchIfMissing=true)
@ConditionalOnBean(ContextRefresher.class)
publicRefreshListenerrefreshListener(ContextRefreshercontextRefresher){
returnnewRefreshListener(contextRefresher);
}
RefreshListener#onApplicationEvent触发ContextRefresher
publicvoidonApplicationEvent(RefreshRemoteApplicationEventevent){
Setkeys=contextRefresher.refresh();
}
大部分需要更新的服务需要打上@RefreshScope,EurekaClient是如何配置更新的
EurekaClientAutoConfiguration#RefreshableEurekaClientConfiguration
@Configuration
@ConditionalOnRefreshScope
protectedstaticclassRefreshableEurekaClientConfiguration{
@Bean
@RefreshScope
publicEurekaClienteurekaClient(...){
returnnewCloudEurekaClient(manager,config,this.optionalArgs,
this.context);
}
@Bean
@RefreshScope
publicApplicationInfoManagereurekaApplicationInfoManager(...){
...
returnnewApplicationInfoManager(config,instanceInfo);
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。