天天看點

Nacos源碼分析八、配置動态重新整理(1)

回顧一下前面分析的内容,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服務端配置:

Nacos源碼分析八、配置動态重新整理(1)

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。

Nacos源碼分析八、配置動态重新整理(1)

然後我們将目光轉向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 + "]");
      }
   }
}
           
Nacos源碼分析八、配置動态重新整理(1)

總結一下

  1. 被@RefreshScope注解的類會建立一個代理類的bean定義(實際上是@Scope注解就會建立,@RefreshScope是繼承了它)
  2. RefreshScope類用于處理@RefreshScope引入的代理類bean定義。主要做三件事:
    1. 把類型從ScopedProxyFactoryBean換成了LockedScopedProxyFactoryBean,同時注入了RefreshScope執行個體進去

      為什麼換類型呢?實際上ScopedProxyFactoryBean是由spring定義了,它隻是定義了一個擴充作用域的規範,使用ScopedProxyFactoryBean來定義如何得到執行個體對象。而具體到每個擴充作用域的個性邏輯則由對應的子類實作,即:

      @Scope --> @RefreshScope

      ScopedProxyFactoryBean --> LockedScopedProxyFactoryBean

    2. beanFactory注冊一個refresh的作用域

到這裡我們基本了解了@RefreshScope和RefreshScope的作用,下面主要分析LockedScopedProxyFactoryBean是如何生成代理對象的。