spring在启动时会读取yml中对应的配置app.apollo.bootstrap.namespace,通过该命名空间去apollo读取对应的配置。详见:
SpringBoot启动加载Apollo配置过程_xl649138628的专栏-CSDN博客
但是在实践中我们发现yml中未配置application命名空间的情况下我们还是能够读取application的配置。现在我们通过追踪源码来拨开迷雾寻找真相。
Springboot在启动时有一个启动类,Springboot在继承apollo需要在该启动类里配置@EnableApolloConfig注解。在该注解中我们需要注意两点
1.@Import(ApolloConfigRegistrar.class) // 服务启动时appliationcontext通过扫描import注解执行里面的registerBeanDefinitions方法注入bean
2.ConfigConsts.NAMESPACE_APPLICATION // 配置默认的命名空间application
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ApolloConfigRegistrar.class) // 通过import注入bean
public @interface EnableApolloConfig {
/**
* Apollo namespaces to inject configuration into Spring Property Sources.
*/
String[] value() default {ConfigConsts.NAMESPACE_APPLICATION}; // 默认未application
/**
* The order of the apollo config, default is {@link Ordered#LOWEST_PRECEDENCE}, which is Integer.MAX_VALUE.
* If there are properties with the same name in different apollo configs, the apollo config with smaller order wins.
* @return
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
我们点击ApolloConfigRegistrar,进入到下面的代码,我们发现调用了
ApolloConfigRegistrarHelper的registerBeanDefinitions方法。
public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {
private ApolloConfigRegistrarHelper helper = ServiceBootstrap.loadPrimary(ApolloConfigRegistrarHelper.class);
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
helper.registerBeanDefinitions(importingClassMetadata, registry);
}
}
点击该方法的实现方法发现是在DefaultApolloConfigRegistrarHelper类中实现的。在该方法中我们发现了获取了EnableApolloConfig注解的value值,这个value值我们前面看到是application。
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(EnableApolloConfig.class.getName()));
String[] namespaces = attributes.getStringArray("value");
int order = attributes.getNumber("order");
PropertySourcesProcessor.addNamespaces(Lists.newArrayList(namespaces), order);
Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
// ....省略
}
他是通过PropertySourcesProcessor方法里的addNamespaces方法将application设置到PropertySourcesProcessor的全局变量NAMESPACE_NAME中。
public static boolean addNamespaces(Collection<String> namespaces, int order) {
return NAMESPACE_NAMES.putAll(order, namespaces);
}
我们继续分析PropertySourcesProcessor类,在下图中我们发现实现了BeanFactoryPostProcessor接口,该接口有一个未实现的方法postProcessBeanFacotry在PropertySourcesProcessor中被实现,该方法也是在上下文加载时调用的,但是调用时机在registerBeanDefinitions方法的后面。
public class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware, PriorityOrdered {
// .... 省略
}
我们继续来分析这个postProcessBeanFacotry的实现方法
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 实例化PropertySources
initializePropertySources();
initializeAutoUpdatePropertiesFeature(beanFactory);
}
进入initializePropertySources方法,我们发现
Config config = ConfigService.getConfig(namespace)这段代码可以通过namespace获取到apollo配置。namespace是通过NAMESPACE_NAME获取到的,而NAMESPACE_NAME里的值是application。这样即使yml里没有配置application,springboot在启动时也加载了application里的配置信息。
private void initializePropertySources() {
if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME)) {
//already initialized
return;
}
CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_PROPERTY_SOURCE_NAME);
//sort by order asc
ImmutableSortedSet<Integer> orders = ImmutableSortedSet.copyOf(NAMESPACE_NAMES.keySet());
Iterator<Integer> iterator = orders.iterator();
while (iterator.hasNext()) {
int order = iterator.next();
for (String namespace : NAMESPACE_NAMES.get(order)) {
// 获取apollo配置
Config config = ConfigService.getConfig(namespace);
composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
}
}
// clean up
NAMESPACE_NAMES.clear();
// add after the bootstrap property source or to the first
if (environment.getPropertySources()
.contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
// ensure ApolloBootstrapPropertySources is still the first
ensureBootstrapPropertyPrecedence(environment);
environment.getPropertySources()
.addAfter(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME, composite);
} else {
environment.getPropertySources().addFirst(composite);
}
}