SpringCloud之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
@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;
}
- @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...{
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
beanFactory.registerScope(this.name/*refresh*/, this/*RefreshScope*/);
...
}
}
RefreshScope 刷新過程
- 入口在ContextRefresher#refresh
public synchronized Set<String> refresh() {
Set<String> keys = refreshEnvironment();
this.scope.refreshAll();
return keys;
}
public synchronized Set<String> refreshEnvironment() {
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(this.context, keys));
return keys;
}
- ①提取標准參數(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>
}
}