Skip to content

Commit

Permalink
nacos config support type and bean factory (#3899)
Browse files Browse the repository at this point in the history
  • Loading branch information
shiyiyue1102 authored Nov 26, 2024
1 parent d93217f commit effabf4
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.alibaba.cloud.nacos.annotation;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
Expand All @@ -37,12 +38,22 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.NotReadablePropertyException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.ReflectionUtils;

public class NacosAnnotationProcessor implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware {
Expand All @@ -68,12 +79,12 @@ private String getGroupKeyContent(String dataId, String group) throws Exception
}
synchronized (this) {
if (!groupKeyCache.containsKey(GroupKey.getKey(dataId, group))) {
String content = nacosConfigManager.getConfigService().getConfig(dataId, group, 5000);
String content = getNacosConfigManager().getConfigService().getConfig(dataId, group, 5000);
groupKeyCache.put(GroupKey.getKey(dataId, group), new AtomicReference<>(content));

log.info("[Nacos Config] Listening config for annotation: dataId={}, group={}", dataId,
group);
nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() {
getNacosConfigManager().getConfigService().addListener(dataId, group, new AbstractListener() {
@Override
public void receiveConfigInfo(String s) {
groupKeyCache.get(GroupKey.getKey(dataId, group)).set(s);
Expand All @@ -94,13 +105,20 @@ public String toString() {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
Class clazz = bean.getClass();
NacosConfig annotationBean = AnnotationUtils.findAnnotation(clazz, NacosConfig.class);
if (annotationBean != null) {
handleBeanNacosConfigAnnotation(annotationBean.dataId(), annotationBean.group(), annotationBean.key(), beanName, bean, annotationBean.defaultValue());
return bean;
}

for (Field field : getBeanFields(clazz)) {
handleFiledAnnotation(bean, beanName, field);
}
Expand Down Expand Up @@ -129,6 +147,86 @@ private void handleFiledAnnotation(Object bean, String beanName, Field field) {
}
}

private void handleBeanNacosConfigAnnotation(String dataId, String group, String key, String beanName, Object bean,
String defaultValue) {
try {
String config = getDestContent(getGroupKeyContent(dataId, group), key);
if (!org.springframework.util.StringUtils.hasText(config)) {
config = defaultValue;
}
//Init bean properties.
if (org.springframework.util.StringUtils.hasText(config)) {
Object targetObject = convertContentToTargetType(config, bean.getClass());
//yaml and json to object
BeanUtils.copyProperties(targetObject, bean, getNullPropertyNames(targetObject));
}
String refreshTargetKey = beanName + "#instance#";
TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey);
if (currentTarget != null) {
log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey,
currentTarget.getTarget(), bean);
targetListenerMap.get(refreshTargetKey).setTarget(bean);
return;
}
log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey,
bean);
TargetRefreshable listener = null;
if (org.springframework.util.StringUtils.hasText(key)) {
listener = new NacosPropertiesKeyListener(bean, wrapArrayToSet(key)) {
@Override
public void configChanged(ConfigChangeEvent event) {
try {
ConfigChangeItem changeItem = event.getChangeItem(key);
String newConfig = changeItem == null ? null : changeItem.getNewValue();
if (!org.springframework.util.StringUtils.hasText(newConfig)) {
newConfig = defaultValue;
}
if (org.springframework.util.StringUtils.hasText(newConfig)) {
Object targetObject = convertContentToTargetType(newConfig, getTarget().getClass());
//yaml and json to object
BeanUtils.copyProperties(targetObject, getTarget(), getNullPropertyNames(targetObject));
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
}

@Override
public String toString() {
return String.format("[spring cloud alibaba nacos config instance key listener , key %s , target %s ] ", key, bean);
}
};
}
else {
listener = new NacosConfigRefreshableListener(bean) {
@Override
public void receiveConfigInfo(String configInfo) {
if (!org.springframework.util.StringUtils.hasText(configInfo)) {
configInfo = defaultValue;
}
if (org.springframework.util.StringUtils.hasText(configInfo)) {
Object targetObject = convertContentToTargetType(configInfo, bean.getClass());
//yaml and json to object
BeanUtils.copyProperties(targetObject, getTarget());
}
}

@Override
public String toString() {
return String.format("[spring cloud alibaba nacos config instance listener , key %s , target %s ] ", key, bean);
}
};
}
getNacosConfigManager().getConfigService()
.addListener(dataId, group, listener);
targetListenerMap.put(refreshTargetKey, listener);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}

private void handleMethodNacosConfigKeysChangeListener(NacosConfigKeysListener annotation, String beanName, Object bean,
Method method) {
String dataId = annotation.dataId();
Expand Down Expand Up @@ -166,7 +264,7 @@ public String toString() {
}
};
nacosPropertiesKeyListener.setLastContent(getGroupKeyContent(dataId, group));
nacosConfigManager.getConfigService().addListener(dataId, group,
getNacosConfigManager().getConfigService().addListener(dataId, group,
nacosPropertiesKeyListener);
targetListenerMap.put(refreshTargetKey, nacosPropertiesKeyListener);
}
Expand Down Expand Up @@ -278,7 +376,7 @@ public String toString() {
};
}

nacosConfigManager.getConfigService().addListener(dataId, group, listener);
getNacosConfigManager().getConfigService().addListener(dataId, group, listener);
targetListenerMap.put(refreshTargetKey, listener);
if (annotation.initNotify() && org.springframework.util.StringUtils.hasText(configInfo)) {
try {
Expand Down Expand Up @@ -415,7 +513,7 @@ public String toString() {
};
}

nacosConfigManager.getConfigService()
getNacosConfigManager().getConfigService()
.addListener(dataId, group, listener);
targetListenerMap.put(refreshTargetKey, listener);

Expand Down Expand Up @@ -500,7 +598,7 @@ public String toString() {
};
}

nacosConfigManager.getConfigService()
getNacosConfigManager().getConfigService()
.addListener(dataId, group, listener);
targetListenerMap.put(refreshTargetKey, listener);
return true;
Expand Down Expand Up @@ -606,12 +704,58 @@ private void handleMethodAnnotation(final Object bean, String beanName, final Me
handleMethodNacosConfigListener(configAnnotation, beanName, bean, method);
return;
}
if (!applicationContext.containsBeanDefinition(beanName)) {
return;
}
BeanDefinition beanDefinition = ((GenericApplicationContext) applicationContext).getBeanDefinition(beanName);
if (beanDefinition instanceof AnnotatedBeanDefinition) {
MethodMetadata factoryMethodMetadata = (((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata());
if (factoryMethodMetadata != null) {
MergedAnnotations annotations = factoryMethodMetadata.getAnnotations();
if (annotations != null && annotations.isPresent(NacosConfig.class)) {
MergedAnnotation<NacosConfig> nacosConfigMergedAnnotation = annotations.get(NacosConfig.class);
Map<String, Object> stringObjectMap = nacosConfigMergedAnnotation.asMap();
String dataId = (String) stringObjectMap.get("dataId");
String group = (String) stringObjectMap.get("group");
String key = (String) stringObjectMap.get("key");
String defaultValue = (String) stringObjectMap.get("defaultValue");
handleBeanNacosConfigAnnotation(dataId, group, key, beanName, bean, defaultValue);
}
}
}

}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
nacosConfigManager = this.applicationContext.getBean(NacosConfigManager.class);
}

private NacosConfigManager getNacosConfigManager() {
if (nacosConfigManager == null) {
nacosConfigManager = this.applicationContext.getBean(NacosConfigManager.class);
}
return nacosConfigManager;
}

private static String[] getNullPropertyNames(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> nullPropertyNames = new HashSet<>();
for (PropertyDescriptor pd : pds) {
String propertyName = pd.getName();
try {
Object propertyValue = src.getPropertyValue(propertyName);
if (propertyValue == null) {
nullPropertyNames.add(propertyName);
}
}
catch (NotReadablePropertyException e) {
//ignore
nullPropertyNames.add(propertyName);
}
}
return nullPropertyNames.toArray(new String[0]);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
* @author shiyiyue1102
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface NacosConfig {

Expand Down

0 comments on commit effabf4

Please sign in to comment.