@EnableApolloConfig
java客户端集成Apollo只需要启动类加上@EnableApolloConfig,或者配合@Configuration一起使用即可
package com.ctrip.framework.apollo.spring.annotation;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(ApolloConfigRegistrar.class)
public @interface EnableApolloConfig {
/**
* namespace的数组
*/
String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
/**
* 优先级,数字越小优先级越高
* 默认是Integer最大值
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
ApolloConfigRegistrar
package com.ctrip.framework.apollo.spring.annotation;
/**
* @author Jason Song([email protected])
*/
public class ApolloConfigRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 解析 @EnableApolloConfig 注解
AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata
.getAnnotationAttributes(EnableApolloConfig.class.getName()));
String[] namespaces = attributes.getStringArray("value");
int order = attributes.getNumber("order");
// 添加到 PropertySourcesProcessor 中
PropertySourcesProcessor.addNamespaces(Lists.newArrayList(namespaces), order);
Map<String, Object> propertySourcesPlaceholderPropertyValues = new HashMap<>();
// to make sure the default PropertySourcesPlaceholderConfigurer's priority is higher than PropertyPlaceholderConfigurer
propertySourcesPlaceholderPropertyValues.put("order", 0);
// 注册 PropertySourcesPlaceholderConfigurer 到 BeanDefinitionRegistry 中,替换 PlaceHolder 为对应的属性值,参考文章 https://leokongwq.github.io/2016/12/28/spring-PropertyPlaceholderConfigurer.html
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesPlaceholderConfigurer.class.getName(),
PropertySourcesPlaceholderConfigurer.class, propertySourcesPlaceholderPropertyValues);
//【差异】注册 PropertySourcesProcessor 到 BeanDefinitionRegistry 中,因为可能存在 XML 配置的 Bean ,用于 PlaceHolder 自动更新机制
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, PropertySourcesProcessor.class.getName(),
PropertySourcesProcessor.class);
// 注册 ApolloAnnotationProcessor 到 BeanDefinitionRegistry 中,解析 @ApolloConfig 和 @ApolloConfigChangeListener 注解。
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloAnnotationProcessor.class.getName(),
ApolloAnnotationProcessor.class);
// 注册 SpringValueProcessor 到 BeanDefinitionRegistry 中,用于 PlaceHolder 自动更新机制
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueProcessor.class.getName(), SpringValueProcessor.class);
//【差异】注册 SpringValueDefinitionProcessor 到 BeanDefinitionRegistry 中,因为可能存在 XML 配置的 Bean ,用于 PlaceHolder 自动更新机制
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, SpringValueDefinitionProcessor.class.getName(), SpringValueDefinitionProcessor.class);
// 注册 ApolloJsonValueProcessor 到 BeanDefinitionRegistry 中,解析 @ApolloJsonValue 注解。
BeanRegistrationUtil.registerBeanDefinitionIfNotExists(registry, ApolloJsonValueProcessor.class.getName(),
ApolloJsonValueProcessor.class);
}
}
如果要在项目中解耦,不想使用@EnableApolloConfig,
可以实现BeanDefinitionRegistryPostProcessor
加上@Configuration
复写postProcessBeanDefinitionRegistry,在里面执行ApolloConfigRegistrar中的代码即可
可以自行加上一些控制代码
注解
@ApolloJsonValue
package com.ctrip.framework.apollo.spring.annotation;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Documented
public @interface ApolloJsonValue {
/**
* The actual value expression: e.g. "${someJsonPropertyKey:someDefaultValue}".
*/
String value();
}
将 Apollo 任意格式的 Namespace 的一个 Item 配置项,解析成对应类型的对象,注入到 @ApolloJsonValue
的对象中
具体解析参照ApolloJsonValueProcessor
@ApolloConfig
package com.ctrip.framework.apollo.spring.annotation;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface ApolloConfig {
/**
* Apollo namespace for the config, if not specified then default to application
*/
String value() default ConfigConsts.NAMESPACE_APPLICATION;
}
将 Apollo Config 对象注入
具体解析参照ApolloAnnotationProcessor
@ApolloConfigChangeListener
package com.ctrip.framework.apollo.spring.annotation;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ApolloConfigChangeListener {
/**
* Apollo namespace for the config, if not specified then default to application
*/
String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
/**
* The keys interested by the listener, will only be notified if any of the interested keys is changed.
* <br />
* If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when any key is changed.
*/
String[] interestedKeys() default {};
/**
* 当且仅当更改的key以任何listener监听的前缀开头时,才会通知listener。
* The prefixes will simply be used to determine whether the {@code listener} should be notified or not using {@code changedKey.startsWith(prefix)}.
* e.g. "spring." means that {@code listener} is interested in keys that starts with "spring.", such as "spring.banner", "spring.jpa", etc.
* and "application" means that {@code listener} is interested in keys that starts with "application", such as "applicationName", "application.port", etc.
* <br />
* If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when whatever key is changed.
*/
String[] interestedKeyPrefixes() default {};
}
处理器
ApolloJsonValueProcessor
无参构造
private final ConfigUtil configUtil;
private final PlaceholderHelper placeholderHelper;
private final SpringValueRegistry springValueRegistry;
private ConfigurableBeanFactory beanFactory;
public ApolloJsonValueProcessor() {
configUtil = ApolloInjector.getInstance(ConfigUtil.class);
placeholderHelper = SpringInjector.getInstance(PlaceholderHelper.class);
springValueRegistry = SpringInjector.getInstance(SpringValueRegistry.class);
}
processField
@Override
protected void processField(Object bean, String beanName, Field field) {
ApolloJsonValue apolloJsonValue = AnnotationUtils.getAnnotation(field, ApolloJsonValue.class);
if (apolloJsonValue == null) {
return;
}
// 获得 Placeholder 表达式
String placeholder = apolloJsonValue.value();
// 解析对应的值
Object propertyValue = placeholderHelper
.resolvePropertyValue(beanFactory, beanName, placeholder);
// 忽略,非 String 值
if (!(propertyValue instanceof String)) {
return;
}
// 设置到 Field 中
boolean accessible = field.isAccessible();
field.setAccessible(true);
ReflectionUtils
.setField(field, bean, parseJsonValue((String)propertyValue, field.getGenericType()));
field.setAccessible(accessible);
// 是否开启自动更新机制
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
// 提取keys属性
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeholder);
// 循环 `keys` ,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。
for (String key : keys) {
SpringValue springValue = new SpringValue(key, placeholder, bean, beanName, field, true);
springValueRegistry.register(beanFactory, key, springValue);
logger.debug("Monitoring {}", springValue);
}
}
}
processMethod
@Override
protected void processMethod(Object bean, String beanName, Method method) {
ApolloJsonValue apolloJsonValue = AnnotationUtils.getAnnotation(method, ApolloJsonValue.class);
if (apolloJsonValue == null) {
return;
}
// 获得 Placeholder 表达式
String placeHolder = apolloJsonValue.value();
// 解析对应的值
Object propertyValue = placeholderHelper
.resolvePropertyValue(beanFactory, beanName, placeHolder);
// 忽略,非 String 值
// propertyValue will never be null, as @ApolloJsonValue will not allow that
if (!(propertyValue instanceof String)) {
return;
}
Type[] types = method.getGenericParameterTypes();
Preconditions.checkArgument(types.length == 1,
"Ignore @Value setter {}.{}, expecting 1 parameter, actual {} parameters",
bean.getClass().getName(), method.getName(), method.getParameterTypes().length);
// 调用 Method ,设置值
boolean accessible = method.isAccessible();
method.setAccessible(true);
ReflectionUtils.invokeMethod(method, bean, parseJsonValue((String) propertyValue, types[0]));
method.setAccessible(accessible);
// 是否开启自动更新机制
if (configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()) {
// 提取 `keys` 属性们。
Set<String> keys = placeholderHelper.extractPlaceholderKeys(placeHolder);
// 循环 `keys` ,创建对应的 SpringValue 对象,并添加到 `springValueRegistry` 中。
for (String key : keys) {
SpringValue springValue = new SpringValue(key, apolloJsonValue.value(), bean, beanName,
method, true);
springValueRegistry.register(beanFactory, key, springValue);
logger.debug("Monitoring {}", springValue);
}
}
}
ApolloAnnotationProcessor
处理
@ApolloConfig
和@ApolloConfigChangeListener
注解处理器的初始化
processField
@Override
protected void processField(Object bean, String beanName, Field field) {
ApolloConfig annotation = AnnotationUtils.getAnnotation(field, ApolloConfig.class);
if (annotation == null) {
return;
}
Preconditions.checkArgument(Config.class.isAssignableFrom(field.getType()),
"Invalid type: %s for field: %s, should be Config", field.getType(), field);
// 创建 Config 对象
String namespace = annotation.value();
Config config = ConfigService.getConfig(namespace);
// 设置 Config 对象,到对应的 Field
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, config);
}
processMethod
@Override
protected void processMethod(final Object bean, String beanName, final Method method) {
ApolloConfigChangeListener annotation = AnnotationUtils
.findAnnotation(method, ApolloConfigChangeListener.class);
if (annotation == null) {
return;
}
Class<?>[] parameterTypes = method.getParameterTypes();
Preconditions.checkArgument(parameterTypes.length == 1,
"Invalid number of parameters: %s for method: %s, should be 1", parameterTypes.length,
method);
Preconditions.checkArgument(ConfigChangeEvent.class.isAssignableFrom(parameterTypes[0]),
"Invalid parameter type: %s for method: %s, should be ConfigChangeEvent", parameterTypes[0],
method);
// 创建 ConfigChangeListener 监听器。该监听器会调用被注解的方法。
ReflectionUtils.makeAccessible(method);
String[] namespaces = annotation.value();
String[] annotatedInterestedKeys = annotation.interestedKeys();
String[] annotatedInterestedKeyPrefixes = annotation.interestedKeyPrefixes();
ConfigChangeListener configChangeListener = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
ReflectionUtils.invokeMethod(method, bean, changeEvent);
}
};
Set<String> interestedKeys = annotatedInterestedKeys.length > 0 ? Sets.newHashSet(annotatedInterestedKeys) : null;
Set<String> interestedKeyPrefixes = annotatedInterestedKeyPrefixes.length > 0 ? Sets.newHashSet(annotatedInterestedKeyPrefixes) : null;
// 向指定 Namespace 的 Config 对象们,注册该监听器
for (String namespace : namespaces) {
Config config = ConfigService.getConfig(namespace);
if (interestedKeys == null && interestedKeyPrefixes == null) {
config.addChangeListener(configChangeListener);
} else {
config.addChangeListener(configChangeListener, interestedKeys, interestedKeyPrefixes);
}
}
}