(1). 概述

在前面查看了半天的:@EnableApolloConfig的源码,始终没有找到我们想要的内容.
即:Apollo向Spring靠拢,始终是要围绕着这个类:Config来做适配来着的,那咋办?
找/META-INF/spring.factories,这是Spring的启动之前的回调点.

(2). apollo-client-xxx.jar/META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ctrip.framework.apollo.spring.boot.ApolloAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
org.springframework.boot.env.EnvironmentPostProcessor=\
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer

(3). ApolloApplicationContextInitializer

public class ApolloApplicationContextInitializer 
       implements ApplicationContextInitializer<ConfigurableApplicationContext> , 
	              EnvironmentPostProcessor, 
				  Ordered {
	
	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);
	
	// 1. Spring回调:EnvironmentPostProcessor.postProcessEnvironment
	public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
	    
		// 从ConfigurableEnvironment获取如下变量:
		// [app.id, apollo.cluster, apollo.cacheDir, apollo.accesskey.secret, apollo.meta, apollo.property.order.enable]
		// 重新设置到:  System.setProperty(propertyName, propertyValue);
		// should always initialize system properties like app.id in the first place
	    initializeSystemProperty(configurableEnvironment);
	
		// apollo.bootstrap.eagerLoad.enabled = false
	    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;
	    }
	
		// apollo.bootstrap.enabled=true
	    Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);
	
	    // bootstrapEnabled为true时,才会调用:initialize
	    if (bootstrapEnabled) {
	      initialize(configurableEnvironment);
	    }
	} // end postProcessEnvironment
	
	// 2. Spring回调:ApplicationContextInitializer.initialize
	public void initialize(ConfigurableApplicationContext context) {
	    ConfigurableEnvironment environment = context.getEnvironment();
		
		// apollo.bootstrap.enabled=true
	    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);
	} // end initialize
	
	
	protected void initialize(ConfigurableEnvironment environment) {
		
		// 判断PropertySource配置里,是否有:ApolloBootstrapPropertySources
		// PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME = ApolloBootstrapPropertySources
		if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) { //false
		  //already initialized
		  return;
		}
		
		// APOLLO_BOOTSTRAP_NAMESPACES = apollo.bootstrap.namespaces
		// NAMESPACE_APPLICATION = application
		// 获取配置文件中设置的namespaces: apollo.bootstrap.namespaces
		String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
		logger.debug("Apollo bootstrap namespaces: {}", namespaces);
		// 按逗号分隔namespaces
		List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);

		// 3. 创建一个组合的:CompositePropertySource
		CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
		
		// 4. namespaceList = [TEST1.jdbc, application]
		for (String namespace : namespaceList) {
		  // ************************************************************************
		  // 终于见到核心类:Config
		  // ************************************************************************
		  Config config = ConfigService.getConfig(namespace);
		  
		  // ************************************************************************
		  // 通过ConfigPropertySourceFactory创建ConfigPropertySource
		  // ************************************************************************
		  composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
		}
		
		// *********************************************************************
		// 向容器中注册:PropertySource,而且,还是放在首位,意味着:编写顺序在最后的namespace具有优先查找功能.
		// 比如: namespaces = [TEST1.jdbc, application]
		// 而 environment.getPropertySources().addFirst()后的结果是:  [application, TEST1.jdbc]
		//      key = server.port
		//     先在:application查找,找不到,再到:TEST1.jdbc里查找.
		// *********************************************************************
		environment.getPropertySources().addFirst(composite);
    }// end initialize
}

(4). ConfigPropertySourceFactory

1) Apollo自定义了ConfigPropertySource(PropertySource的实现类).
2) 同时,ConfigPropertySource内部持有Apollo的Config对象,最终会委托到:Config获取配置信息.
3) 把ConfigPropertySource向Spring中注册(environment.getPropertySources().addFirst(composite)).
4) 还有不懂的,请参考我前面的文章,自定义PropertySource

public class ConfigPropertySourceFactory {

  private final List<ConfigPropertySource> configPropertySources = Lists.newLinkedList();

  // ***************************************************************************
  // Config类提供了获取配置的方法: 
  //  String getProperty(String key, String defaultValue);
  // ***************************************************************************
  public ConfigPropertySource getConfigPropertySource(String name, Config source) {
	  // 1. 自定义:ConfigPropertySource属于:PropertySource的子类.
	  // 2. ConfigPropertySource内部又委托给了:Config类,到此,一切问题就已经打通了.
    ConfigPropertySource configPropertySource = new ConfigPropertySource(name, source);
	configPropertySources.add(configPropertySource);
    return configPropertySource;
  }

  public List<ConfigPropertySource> getAllConfigPropertySources() {
    return Lists.newLinkedList(configPropertySources);
  }
}

(5). 总结

Spring与Apollo的结合,实际就是自定义了PropertySource,然后,向Spring的Environment中注册,获取配置最终会委托给:Config.getProperty方法,所以,后面,专心剖析:Config.