回顧一下前面分析的内容,NacosConfigAutoConfiguration定義了NacosContextRefresher監聽器,監聽ApplicationReadyEvent事件,當觸發事件後會往nacos的configService中注冊配置監聽,Nacos在收到配置變更時會向applicationContext釋出RefreshEvent事件。下面我們分析觸發這個事件的後續操作。
首先我們定義一下測試代碼:
@RestController
@RequestMapping("/refreshConfig")
@RefreshScope
public class RefreshController {
@Value("${refresh.config}")
private String config;
@GetMapping
public String getConfig(){
return config;
}
}
這裡的refresh.config配置由nacos服務端配置:
bootstrap.yml配置:
spring:
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
prefix: demo
group: DEMO_GROUP
profiles:
active: dev
在搞清楚整個流程之前,我們需要了解一些關于Spring-cloud的内容。 首先是@RefreshScope注解和RefreshScope類
@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;
}
這個@RefreshScope注解主要聲明一個類的對象的作用域和重新整理相關,既不是單例也不是原型,而且是會進行動态代理的。
@Scope("refresh")
表示他是跟
refresh
的作用域相關。
我們看一下spring的包掃描部分ClassPathBeanDefinitionScanner的doScan方法:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 周遊所有的包路徑
for (String basePackage : basePackages) {
// 找到包下的Bean定義
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
// 設定預設值, 有個BeanDefinitionDefaults對象定義預設值
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// Lazy、Primary、DependsOn、Role、Description
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 根據ScopedProxyMode生成代理Holder
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注冊
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
關注 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);這一行,根據scope的中繼資料生成對應的bean定義包裝類holder:
static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
return definition;
}
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}
然後是ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);這行:
public static BeanDefinitionHolder createScopedProxy(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
}
@RefreshScope的proxyMode屬性預設是TARGET_CLASS,是以,這裡proxyTargetClass是true。
然後是ScopedProxyUtils.createScopedProxy方法:
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
BeanDefinitionRegistry registry, boolean proxyTargetClass) {
// 原bean的名字
String originalBeanName = definition.getBeanName();
//原bean定義
BeanDefinition targetDefinition = definition.getBeanDefinition();
//代理bean名字,加了scopedTarget.字首
String targetBeanName = getTargetBeanName(originalBeanName);
// Create a scoped proxy definition for the original bean name,
// "hiding" the target bean in an internal target definition.
// 代理類的bean定義,類型是ScopedProxyFactoryBean
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
//設定裝飾的bean定義,把原bean定義放進去
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
// 設定被代理的bean定義
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole());
// targetBeanName屬性注入
proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
if (proxyTargetClass) {
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
}
else {
// proxyTargetClass屬性注入
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
// Copy autowire settings from original bean definition.
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
}
// The target bean should be ignored in favor of the scoped proxy.
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
// Register the target bean as separate bean in the factory.
// 被代理的bean定義也會注冊進去,但是beanName換掉了,是scopedTarget.XXX格式。
registry.registerBeanDefinition(targetBeanName, targetDefinition);
// Return the scoped proxy definition as primary bean definition
// (potentially an inner bean).
// 傳回代理的bean定義,beanName用原來的,内容換了。外面也會注冊。
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
分析一下這段代碼:主要是多了一個bean定義,原bean定義的beanName換掉了,換成scopedTarget.xxx格式,而xxx的beanName對應的是代理類。對應的類型是ScopedProxyFactoryBean。
然後我們将目光轉向spring-cloud-context的spring.factories檔案:
# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.WritableEnvironmentEndpointAutoConfiguration
我們看到EnableAutoConfiguration配置中有一個RefreshAutoConfiguration,點進去看一下:
@Bean
@ConditionalOnMissingBean(RefreshScope.class)
public static RefreshScope refreshScope() {
return new RefreshScope();
}
定義了一個RefreshScope類的Bean,這個類是和@RefreshScope注解對應的, 實際上就是針對@RefreshScope注解的bean的處理器。我們點進去看一下:
public class RefreshScope extends GenericScope implements ApplicationContextAware,
ApplicationListener<ContextRefreshedEvent>, Ordered {
看一下父類的定義:
public class GenericScope implements Scope, BeanFactoryPostProcessor,
BeanDefinitionRegistryPostProcessor, DisposableBean {
主要實作兩個處理器BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
首先是BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,在容器的bean定義注冊完成後執行:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
for (String name : registry.getBeanDefinitionNames()) {
BeanDefinition definition = registry.getBeanDefinition(name);
if (definition instanceof RootBeanDefinition) {
RootBeanDefinition root = (RootBeanDefinition) definition;
if (root.getDecoratedDefinition() != null && root.hasBeanClass()
&& root.getBeanClass() == ScopedProxyFactoryBean.class) {
// 如果是ScopedProxyFactoryBean這個類型的
if (getName().equals(root.getDecoratedDefinition().getBeanDefinition()
.getScope())) { // 此時name是refresh
// 換掉類型
root.setBeanClass(LockedScopedProxyFactoryBean.class);
// 構造方法添加自身作為參數
root.getConstructorArgumentValues().addGenericArgumentValue(this);
// surprising that a scoped proxy bean definition is not already
// marked as synthetic?
// 合成 标記
root.setSynthetic(true);
}
}
}
}
}
可以看到,代理類的類型從ScopedProxyFactoryBean換成了LockedScopedProxyFactoryBean,同時構造方法注入RefreshScope對象進去。
然後是BeanFactoryPostProcessor的postProcessBeanFactory方法:
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
this.beanFactory = beanFactory;
// 注冊refresh作用域
beanFactory.registerScope(this.name, this);
setSerializationId(beanFactory);
}
這個是beanFactory的後置處理器,當beanFactory準備完成後執行。主要是注冊了一個作用域:refresh
@Override
public void registerScope(String scopeName, Scope scope) {
Assert.notNull(scopeName, "Scope identifier must not be null");
Assert.notNull(scope, "Scope must not be null");
if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
}
// 添加到緩存裡
Scope previous = this.scopes.put(scopeName, scope);
if (previous != null && previous != scope) {
if (logger.isDebugEnabled()) {
logger.debug("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
}
}
}
總結一下
- 被@RefreshScope注解的類會建立一個代理類的bean定義(實際上是@Scope注解就會建立,@RefreshScope是繼承了它)
- RefreshScope類用于處理@RefreshScope引入的代理類bean定義。主要做三件事:
-
把類型從ScopedProxyFactoryBean換成了LockedScopedProxyFactoryBean,同時注入了RefreshScope執行個體進去
為什麼換類型呢?實際上ScopedProxyFactoryBean是由spring定義了,它隻是定義了一個擴充作用域的規範,使用ScopedProxyFactoryBean來定義如何得到執行個體對象。而具體到每個擴充作用域的個性邏輯則由對應的子類實作,即:
@Scope --> @RefreshScope
ScopedProxyFactoryBean --> LockedScopedProxyFactoryBean
- beanFactory注冊一個refresh的作用域
-
到這裡我們基本了解了@RefreshScope和RefreshScope的作用,下面主要分析LockedScopedProxyFactoryBean是如何生成代理對象的。