diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/AbstractConfigChangeListener.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/AbstractConfigChangeListener.java index 074bbd1ba..5e9ca577c 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/AbstractConfigChangeListener.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/AbstractConfigChangeListener.java @@ -23,10 +23,27 @@ import com.alibaba.nacos.api.config.listener.AbstractSharedListener; import com.alibaba.nacos.client.config.impl.ConfigChangeHandler; -public abstract class AbstractConfigChangeListener extends AbstractSharedListener { +public abstract class AbstractConfigChangeListener extends AbstractSharedListener implements TargetRefreshable { String lastContent; + Object target; + + @Override + public Object getTarget() { + return target; + } + + @Override + public void setTarget(Object target) { + this.target = target; + } + + public AbstractConfigChangeListener(Object target) { + this.target = target; + } + + @Override public void innerReceive(String dataId, String group, String configInfo) { diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosAnnotationProcessor.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosAnnotationProcessor.java index bdcd577e3..775c8ffa0 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosAnnotationProcessor.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosAnnotationProcessor.java @@ -27,6 +27,7 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -61,6 +62,7 @@ public int getOrder() { return 0; } + private Map targetListenerMap = new ConcurrentHashMap<>(); private Map> groupKeyCache = new ConcurrentHashMap<>(); private String getGroupKeyContent(String dataId, String group) throws Exception { @@ -141,20 +143,31 @@ private void handleMethodNacosConfigKeysChangeListener(NacosConfigKeysListener a } ReflectionUtils.makeAccessible(method); + String refreshTargetKey = beanName + "#" + method.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); // annotation on string. nacosConfigManager.getConfigService().addListener(dataId, group, - new NacosPropertiesKeyListener(wrapArrayToSet(annotation.interestedKeys()), + new NacosPropertiesKeyListener(bean, wrapArrayToSet(annotation.interestedKeys()), wrapArrayToSet(annotation.interestedKeyPrefixes())) { @Override public void configChanged(ConfigChangeEvent event) { - ReflectionUtils.invokeMethod(method, bean, event); + ReflectionUtils.invokeMethod(method, this.getTarget(), event); } @Override public String toString() { - return String.format("sca nacos config listener on bean method %s", beanName + "@" + bean.hashCode() + "#" + method.getName()); + return String.format("sca nacos config listener on bean method %s", beanName + "@" + this.getTarget() + .hashCode() + "#" + method.getName()); } }); } @@ -180,10 +193,20 @@ private void handleMethodNacosConfigKeyListener(NacosConfigKeyListener annotatio } ReflectionUtils.makeAccessible(method); + String refreshTargetKey = beanName + "#" + method.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); // annotation on string. nacosConfigManager.getConfigService() - .addListener(dataId, group, new NacosPropertiesKeyListener(wrapArrayToSet(key)) { + .addListener(dataId, group, new NacosPropertiesKeyListener(bean, wrapArrayToSet(key)) { @Override public void configChanged(ConfigChangeEvent event) { @@ -191,14 +214,14 @@ public void configChanged(ConfigChangeEvent event) { List collect = changeItems.stream().filter(a -> a.getKey().equals(key)) .collect(Collectors.toList()); if (!collect.isEmpty()) { - ReflectionUtils.invokeMethod(method, bean, collect.get(0)); + ReflectionUtils.invokeMethod(method, this.getTarget(), collect.get(0)); } } @Override public String toString() { - return String.format("sca nacos config listener on bean method %s", beanName + "@" + bean.hashCode() + "#" + method.getName()); + return String.format("sca nacos config listener on bean method %s", beanName + "@" + getTarget().hashCode() + "#" + method.getName()); } }); @@ -220,16 +243,33 @@ private void handleMethodNacosConfigChangeListener(NacosConfigListener annotatio } ReflectionUtils.makeAccessible(method); + String refreshTargetKey = beanName + "#" + method.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); + nacosConfigManager.getConfigService().addListener(dataId, group, new NacosConfigRefreshableListener(bean) { + + @Override + public Executor getExecutor() { + return null; + } - nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() { @Override public void receiveConfigInfo(String configInfo) { - ReflectionUtils.invokeMethod(method, bean, configInfo); + ReflectionUtils.invokeMethod(method, getTarget(), configInfo); } @Override public String toString() { - return String.format("sca nacos config listener on bean method %s", beanName + "@" + bean.hashCode() + "#" + method.getName()); + return String.format("sca nacos config listener on bean method %s", beanName + "@" + getTarget() + .hashCode() + "#" + method.getName()); } }); @@ -271,22 +311,35 @@ private void handleFiledNacosConfigAnnotationWithoutKey(String dataId, String gr // annotation on string. if (String.class.isAssignableFrom(field.getType())) { ReflectionUtils.setField(field, bean, config); - nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() { - @Override - public void receiveConfigInfo(String configInfo) { - try { - ReflectionUtils.setField(field, bean, configInfo); - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - @Override - public String toString() { - return String.format("sca nacos config listener on bean filed %s", beanName + "@" + bean.hashCode() + "#" + field.getName()); - } - }); + String refreshTargetKey = beanName + "#" + field.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); + nacosConfigManager.getConfigService() + .addListener(dataId, group, new NacosConfigRefreshableListener(bean) { + @Override + public void receiveConfigInfo(String configInfo) { + try { + ReflectionUtils.setField(field, getTarget(), configInfo); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public String toString() { + return String.format("sca nacos config listener on bean filed %s", beanName + "@" + getTarget().hashCode() + "#" + field.getName()); + } + }); return; } @@ -303,27 +356,39 @@ public String toString() { throw new RuntimeException(e); } ReflectionUtils.setField(field, bean, properties); - nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() { - @Override - public void receiveConfigInfo(String configInfo) { - try { - Properties properties = new Properties(); - if (StringUtils.isNotBlank(configInfo)) { - properties = PropertiesUtils.convertToProperties(configInfo); + String refreshTargetKey = beanName + "#" + field.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); + nacosConfigManager.getConfigService() + .addListener(dataId, group, new NacosConfigRefreshableListener(bean) { + @Override + public void receiveConfigInfo(String configInfo) { + try { + Properties properties = new Properties(); + if (StringUtils.isNotBlank(configInfo)) { + properties = PropertiesUtils.convertToProperties(configInfo); + } + ReflectionUtils.setField(field, getTarget(), properties); + } + catch (Throwable e) { + throw new RuntimeException(e); + } } - ReflectionUtils.setField(field, bean, properties); - } - catch (Throwable e) { - throw new RuntimeException(e); - } - } - @Override - public String toString() { - return String.format("sca nacos config properties listener on bean filed %s", beanName + "@" + bean.hashCode() + "#" + field.getName()); - } + @Override + public String toString() { + return String.format("sca nacos config properties listener on bean filed %s", beanName + "@" + getTarget().hashCode() + "#" + field.getName()); + } - }); + }); return; } @@ -338,7 +403,18 @@ public String toString() { //yaml and json to object Object o = ObjectUtils.convertToObject(config, field.getType()); ReflectionUtils.setField(field, bean, o); - nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() { + String refreshTargetKey = beanName + "#" + field.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); + nacosConfigManager.getConfigService().addListener(dataId, group, new NacosConfigRefreshableListener(bean) { @Override public void receiveConfigInfo(String configInfo) { try { @@ -346,7 +422,7 @@ public void receiveConfigInfo(String configInfo) { configInfo = "{}"; } Object o = ObjectUtils.convertToObject(configInfo, field.getType()); - ReflectionUtils.setField(field, bean, o); + ReflectionUtils.setField(field, getTarget(), o); } catch (Exception e) { throw new RuntimeException(e); @@ -355,7 +431,7 @@ public void receiveConfigInfo(String configInfo) { @Override public String toString() { - return String.format("sca nacos config object listener on bean filed %s", beanName + "@" + bean.hashCode() + "#" + field.getName()); + return String.format("sca nacos config object listener on bean filed %s", beanName + "@" + getTarget().hashCode() + "#" + field.getName()); } }); @@ -378,19 +454,30 @@ private void handleFiledNacosConfigAnnotationWithKey(String dataId, String group // annotation on string. if (String.class.isAssignableFrom(field.getType())) { ReflectionUtils.setField(field, bean, config); + String refreshTargetKey = beanName + "#" + field.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); nacosConfigManager.getConfigService() - .addListener(dataId, group, new NacosPropertiesKeyListener(wrapArrayToSet(key)) { + .addListener(dataId, group, new NacosPropertiesKeyListener(bean, wrapArrayToSet(key)) { @Override public void configChanged(ConfigChangeEvent event) { ConfigChangeItem changeItem = event.getChangeItem(key); String newConfig = changeItem == null ? null : changeItem.getNewValue(); - ReflectionUtils.setField(field, bean, newConfig); + ReflectionUtils.setField(field, getTarget(), newConfig); } @Override public String toString() { - return String.format("[spring cloud alibaba nacos config key listener , key %s , target %s ] ", key, beanName + "@" + bean.hashCode() + "#" + field.getName()); + return String.format("[spring cloud alibaba nacos config key listener , key %s , target %s ] ", key, beanName + "@" + getTarget().hashCode() + "#" + field.getName()); } }); @@ -411,8 +498,19 @@ public String toString() { throw new RuntimeException(e); } ReflectionUtils.setField(field, bean, properties); + String refreshTargetKey = beanName + "#" + field.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); nacosConfigManager.getConfigService() - .addListener(dataId, group, new NacosPropertiesKeyListener(wrapArrayToSet(key)) { + .addListener(dataId, group, new NacosPropertiesKeyListener(bean, wrapArrayToSet(key)) { @Override public void configChanged(ConfigChangeEvent event) { @@ -424,7 +522,7 @@ public void configChanged(ConfigChangeEvent event) { if (StringUtils.isNotBlank(newConfig)) { properties = PropertiesUtils.convertToProperties(newConfig); } - ReflectionUtils.setField(field, bean, properties); + ReflectionUtils.setField(field, getTarget(), properties); } catch (Throwable e) { throw new RuntimeException(e); @@ -433,7 +531,7 @@ public void configChanged(ConfigChangeEvent event) { @Override public String toString() { - return String.format("[spring cloud alibaba nacos config key listener for properties , key %s , target %s ] ", key, beanName + "@" + bean.hashCode() + "#" + field.getName()); + return String.format("[spring cloud alibaba nacos config key listener for properties , key %s , target %s ] ", key, beanName + "@" + getTarget().hashCode() + "#" + field.getName()); } }); @@ -442,8 +540,19 @@ public String toString() { if (field.getType().isPrimitive() && setPrimitiveFiled(field, bean, config)) { + String refreshTargetKey = beanName + "#" + field.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); nacosConfigManager.getConfigService() - .addListener(dataId, group, new NacosPropertiesKeyListener(wrapArrayToSet(key)) { + .addListener(dataId, group, new NacosPropertiesKeyListener(bean, wrapArrayToSet(key)) { @Override public void configChanged(ConfigChangeEvent event) { @@ -451,7 +560,7 @@ public void configChanged(ConfigChangeEvent event) { ConfigChangeItem changeItem = event.getChangeItem(key); String newConfig = changeItem == null ? null : changeItem.getNewValue(); if (StringUtils.isNotBlank(newConfig)) { - setPrimitiveFiled(field, bean, newConfig); + setPrimitiveFiled(field, getTarget(), newConfig); } } catch (Exception e) { @@ -461,7 +570,7 @@ public void configChanged(ConfigChangeEvent event) { @Override public String toString() { - return String.format("[spring cloud alibaba nacos config key listener , key %s , target %s ] ", key, beanName + "@" + bean.hashCode() + "#" + field.getName()); + return String.format("[spring cloud alibaba nacos config key listener , key %s , target %s ] ", key, beanName + "@" + getTarget().hashCode() + "#" + field.getName()); } }); return; @@ -474,8 +583,19 @@ public String toString() { //yaml and json to object Object o = ObjectUtils.convertToObject(config, field.getType()); ReflectionUtils.setField(field, bean, o); + String refreshTargetKey = beanName + "#" + field.getName(); + TargetRefreshable currentTarget = targetListenerMap.get(refreshTargetKey); + if (currentTarget != null) { + log.info("[Nacos Config] reset {} listener from {} to {} ", refreshTargetKey, + currentTarget.hashCode(), bean.hashCode()); + targetListenerMap.get(refreshTargetKey).setTarget(bean); + return; + } + + log.info("[Nacos Config] register {} listener on {} ", refreshTargetKey, + bean.hashCode()); nacosConfigManager.getConfigService() - .addListener(dataId, group, new NacosPropertiesKeyListener(wrapArrayToSet(key)) { + .addListener(dataId, group, new NacosPropertiesKeyListener(bean, wrapArrayToSet(key)) { @Override public void configChanged(ConfigChangeEvent event) { @@ -486,7 +606,7 @@ public void configChanged(ConfigChangeEvent event) { newConfig = "{}"; } Object o = ObjectUtils.convertToObject(newConfig, field.getType()); - ReflectionUtils.setField(field, bean, o); + ReflectionUtils.setField(field, getTarget(), o); } catch (Exception e) { throw new RuntimeException(e); @@ -495,7 +615,7 @@ public void configChanged(ConfigChangeEvent event) { @Override public String toString() { - return String.format("[spring cloud alibaba nacos config key listener , key %s , target %s ] ", key, beanName + "#" + field.getName()); + return String.format("[spring cloud alibaba nacos config key listener , key %s , target %s ] ", key, beanName + "@" + getTarget().hashCode() + "#" + field.getName()); } }); diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosConfigRefreshableListener.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosConfigRefreshableListener.java new file mode 100644 index 000000000..ff339e69b --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosConfigRefreshableListener.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.cloud.nacos.annotation; + +import com.alibaba.nacos.api.config.listener.AbstractListener; + +public abstract class NacosConfigRefreshableListener extends AbstractListener implements TargetRefreshable { + + Object target; + + NacosConfigRefreshableListener(Object target) { + this.target = target; + } + + public Object getTarget() { + return target; + } + + @Override + public void setTarget(Object target) { + this.target = target; + } + +} diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosPropertiesKeyListener.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosPropertiesKeyListener.java index 06d192a5e..2e27db7f4 100644 --- a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosPropertiesKeyListener.java +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/NacosPropertiesKeyListener.java @@ -28,14 +28,17 @@ public abstract class NacosPropertiesKeyListener extends AbstractConfigChangeLis Set interestedKeyPrefixes; - NacosPropertiesKeyListener() { + NacosPropertiesKeyListener(Object target) { + super(target); } - NacosPropertiesKeyListener(Set interestedKeys) { + NacosPropertiesKeyListener(Object target, Set interestedKeys) { + this(target); this.interestedKeys = interestedKeys; } - public NacosPropertiesKeyListener(Set interestedKeys, Set interestedKeyPrefixes) { + public NacosPropertiesKeyListener(Object target, Set interestedKeys, Set interestedKeyPrefixes) { + this(target); this.interestedKeys = interestedKeys; this.interestedKeyPrefixes = interestedKeyPrefixes; } diff --git a/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/TargetRefreshable.java b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/TargetRefreshable.java new file mode 100644 index 000000000..8f851b9f3 --- /dev/null +++ b/spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/annotation/TargetRefreshable.java @@ -0,0 +1,27 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.cloud.nacos.annotation; + +import com.alibaba.nacos.api.config.listener.Listener; + +public interface TargetRefreshable extends Listener { + + Object getTarget(); + + void setTarget(Object target); + +}