接入Apollo后,我们可以通过注解方式一行注解,即可获取所需的配置信息(由于Apollo支持热部署,能够实现动态加载)
1.定义用来标识Apolo配置的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface ApolloValue {
String value() default "";
}
2.定义映射配置信息的对象
import lombok.Data;
@Data
public class OwlApolloConfigBean {
@ApolloValue("demo.demo")
private String demo;
}
3.接入Apollo,获取配置信息,并通过监听事件,实现动态加载。
/**
* @author XingPengLong
* @date 2018-06-13 下午9:10.
*/
@Slf4j
@Configuration
@EnableApolloConfig
public class OwlApolloConfig {
private static Config appConfig;
private static final String APOLLO_KEY = "owlApolloConfigBean";
private static ConcurrentHashMap<String, OwlApolloConfigBean> concurrentHashMap = new ConcurrentHashMap<>();
static {
//config instance is singleton for each namespace and is never null
appConfig = ConfigService.getAppConfig();
appConfig.addChangeListener(changeEvent -> {
for (String key : changeEvent.changedKeys()) {
log.info("OwlApolloConfig#static before concurrentHashMap:{}", JSON.toJSONString(concurrentHashMap));
ConfigChange change = changeEvent.getChange(key);
OwlApolloConfigBean owlApolloConfigBean = concurrentHashMap.get(APOLLO_KEY);
switch (change.getChangeType()) {
case MODIFIED:
String propertyName = change.getPropertyName();
String newValue = change.getNewValue();
if (StringUtils.isEmpty(newValue)) {
break;
}
Field[] declaredFields = owlApolloConfigBean.getClass().getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
Annotation[] declaredAnnotations = field.getDeclaredAnnotations();
for (Annotation annotation : declaredAnnotations) {
if (annotation instanceof ApolloValue) {
String value = ((ApolloValue) annotation).value();
if (propertyName.equals(value)) {
try {
field.set(owlApolloConfigBean, change.getNewValue());
} catch (IllegalAccessException e) {
log.error("set field meet error:" + field.getName());
}
}
break;
}
}
}
break;
case ADDED:
break;
case DELETED:
break;
default:
return;
}
log.info("OwlApolloConfig#static after concurrentHashMap:{}", JSON.toJSONString(concurrentHashMap));
}
});
}
@Bean
public OwlApolloConfigBean getOwlApolloConfigBean() {
Set<String> propertyNames = appConfig.getPropertyNames();
OwlApolloConfigBean owlApolloConfigBean = concurrentHashMap.get(APOLLO_KEY);
if (owlApolloConfigBean == null) {
owlApolloConfigBean = new OwlApolloConfigBean();
}
Field[] declaredFields = owlApolloConfigBean.getClass().getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
Annotation[] declaredAnnotations = field.getDeclaredAnnotations();
for (Annotation annotation : declaredAnnotations) {
if (annotation instanceof ApolloValue) {
String value = ((ApolloValue) annotation).value();
if (propertyNames.contains(value)) {
try {
field.set(owlApolloConfigBean, appConfig.getProperty(value, ""));
} catch (IllegalAccessException e) {
log.error("set field meet error:" + field.getName());
}
}
break;
}
}
}
log.info("OwlApolloConfig#getOwlApolloConfigBean concurrentHashMap:{}", JSON.toJSONString(concurrentHashMap));
concurrentHashMap.putIfAbsent(APOLLO_KEY, owlApolloConfigBean);
log.info("OwlApolloConfig#getOwlApolloConfigBean concurrentHashMap:{}", JSON.toJSONString(concurrentHashMap));
return owlApolloConfigBean;
}
}
4.使用
@RestController
public class DemoController {
@Autowired
private OwlApolloConfig owlApolloConfig;
@GetMapping(value = "/")
public PlainResult<String> getOwlApolloConfigBeanDemo() {
String demo = owlApolloConfig.getOwlApolloConfigBean().getDemo();
return PlainResultBuilder.build(demo);
}
}
5.总结
以上方法仅仅实现了注解接入Apollo,后续有更好的办法,如映射对象分组、无缝加入到Spring容器等。待续。
优化:2018/11/05 加入Spring容器管理
1.定义配置信息映射的对象
@ConfigurationProperties("gciBiz")
@Getter
@Setter
public class GciBizProperties {
private String demo;
}
|
2.通过Spring提供的 ApplicationReadyEvent 钩子,拿到通过ConfigurationProperties 标记的类对象,测试发现这些类均是以 (类型-类所在包的全路径)的格式由Spring容器进行管理。
/**
* Apollo服务配置
* 注:暂时仅支持以逗号分隔的两级嵌套的结构
*
* @author XingPengLong
* @date 2018-09-19 下午5:26.
*/
@EnableConfigurationProperties(GciBizProperties.class)
@Slf4j
@Configuration
@EnableApolloConfig
public class ApolloConfiguration implements ApplicationListener<ApplicationReadyEvent> {
private static final String COM_YOUZAN_OWL_GCI_BIZ_CONFIG_BEAN_APOLLO = "-com.youzan.owl.gci.biz.config.bean.apollo";
private static final String PROPERTY_NAME_REGEX = "\\.";
private ApplicationContext applicationContext;
private void init() {
Config appConfig = ConfigService.getAppConfig();
appConfig.addChangeListener((ConfigChangeEvent changeEvent) -> {
for (String key : changeEvent.changedKeys()) {
ConfigChange change = changeEvent.getChange(key);
switch (change.getChangeType()) {
case MODIFIED:
wrapperModifier(change);
break;
case ADDED:
break;
case DELETED:
break;
default:
return;
}
}
});
}
private void wrapperModifier(ConfigChange change) {
String propertyName = change.getPropertyName();
if (StringUtils.isEmpty(change.getNewValue())) {
return;
}
String[] split = propertyName.split(PROPERTY_NAME_REGEX);
//暂时仅支持两级嵌套
if (split.length == 2) {
applicationContext.getBeansWithAnnotation(ConfigurationProperties.class).entrySet().stream()
.filter(k -> k.getKey().startsWith(split[0] + COM_YOUZAN_OWL_GCI_BIZ_CONFIG_BEAN_APOLLO))
.forEach(item -> setAvailableFieldValue(change, split[1], item));
}
}
private void setAvailableFieldValue(ConfigChange change, String anObject, Map.Entry<String, Object> item) {
Field[] declaredFields = item.getValue().getClass().getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
if (field.getName().equals(anObject)) {
try {
field.set(item.getValue(), change.getNewValue());
} catch (IllegalAccessException i) {
log.error("setAvailableFieldValue meet error:" + field.getName(), i);
}
break;
}
}
log.info("ApolloConfiguration {}", JSON.toJSONString(item.getValue()));
}
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
if (applicationReadyEvent.getApplicationContext().getParent() == null) {
this.applicationContext = applicationReadyEvent.getApplicationContext();
init();
}
}
}
|
3.使用
@RestController
public class DemoController {
@Autowired
private GciBizProperties gciBizProperties;
@GetMapping(value = "/test")
public String test() {
return gciBizProperties.getDemo();
}
|
暂时仅支持以逗号分隔的两级嵌套的结构,比较僵硬,未完待续。。。
本文介绍了如何通过注解方式简单接入Apollo,实现配置信息的动态加载。详细步骤包括定义注解、创建配置对象、监听Apollo事件以及在Spring容器中管理配置。目前支持逗号分隔的两级嵌套配置,但仍有待优化和完善。

814

被折叠的 条评论
为什么被折叠?



