Apollo-client初始化入口流程解析

1,019 阅读4分钟

如需参考源码解析,请访问:gitee.com/lidishan/ap…
阅读前声明:本文不做相关用法说明,只解析Apollo-client源码

apollo中有一个注解,接下来先看一下这个启动注解

@EnableApolloConfig

为什么要使用这个注解?

系统启动时,会检索对应xml配置信息,并拉取apollo数据在postProcessAfterInitialization阶段进行覆盖

配置注解初始化流程如下:

第一步:在启动类上添加注解@EnableApolloConfig

@EnableApolloConfig
// ......其他注解
public class Boot {
    public static void main(String[] args) {
        new SpringApplication(Boot.class).run(args);
    }
}

第二步:点进注解,会发现用@Import(ApolloConfigRegistrar.class)注入了这个类进bean容器

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ApolloConfigRegistrar.class) // @Import(ApolloConfigRegistrar.class)注入了这个类进bean容器
public @interface EnableApolloConfig {
  /** 注入spring property的命名空间,默认:application */
  String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
  /** order最大,执行顺序排在最后面 */
  int order() default Ordered.LOWEST_PRECEDENCE;
}

第三步:上面发现会将ApolloConfigRegistrar注入bean容器,其大致逻辑(附源码分析):

  1. 用SPI记载机制加载类ApolloConfigRegistrarHelper

-- 这步是在19年新增的,估计是想引入SPI用自定义的加载注册类

  1. PropertySourcesProcessor配置初始化(里面还提供了属性自动更新的onChange方法)

-- 注册了监听方法,监听触发的时候从springValueRegistry取值,然后找到对应的bean相关信息反射进行重新赋值

  1. 注册所有继承了ApolloProcessor接口的bean(ApolloProcessor里面有postProcessBeforeInitialization 、postProcessAfterInitialization这两个在初始化前后执行的钩子),然后在钩子里面实现模板方法,并扫描对应注解进行赋值操作

实例化的bean如下:

ApolloAnnotationProcessor:
- 1 通过反射获取注解@ApolloConfig,并解析获取值,然后反射注入对应的bean的field
- 2 将声明了@ApolloConfigChangeListener注解的方法添加进对应命名空间Namespace的配置中 SpringValueProcessor:解析被@Value声明的field和method,然后把bean和键值等信息包装放到SpringValueRegistry中 SpringValueDefinitionProcessor:找出所有的bean映射的value信息,并存储在springValueDefinitions。举个例子:mybatis的初始化类里面的参数跟xml映射的属性 ApolloJsonValueProcessor:扫描所有注解,并加载赋值进去。这个注解作用是可以更方便解析json

-- 源码分析步骤如下:

1. SPI加载。可在外层自定义继承了ApolloConfigRegistrarHelper的实现类,然后加个@Order注解调到最大,就能覆盖当前默认加载的方式,变成自己的加载方式

// 底层还是借助ServiceLoader.load(clazz) spi的类加载机制来加载,然后根据order来排序取最大的那个(@Order中数字越小排序越大)
private ApolloConfigRegistrarHelper helper = ServiceBootstrap.loadPrimary(ApolloConfigRegistrarHelper.class);
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    helper.registerBeanDefinitions(importingClassMetadata, registry);
}

2. PropertySourcesProcessor配置初始化(里面还提供了属性自动更新的onChange方法)

-- 注册了监听方法,监听触发的时候从springValueRegistry取值,然后找到对应的bean相关信息反射进行重新赋值

// 调用路径:DefaultApolloConfigRegistrarHelper->PropertySourcesProcessor
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
    // PropertySourcesProcessor:配置初始化(里面还提供了属性自动更新的onChange方法)
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(), PropertySourcesProcessor.class);
}
public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware, PriorityOrdered {
  // ...........
  /**  **/
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    // 初始化属性配置
    initializePropertySources();
    // 初始化属性自动更新的
    initializeAutoUpdatePropertiesFeature(beanFactory);
  }
  // ...........
}
/**
 * 监听变更,如果配置有变更就会触发这个事件
 */
@Override
public void onChange(ConfigChangeEvent changeEvent) {
    Set<String> keys = changeEvent.changedKeys();
    if (CollectionUtils.isEmpty(keys)) {
        return;
    }
    for (String key : keys) {
        // 1. check whether the changed key is relevant
        // 从springValueRegistry中取值,然后找到对应的bean实例,通过反射重新设置值
        Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
        if (targetValues == null || targetValues.isEmpty()) {
            continue;
        }
        // 2. update the value
        for (SpringValue val : targetValues) {
            updateSpringValue(val);
        }
    }
}

3. 注册所有继承了ApolloProcessor接口的bean(ApolloProcessor里面有postProcessBeforeInitialization

、postProcessAfterInitialization这两个在初始化前后执行的钩子),然后在钩子里面实现模板方法,并扫描对应注解进行赋值操作

// 调用路径:DefaultApolloConfigRegistrarHelper->PropertySourcesProcessor
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // ...................其他逻辑
    // ================以下类均实现了ApolloProcessor==================================
    // ==========================里面有postProcessBeforeInitialization、postProcessAfterInitialization,
    // ==========================里面采用模板方法模式,下面只需要实现对应模板方法processField()、processMethod()即可
    // - 1 通过反射获取注解@ApolloConfig,并解析获取值,然后反射注入对应的bean的field
    // - 2 将声明了@ApolloConfigChangeListener注解的方法添加进对应命名空间Namespace的配置中
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(), ApolloAnnotationProcessor.class);
    // - 1 解析被@Value声明的field和method,然后把bean和键值等信息包装放到SpringValueRegistry中
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(), SpringValueProcessor.class);
    // - 1 找出所有的bean映射的value信息,并存储在springValueDefinitions。举个例子:mybatis的初始化类里面的参数跟xml映射的属性
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueDefinitionProcessor.class.getName(), SpringValueDefinitionProcessor.class);
    // - 1 扫描所有注解,并加载赋值进去。这个注解作用是可以更方便解析json
    //    @ApolloJsonValue("${someJsonPropertyKey:{\"someString\":\"someDefaultValue\", \"someInt\":10}}")
    //    private SomeObject someObject;
    BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(), ApolloJsonValueProcessor.class);
    // ...................其他逻辑
}

备注:认真看会发现onChange()方法里面只会从springValueRegistry加载数据。而SpringValueDefinitionProcessor注册bean时是不会把数据存储在springValueRegistry (我们可以把它当做是一种不用注解用mybatis.xml之类配置解析的方法),这种情况下apollo是不会覆盖其值

除此之外,apolloc-client实现了springboot自动装载机制的包。其初始化方式如下

第一步:采用springboot自动装配机制加载,先看一下spring.factories加载了什么

#如果没有用apollo的注解,会用这个进行初始化一些扫描,不过这个跟apollo的注解区别是什么
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration
#初始化上下文参数(将上下文参数加载存储起来)
org.springframework.context.ApplicationContextInitializer=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
#将环境变量初始化(比如把 -Dapollo.meta=12, app.properties里面的参数设置等,这里会加载进来)
org.springframework.boot.env.EnvironmentPostProcessor=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer

第二步:先看下自动装配类ApolloAutoConfiguration

1. autoConfiguration只有在(apollo.bootstrap.enabled=true && bean=PropertySourcesProcessor 没有初始化) 才会起作用

// 这个autoConfiguration只有在apollo.bootstrap.enabled=true && bean=PropertySourcesProcessor 没有初始化
// 自动装配的时间节点要晚于@EnableApolloConfig生效的时间节点
@Configuration
@ConditionalOnProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED)// apollo.bootstrap.enabled=true才启动
@ConditionalOnMissingBean(PropertySourcesProcessor.class)// 这个bean=PropertySourcesProcessor 没有初始化,才会触发这里的初始化.用于跟注解的形式相互不冲。不使用注解的时候,就会触发当前自动配置类初始化
public class ApolloAutoConfiguration {
    @Bean
    public ConfigPropertySourcesProcessor configPropertySourcesProcessor() {
        return new ConfigPropertySourcesProcessor();
    }
}

2. 就算上实现autoConfiguration没有生效,也会触发ApplicationContextInitializer和EnvironmentPostProcessor的生效,

在apollo中这两个初始化和加载环境配置 是在同一个类:ApolloApplicationContextInitializer

下面分析一下ApolloApplicationContextInitializer的大致作用如下:

  • 该类核心作用:
    • 初始化apollo系统配置initialize()。遍历命名空间,调用ConfigService.getConfig()加载所有apollo配置
    • 注入apollo配置信息postProcessEnvironment()。初始化环境变量,会在initialize()前执行
/**
 * 初始化apollo系统配置initialize() 和 注入apollo配置信息postProcessEnvironment()
 * - ApplicationContextInitializer 是一个注入点,在spring容器初始化之前就会调用
 */
public class ApolloApplicationContextInitializer implements
    ApplicationContextInitializer<ConfigurableApplicationContext> , EnvironmentPostProcessor, Ordered {
  public static final int DEFAULT_ORDER = 0;

  private static final Logger logger = LoggerFactory.getLogger(ApolloApplicationContextInitializer.class);
  private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
  private static final String[] APOLLO_SYSTEM_PROPERTIES = {"app.id", ConfigConsts.APOLLO_CLUSTER_KEY,
      "apollo.cacheDir", "apollo.accesskey.secret", ConfigConsts.APOLLO_META_KEY, PropertiesFactory.APOLLO_PROPERTY_ORDER_ENABLE};

  private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector
      .getInstance(ConfigPropertySourceFactory.class);

  private int order = DEFAULT_ORDER;

  /**
   * 容器初始化之前就会调用这个接口
   * - 获取上下文环境
   * - initialize加载环境数据
   */
  @Override
  public void initialize(ConfigurableApplicationContext context) {
    // 获取环境变量信息
    ConfigurableEnvironment environment = context.getEnvironment();
    // 这个用于配置是否开启,默认是开启的。false则不开启
    if (!environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false)) {
      logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
      return;
    }
    logger.debug("Apollo bootstrap config is enabled for context {}", context);

    initialize(environment);
  }

  /**
   * 在环境变量准备好之后,初始化配置(就是遍历所有命名空间,然后通过ConfigService.getConfig()进行记载)
   */
  protected void initialize(ConfigurableEnvironment environment) {

    if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
      //already initialized
      return;
    }

    String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
    logger.debug("Apollo bootstrap namespaces: {}", namespaces);
    List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);

    // 会执行这个ConfigService.getConfig,然后初始化调用 长轮询 + 定时五分钟拉取
    CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
    for (String namespace : namespaceList) {
        // **重点**,后面会进行解析
      Config config = ConfigService.getConfig(namespace);
      composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
    }

    environment.getPropertySources().addFirst(composite);
  }

  /**
   * 加载环境变量,比如apollo的相关配置信息 -Dapollo.meta之类的
   */
  @Override
  public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
    // should always initialize system properties like app.id in the first place
    initializeSystemProperty(configurableEnvironment);
    Boolean eagerLoadEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, Boolean.class, false);
    //EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
    if (!eagerLoadEnabled) {
      return;
    }
    Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);
    if (bootstrapEnabled) {
      initialize(configurableEnvironment);
    }
  }
  // .....省略其他逻辑
}