From ad1392d11748ddbc6905cebaf6d91b0fa23f4e24 Mon Sep 17 00:00:00 2001 From: shiyiyue1102 Date: Fri, 15 Nov 2024 10:05:47 +0800 Subject: [PATCH] enhance nacos config annotation ,support applying to Spring Bean. (#3888) --- .../annotation/NacosAnnotationProcessor.java | 123 ++++++++++++++++++ .../annotation/NacosConfig.java | 2 +- 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com.alibaba.cloud.nacos/annotation/NacosAnnotationProcessor.java b/spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com.alibaba.cloud.nacos/annotation/NacosAnnotationProcessor.java index 887a58cbd..ffaaf5f8c 100644 --- a/spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com.alibaba.cloud.nacos/annotation/NacosAnnotationProcessor.java +++ b/spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com.alibaba.cloud.nacos/annotation/NacosAnnotationProcessor.java @@ -37,12 +37,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; +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 { @@ -101,6 +108,12 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro 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); } @@ -129,6 +142,93 @@ 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); + } + + 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()); + } + } + 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); + } + }; + } + + nacosConfigManager.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(); @@ -607,6 +707,29 @@ private void handleMethodAnnotation(final Object bean, String beanName, final Me 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 nacosConfigMergedAnnotation = annotations.get(NacosConfig.class); + Map 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 diff --git a/spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com.alibaba.cloud.nacos/annotation/NacosConfig.java b/spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com.alibaba.cloud.nacos/annotation/NacosConfig.java index 1799803b4..37fa1face 100644 --- a/spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com.alibaba.cloud.nacos/annotation/NacosConfig.java +++ b/spring-cloud-alibaba-starters/spring-alibaba-nacos-config/src/main/java/com.alibaba.cloud.nacos/annotation/NacosConfig.java @@ -28,7 +28,7 @@ * @author shiyiyue1102 */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD}) +@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) @Documented public @interface NacosConfig {