如需参考源码解析,请访问:https://gitee.com/lidishan/apollo-code-analysis
阅读前声明:本文不做相关用法说明,只解析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容器,其大致逻辑(附源码分析):
- 用SPI记载机制加载类ApolloConfigRegistrarHelper
– 这步是在19年新增的,估计是想引入SPI用自定义的加载注册类
- PropertySourcesProcessor配置初始化(里面还提供了属性自动更新的onChange方法)
– 注册了监听方法,监听触发的时候从springValueRegistry取值,然后找到对应的bean相关信息反射进行重新赋值
- 注册所有继承了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);
}
}
// .....省略其他逻辑
}