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(); Set keys=④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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。