This is an automated email from the ASF dual-hosted git repository.

yiji pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/master by this push:
     new 2339729  [Dubbo-7367]fix too many instance bean created (#7438)
2339729 is described below

commit 23397290073381c0024706c935072dae457a5742
Author: 张远征hd <[email protected]>
AuthorDate: Fri Apr 2 16:07:18 2021 +0800

    [Dubbo-7367]fix too many instance bean created (#7438)
    
    1,修复相同parameters当产生多个Reference的问题。
    2,如果相同服务以不同参数订阅多次,则启动时增加WARN日志
    
    * Update ReferenceAnnotationBeanPostProcessor.java
    
    修改日志格式
    
    * Update ReferenceAnnotationBeanPostProcessor.java
    
    release referencedBeanNameIdx after used.
    
    * Update ReferenceAnnotationBeanPostProcessorTest.java
    
    add UT
    
    * Update ReferenceAnnotationBeanPostProcessor.java
    
    只处理String类型的array,对Method[]暂时不处理
    
    * 优化generateReferenceBeanName
    
    支持处理methods属性和arguments属性
    
    * methods和arguments需要排序
    
    * Update ReferenceAnnotationUtils.java
    
    use lambda
    
    * update ServiceInstancesChangedListener
    
    调整ServiceInstancesChangedListener的事件通知。所有的directory都能通知到
    
    * Update ServiceInstancesChangedListener.java
    
    listeners内部改为HashSet
    
    * update generateReferenceBeanName
    
    generateReferenceBeanName改用3.0-preview逻辑
    
    * remote println
    
    * Update ReferenceAnnotationBeanPostProcessor.java
    
    remote unused private method
    
    * Update ReferenceAnnotationBeanPostProcessor.java
    
    * Update ReferenceAnnotationBeanPostProcessorTest.java
    
    update UT
    
    * update UT
    
    * revert to use ReferenceAnnotationUtils
    
    * 使用来自kylixs的convertAttribute方法
    
    * organize imports & update UT
    
    * update UT
    
    * update ReferenceAnnotationBeanPostProcessor & UT
---
 .../ReferenceAnnotationBeanPostProcessor.java      | 73 +++++++++++++++++++++-
 .../ReferenceAnnotationBeanPostProcessorTest.java  | 32 +++++++++-
 .../registry/client/ServiceDiscoveryRegistry.java  |  2 +-
 .../listener/ServiceInstancesChangedListener.java  | 22 ++++---
 4 files changed, 116 insertions(+), 13 deletions(-)

diff --git 
a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
 
b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
index 07de2ee..9a9dfac 100644
--- 
a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
+++ 
b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessor.java
@@ -16,6 +16,8 @@
  */
 package org.apache.dubbo.config.spring.beans.factory.annotation;
 
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.apache.dubbo.config.annotation.Reference;
@@ -24,6 +26,7 @@ import org.apache.dubbo.config.spring.ReferenceBean;
 import org.apache.dubbo.config.spring.ServiceBean;
 
 import 
com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor;
+import com.alibaba.spring.util.AnnotationUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.InjectionMetadata;
 import 
org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -31,15 +34,25 @@ import 
org.springframework.beans.factory.config.RuntimeBeanReference;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
 import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.util.ObjectUtils;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
 
 import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
 import static com.alibaba.spring.util.AnnotationUtils.getAttributes;
@@ -56,7 +69,9 @@ import static org.springframework.util.StringUtils.hasText;
  * @since 2.5.7
  */
 public class ReferenceAnnotationBeanPostProcessor extends 
AbstractAnnotationBeanPostProcessor implements
-        ApplicationContextAware {
+        ApplicationContextAware, ApplicationListener {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(ReferenceAnnotationBeanPostProcessor.class);
 
     /**
      * The bean name of {@link ReferenceAnnotationBeanPostProcessor}
@@ -79,6 +94,8 @@ public class ReferenceAnnotationBeanPostProcessor extends 
AbstractAnnotationBean
 
     private ApplicationContext applicationContext;
 
+    private static Map<String, TreeSet<String>> referencedBeanNameIdx = new 
HashMap<>();
+
     /**
      * {@link com.alibaba.dubbo.config.annotation.Reference 
@com.alibaba.dubbo.config.annotation.Reference} has been supported since 2.7.3
      * <p>
@@ -131,6 +148,8 @@ public class ReferenceAnnotationBeanPostProcessor extends 
AbstractAnnotationBean
          */
         String referenceBeanName = getReferenceBeanName(attributes, 
injectedType);
 
+        referencedBeanNameIdx.computeIfAbsent(referencedBeanName, k -> new 
TreeSet<String>()).add(referenceBeanName);
+
         ReferenceBean referenceBean = 
buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
 
         boolean localServiceBean = isLocalServiceBean(referencedBeanName, 
referenceBean, attributes);
@@ -212,9 +231,16 @@ public class ReferenceAnnotationBeanPostProcessor extends 
AbstractAnnotationBean
         if (!attributes.isEmpty()) {
             beanNameBuilder.append('(');
             for (Map.Entry<String, Object> entry : attributes.entrySet()) {
+                String value;
+                if ("parameters".equals(entry.getKey())) {
+                    ArrayList<String> pairs = getParameterPairs(entry);
+                    value = 
convertAttribute(pairs.stream().sorted().toArray());
+                } else {
+                    value = convertAttribute(entry.getValue());
+                }
                 beanNameBuilder.append(entry.getKey())
                         .append('=')
-                        .append(entry.getValue())
+                        .append(value)
                         .append(',');
             }
             // replace the latest "," to be ")"
@@ -226,6 +252,38 @@ public class ReferenceAnnotationBeanPostProcessor extends 
AbstractAnnotationBean
         return beanNameBuilder.toString();
     }
 
+    private ArrayList<String> getParameterPairs(Map.Entry<String, Object> 
entry) {
+        String[] entryValues = (String[]) entry.getValue();
+        ArrayList<String> pairs = new ArrayList<>();
+        // parameters spec is {key1,value1,key2,value2}
+        for (int i = 0; i < entryValues.length / 2 * 2; i = i + 2) {
+            pairs.add(entryValues[i] + "=" + entryValues[i + 1]);
+        }
+        return pairs;
+    }
+
+    private String convertAttribute(Object obj) {
+        if (obj == null) {
+            return null;
+        }
+        if (obj instanceof Annotation) {
+            AnnotationAttributes attributes = 
AnnotationUtils.getAnnotationAttributes((Annotation) obj, true);
+            for (Map.Entry<String, Object> entry : attributes.entrySet()) {
+                entry.setValue(convertAttribute(entry.getValue()));
+            }
+            return String.valueOf(attributes);
+        } else if (obj.getClass().isArray()) {
+            Object[] array = ObjectUtils.toObjectArray(obj);
+            String[] newArray = new String[array.length];
+            for (int i = 0; i < array.length; i++) {
+                newArray[i] = convertAttribute(array[i]);
+            }
+            return Arrays.toString(Arrays.stream(newArray).sorted().toArray());
+        } else {
+            return String.valueOf(obj);
+        }
+    }
+
     /**
      * Is Local Service bean or not?
      *
@@ -344,4 +402,15 @@ public class ReferenceAnnotationBeanPostProcessor extends 
AbstractAnnotationBean
         this.injectedFieldReferenceBeanCache.clear();
         this.injectedMethodReferenceBeanCache.clear();
     }
+
+    @Override
+    public void onApplicationEvent(ApplicationEvent event) {
+        if (event instanceof ContextRefreshedEvent) {
+            referencedBeanNameIdx.entrySet().stream().filter(e -> 
e.getValue().size() > 1).forEach(e -> {
+                String logPrefix = e.getKey() + " has " + e.getValue().size() 
+ " reference instances, there are: ";
+                
logger.warn(e.getValue().stream().collect(Collectors.joining(", ", logPrefix, 
"")));
+            });
+            referencedBeanNameIdx.clear();
+        }
+    }
 }
diff --git 
a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
 
b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
index 6007e81..d262850 100644
--- 
a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
+++ 
b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceAnnotationBeanPostProcessorTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.dubbo.config.spring.beans.factory.annotation;
 
+import org.apache.dubbo.config.annotation.Argument;
 import org.apache.dubbo.config.annotation.Method;
 import org.apache.dubbo.config.annotation.Reference;
 import org.apache.dubbo.config.spring.ReferenceBean;
@@ -123,6 +124,31 @@ public class ReferenceAnnotationBeanPostProcessorTest {
     @Reference
     private HelloService helloService2;
 
+    // Instance 1
+    @Reference(check = false, parameters = {"a", "2", "b", "1"}, filter = 
{"echo"})
+    private HelloService helloServiceWithArray0;
+
+    // Instance 2
+    @Reference(check = false, parameters = {"a", "1", "b", "2"}, filter = 
{"echo"})
+    private HelloService helloServiceWithArray1;
+
+    @Reference(parameters = {"b", "2", "a", "1"}, filter = {"echo"}, check = 
false)
+    private HelloService helloServiceWithArray2;
+
+    // Instance 3
+    @Reference(check = false, parameters = {"a", "1"}, filter = {"echo"}, 
methods = {@Method(name = "sayHello", timeout = 100)})
+    private HelloService helloServiceWithMethod1;
+
+    @Reference(parameters = {"a", "1"}, filter = {"echo"}, check = false, 
methods = {@Method(name = "sayHello", timeout = 100)})
+    private HelloService helloServiceWithMethod2;
+
+    // Instance 4
+    @Reference(parameters = {"a", "1"}, filter = {"echo"}, methods = 
{@Method(name = "sayHello", arguments = {@Argument(callback = true, type = 
"String"), @Argument(callback = false, type = "int")}, timeout = 100)}, check = 
false)
+    private HelloService helloServiceWithArgument1;
+
+    @Reference(check = false, filter = {"echo"}, parameters = {"a", "1"}, 
methods = {@Method(name = "sayHello", timeout = 100, arguments = 
{@Argument(callback = false, type = "int"), @Argument(callback = true, type = 
"String")})})
+    private HelloService helloServiceWithArgument2;
+
     @Test
     public void test() throws Exception {
 
@@ -177,7 +203,7 @@ public class ReferenceAnnotationBeanPostProcessorTest {
 
         Collection<ReferenceBean<?>> referenceBeans = 
beanPostProcessor.getReferenceBeans();
 
-        Assertions.assertEquals(4, referenceBeans.size());
+        Assertions.assertEquals(8, referenceBeans.size());
 
         ReferenceBean<?> referenceBean = referenceBeans.iterator().next();
 
@@ -194,7 +220,7 @@ public class ReferenceAnnotationBeanPostProcessorTest {
         Map<InjectionMetadata.InjectedElement, ReferenceBean<?>> 
referenceBeanMap =
                 beanPostProcessor.getInjectedFieldReferenceBeanMap();
 
-        Assertions.assertEquals(3, referenceBeanMap.size());
+        Assertions.assertEquals(10, referenceBeanMap.size());
 
         for (Map.Entry<InjectionMetadata.InjectedElement, ReferenceBean<?>> 
entry : referenceBeanMap.entrySet()) {
 
@@ -311,7 +337,7 @@ public class ReferenceAnnotationBeanPostProcessorTest {
 
         Collection<ReferenceBean<?>> referenceBeans = 
beanPostProcessor.getReferenceBeans();
 
-        Assertions.assertEquals(4, referenceBeans.size());
+        Assertions.assertEquals(8, referenceBeans.size());
 
         ReferenceBean<?> referenceBean = referenceBeans.iterator().next();
 
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
index 7c59c21..ac0b526 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
@@ -327,7 +327,7 @@ public class ServiceDiscoveryRegistry implements Registry {
         serviceListener.addListener(protocolServiceKey, listener);
         registerServiceInstancesChangedListener(url, serviceListener);
 
-
+        // FIXME: This will cause redundant duplicate notifications
         serviceNames.forEach(serviceName -> {
             List<ServiceInstance> serviceInstances = 
serviceDiscovery.getInstances(serviceName);
             if (CollectionUtils.isNotEmpty(serviceInstances)) {
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
index 55af495..d40fd01 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
@@ -37,6 +37,7 @@ import 
org.apache.dubbo.registry.client.metadata.store.RemoteMetadataServiceImpl
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -61,8 +62,9 @@ public class ServiceInstancesChangedListener implements 
ConditionalEventListener
 
     private final Set<String> serviceNames;
     private final ServiceDiscovery serviceDiscovery;
+    private final String registryId;
     private URL url;
-    private Map<String, NotifyListener> listeners;
+    private Map<String, Set<NotifyListener>> listeners;
 
     private Map<String, List<ServiceInstance>> allInstances;
 
@@ -73,6 +75,7 @@ public class ServiceInstancesChangedListener implements 
ConditionalEventListener
     public ServiceInstancesChangedListener(Set<String> serviceNames, 
ServiceDiscovery serviceDiscovery) {
         this.serviceNames = serviceNames;
         this.serviceDiscovery = serviceDiscovery;
+        this.registryId = serviceDiscovery.getUrl().getParameter("id");
         this.listeners = new HashMap<>();
         this.allInstances = new HashMap<>();
         this.serviceUrls = new HashMap<>();
@@ -188,9 +191,10 @@ public class ServiceInstancesChangedListener implements 
ConditionalEventListener
     }
 
     private void notifyAddressChanged() {
-        listeners.forEach((key, notifyListener) -> {
-            //FIXME, group wildcard match
-            notifyListener.notify(toUrlsWithEmpty(serviceUrls.get(key)));
+        listeners.forEach((key, notifyListeners) -> {
+            notifyListeners.forEach(notifyListener -> {
+                notifyListener.notify(toUrlsWithEmpty(serviceUrls.get(key)));
+            });
         });
     }
 
@@ -202,7 +206,7 @@ public class ServiceInstancesChangedListener implements 
ConditionalEventListener
     }
 
     public void addListener(String serviceKey, NotifyListener listener) {
-        this.listeners.put(serviceKey, listener);
+        this.listeners.computeIfAbsent(serviceKey, k -> new 
HashSet<>()).add(listener);
     }
 
     public void removeListener(String serviceKey) {
@@ -241,6 +245,10 @@ public class ServiceInstancesChangedListener implements 
ConditionalEventListener
         return serviceNames.contains(event.getServiceName());
     }
 
+    public String getRegistryId() {
+        return registryId;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -250,11 +258,11 @@ public class ServiceInstancesChangedListener implements 
ConditionalEventListener
             return false;
         }
         ServiceInstancesChangedListener that = 
(ServiceInstancesChangedListener) o;
-        return Objects.equals(getServiceNames(), that.getServiceNames());
+        return Objects.equals(getServiceNames(), that.getServiceNames()) && 
Objects.equals(getRegistryId(), that.getRegistryId());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(getClass(), getServiceNames());
+        return Objects.hash(getClass(), getServiceNames(), getRegistryId());
     }
 }

Reply via email to