一兆蕉、背景
項(xiàng)目中需要用的阿波羅熱發(fā)布,一種是利用注解ApolloConfigChangeListener的方式,可用如下配置就可缤沦。
@Component
@Slf4j
public class ApolloRefreshConfig2 implements ApplicationContextAware{
@Autowired
private RefreshScope refreshScope;
private ApplicationContext applicationContext;
@ApolloConfigChangeListener()
public void onChange(ConfigChangeEvent changeEvent) {
refreshProperties(changeEvent);
}
private void refreshProperties(ConfigChangeEvent changeEvent) {
this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
refreshScope.refreshAll();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
可是在測(cè)試中發(fā)現(xiàn)熱部署并沒有更新虎韵,后查看注解ApolloConfigChangeListener,發(fā)現(xiàn)如果不指定value值則默認(rèn)namespace為 application缸废。而我們項(xiàng)目中并沒有使用application包蓝,而是拆分出來更多的命名空間;所以并不適用企量,需要指定value测萎,如value={"edl-pub-service-common","Java.bmc-local-eureka","Java.bmc-pub-mysql","Java.bmc-redis","Java.bmc-rabbitmq","Java.bmc-httplog"};
@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};
String[] interestedKeys() default {};
String[] interestedKeyPrefixes() default {};
}
這里放一下我們的配置文件
# 應(yīng)用ID(在Apollo服務(wù)端新增項(xiàng)目添加的應(yīng)用ID)
app:
id: bmc
# apollo-configservice地址
apollo:
meta: http://10.197.236.187:7070
bootstrap:
enabled: true
namespaces: edl-pub-service-common,Java.bmc-local-eureka,Java.bmc-pub-mysql,Java.bmc-redis,Java.bmc-rabbitmq,Java.bmc-httplog
eagerLoad:
enabled: true
可以不想再value中硬編碼,那么問題來了:能否將value中的動(dòng)態(tài)配置届巩,比如讀取配置文件中的數(shù)據(jù)硅瞧?
二、嘗試
1.@ApolloConfigChangeListener()注解的value直接設(shè)置EL表達(dá)式恕汇,結(jié)果------失敗腕唧。
如下代碼設(shè)置
@ApolloConfigChangeListener(value = "${apollo.bootstrap.namespacesArr}")
public void onChange(ConfigChangeEvent changeEvent) {
refreshProperties(changeEvent);
}
經(jīng)過debug嘗試,發(fā)現(xiàn)Apollo會(huì)默認(rèn)注冊(cè) AutoUpdateConfigChangeListener監(jiān)聽器瘾英,如果掃描到ApolloConfigChangeListener注解也會(huì)注冊(cè)到監(jiān)聽器中(源碼見com.ctrip.framework.apollo.spring.annotation.ApolloAnnotationProcessor),可是在獲取此注解的時(shí)候并沒有判斷是不是EL表達(dá)式枣接,所以你傳入 {apollo.bootstrap.namespacesArr}缺谴;代碼如下
@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);
ReflectionUtils.makeAccessible(method);
// *********** 重點(diǎn)在這里 但惶,直接讀取 ***********
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;
for (String namespace : namespaces) {
Config config = ConfigService.getConfig(namespace);
if (interestedKeys == null && interestedKeyPrefixes == null) {
config.addChangeListener(configChangeListener);
} else {
config.addChangeListener(configChangeListener, interestedKeys, interestedKeyPrefixes);
}
}
}
所以該我們配置文件的屬性改變因?yàn)椴皇侵付ǖ膎amespaces就不會(huì)更新。
2.利用反射動(dòng)態(tài)修改@ApolloConfigChangeListener()注解的value湿蛔,將value賦值為配置的屬性膀曾,結(jié)果------失敗。
此方法明顯行不通煌集,因?yàn)锳polloAnnotationProcessor中每次獲取的ApolloConfigChangeListener 開始設(shè)置的值妓肢,而不是通過我們反射獲取的值,反射的主要代碼如下
try {
method = ApolloRefreshConfig.class.getMethod("onChange", ConfigChangeEvent.class);
ApolloConfigChangeListener annotation = method.getAnnotation(ApolloConfigChangeListener.class);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
Field value = invocationHandler.getClass().getDeclaredField("memberValues");
value.setAccessible(true);
Map<String, String[]> stringObjectMap = (Map<String, String[]>) value.get(invocationHandler);
stringObjectMap.put("value", namespacesArr);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
3.新寫監(jiān)聽器苫纤,加入監(jiān)聽器集合中,結(jié)果------失敗纲缓。
因?yàn)槭冀K讀取不到配置文件卷拘,猜測(cè)跟ApolloProcessor 繼承 BeanPostProcessor有關(guān)吧。
@Component
@Slf4j
public class ApolloRefreshListener extends ApolloProcessor implements ApplicationContextAware {
@Autowired
private RefreshScope refreshScope;
@Value("${apollo.bootstrap.namespaces}")
private String namespaces;
private ApplicationContext applicationContext;
@Autowired
private ConfigPropertySourceFactory configPropertySourceFactory;
/* @PostConstruct
private void init(){
List<String> namespacesList = Lists.newArrayList();
Splitter.on(",").omitEmptyStrings().split(namespaces).forEach(item -> namespacesList.add(item));
namespacesArr = new String[namespacesList.size()];
}*/
@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);
String namespace = annotation.value();
Config config = ConfigService.getConfig(namespace);
ReflectionUtils.makeAccessible(field);
ReflectionUtils.setField(field, bean, config);
}
@Override
protected void processMethod(final Object bean, String beanName, final Method method) {
ApolloRefreshConfig apolloRefreshListener = new ApolloRefreshConfig();
List<ConfigPropertySource> allConfigPropertySources = configPropertySourceFactory.getAllConfigPropertySources();
for (ConfigPropertySource allConfigPropertySource : allConfigPropertySources) {
Config config = ConfigService.getConfig(allConfigPropertySource.getName());
config.addChangeListener(apolloRefreshListener);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
三祝高、結(jié)論
暫無解決辦法栗弟,目前只有寫死。