Apollo源码解析——集成到springboot

本文详细解读了SpringBoot如何与Apollo集成,涉及自动配置、SpringValue处理器、配置属性注入和变更监听机制。重点解析了ApolloAutoConfiguration、ApplicationContextInitializer和关键组件的工作流程,以及配置属性如何自动更新到Spring应用中。

文章目录


前言

这里就不说SpringBoot自动装备的原理,ApolloSpringBoot的整合就是借助了SpringBoot的SPI机制,下面我们来看下处理流程


一、SpringBoot集成apollo源码分析

1. spring.factories

我们都知道spring.factoriesSpringBoot的扩展点,是实现自动装配的核心原理,Apollo客户端就是借助这个SPI机制与SpringBoot整合,我们看下apollojar包中的META-INFO/spring.factories文件
在这里插入图片描述

1.1 ApolloAutoConfiguration解析

这里看名字是实现apollo的自动配置,在配置类中会判断如果spring中没有PropertySourcesProcessor这个bean,那么他会new一个ConfigPropertySourcesProcessor到容器中,确保这个bean一定存在,这个bean的作用我们后续会解析

在这里插入图片描述

1.2ApolloApplicationContextInitializer解析

ApolloApplicationContextInitializer实现了两个接口ApplicationContextInitializerEnvironmentPostProcessorSpring Boot启动过程,会先调用EnvironmentPostProcessor.postProcessEnvironment方法,再调用ApplicationContextInitializer.initialize方法。也就是Spring Boot优先准备环境,再初始化容器。

1.2.1 ApolloApplicationContextInitializer#postProcessEnvironment

这里首先就是检查apollo.bootstrap.eagerLoad.enabled属性,判断是否允许在Spring启动初期加载apollo配置,如果允许则继续检查apollo.bootstrap.enabled属性判断是否允许加载apollo,如果都允许,则调用initialize方法进行初始化

  @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) {
   
   
      DeferredLogger.enable();
      //初始化Apollo的配置
      initialize(configurableEnvironment);
    }

  }
1.2.2 ApolloApplicationContextInitializer#initialize
  1. 这里会发现他从apollo.bootstrap.namespaces属性配置中获取namespaces,然后遍历命名空间列表,调用ConfigService$getConfig拉取远程配置,然后将拉取到的配置包装成PropertySource,然后将包装好的PropertySource添加到CompositePropertySource内部,最后 CompositePropertySource 属性源包装类添加到 Spring 的 Environment 环境中,注意是插入在属性源列表的头部,因为取属性的时候其实是遍历这个属性源列表来查找,找到即返回,所以出现同名属性是以前面的为准
  protected void initialize(ConfigurableEnvironment environment) {
   
   

    if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
   
   
      //already initialized, replay the logs that were printed before the logging system was initialized
      DeferredLogger.replayTo();
      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);

    CompositePropertySource composite;
    final ConfigUtil configUtil = ApolloInjector.getInstance(ConfigUtil.class);
    if (configUtil.isPropertyNamesCacheEnabled()) {
   
   
      composite = new CachedCompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
    } else {
   
   
      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);
  }
1.2.2.1 configPropertySourceFactory#getConfigPropertySource
  1. ApolloConfig封装成Spring ConfigConfigPropertySource对象
  2. 将新生成的ConfigPropertySource对象添加到内部列表,方便后续为每个配置实例添加配置变化监听器。
  public ConfigPropertySource getConfigPropertySource(String name, Config source) {
   
   
    //将Apollo的Config配置封装为继承自Spring内置的EnumerablePropertySource类的ConfigPropertySource对象
    ConfigPropertySource configPropertySource = new ConfigPropertySource(name, source);
    //将新生成的ConfigPropertySource对象缓存到内部列表,以备后续为每个配置实例添加配置变化监听器使用。
    configPropertySources.add(configPropertySource);

    return configPropertySource;
  }
1.2.2.2 ConfigService#getConfig
  1. 这里getManager去获取ConfigManager实例,采用了javaServiceLoader机制,是SPI机制
  2. 这里看ConfigManager的实现类其实只有一个,就是DefaultConfigManager
  public static Config getConfig(String namespace) {
   
   
    return s_instance.getManager().getConfig(namespace);
  }
  private ConfigManager getManager() {
   
   
    //双重检查锁防止重复创建
    if (m_configManager == null) {
   
   
      synchronized (this) {
   
   
        if (m_configManager == null) {
   
   
          //使用ApolloInjector获取ConfigManager实例,这里是采用了java的ServiceLoeader机制
          m_configManager = ApolloInjector.getInstance(ConfigManager.class);
        }
      }
    }

    return m_configManager;
  }
1.2.2.2 DefaultConfigManager#getConfig
  1. 这里首先从缓存中获取配置,缓存中没有则从远程拉取
  2. 这里使用SPI机制获取Factory,默认是DefaultConfigFactory,这里会调用DefaultConfigFactory#create从远程拉取配置
  public Config getConfig(String namespace) {
   
   
    Config config = m_configs.get(namespace);

    if (config == null) {
   
   
      synchronized (this) {
   
   
        config = m_configs.get(namespace);

        if (config == null) {
   
   
          //这里Factory创建也使用了ServiceLoader机制,默认是DefaultConfigFactory
          ConfigFactory factory = m_factoryManager.getFactory(namespace);

          config = factory.create(namespace);
          m_configs.put(namespace, config);
        }
      }
    }

    return config;
  }
1.2.2.3 DefaultConfigFactory#create

1.这里重点是首先调用createLocalConfigRepository(namespace)会创建LocalConfigRepository,在createLocalConfigRepository()方法中又会调用createRemoteConfigRepository(namespace)创建RemoteConfigRepository,最后又会调用createRepositoryConfig创建DefaultConfig对象,内部持有LocalConfigRepository
2. 对象创建时序:DefaultConfig -> LocalFileConfigRepository -> RemoteConfigRepository
3. 对象关系:DefaultConfig -> 持有 LocalFileConfigRepository, LocalFileConfigRepository -> 持有 RemoteConfigRepository
3. 配置变化传播时序:RemoteConfigRepository -> LocalFileConfigRepository

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值