diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilder.java
new file mode 100644
index 00000000000..8a406f529ee
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilder.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.config.spring.beans.factory.annotation;
+
+import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY;
+import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL;
+import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY;
+import static org.apache.dubbo.config.spring.util.AnnotationUtils.resolveInterfaceName;
+import static org.springframework.util.StringUtils.arrayToCommaDelimitedString;
+import static org.springframework.util.StringUtils.hasText;
+
+import org.apache.dubbo.common.Constants;
+import org.apache.dubbo.config.annotation.Reference;
+import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.registry.Registry;
+
+import org.springframework.core.env.Environment;
+
+/**
+ * The Bean Name Builder for the annotations {@link Service} and {@link Reference}
+ *
+ * The naming rule is consistent with the the implementation {@link Registry} that is based on the service-name aware
+ * infrastructure, e.g Spring Cloud, Cloud Native and so on.
+ *
+ * The pattern of bean name : ${category}:${protocol}:${serviceInterface}:${version}:${group}.
+ *
+ * ${version} and ${group} are optional.
+ *
+ * @since 2.6.6
+ */
+class AnnotationBeanNameBuilder {
+
+ private static final String SEPARATOR = ":";
+
+ // Required properties
+
+ private final String category;
+
+ private final String protocol;
+
+ private final String interfaceClassName;
+
+ // Optional properties
+
+ private String version;
+
+ private String group;
+
+ private Environment environment;
+
+ private AnnotationBeanNameBuilder(String category, String protocol, String interfaceClassName) {
+ this.category = category;
+ this.protocol = protocol;
+ this.interfaceClassName = interfaceClassName;
+ }
+
+ private AnnotationBeanNameBuilder(Service service, Class> interfaceClass) {
+ this(PROVIDERS_CATEGORY, resolveProtocol(service.protocol()), resolveInterfaceName(service, interfaceClass));
+ this.group(service.group());
+ this.version(service.version());
+ }
+
+ private AnnotationBeanNameBuilder(Reference reference, Class> interfaceClass) {
+ this(CONSUMERS_CATEGORY, resolveProtocol(reference.protocol()), resolveInterfaceName(reference, interfaceClass));
+ this.group(reference.group());
+ this.version(reference.version());
+ }
+
+ public static AnnotationBeanNameBuilder create(Service service, Class> interfaceClass) {
+ return new AnnotationBeanNameBuilder(service, interfaceClass);
+ }
+
+ public static AnnotationBeanNameBuilder create(Reference reference, Class> interfaceClass) {
+ return new AnnotationBeanNameBuilder(reference, interfaceClass);
+ }
+
+ private static void append(StringBuilder builder, String value) {
+ if (hasText(value)) {
+ builder.append(SEPARATOR).append(value);
+ }
+ }
+
+ public AnnotationBeanNameBuilder group(String group) {
+ this.group = group;
+ return this;
+ }
+
+ public AnnotationBeanNameBuilder version(String version) {
+ this.version = version;
+ return this;
+ }
+
+ public AnnotationBeanNameBuilder environment(Environment environment) {
+ this.environment = environment;
+ return this;
+ }
+
+ /**
+ * Resolve the protocol
+ *
+ * @param protocols one or more protocols
+ * @return if protocols
== null
, it will return
+ * {@link Constants#DEFAULT_PROTOCOL "dubbo"} as the default protocol
+ * @see Constants#DEFAULT_PROTOCOL
+ */
+ private static String resolveProtocol(String... protocols) {
+ String protocol = arrayToCommaDelimitedString(protocols);
+ return hasText(protocol) ? protocol : DEFAULT_PROTOCOL;
+ }
+
+ /**
+ * Build bean name while resolve the placeholders if possible.
+ *
+ * @return pattern : ${category}:${protocol}:${serviceInterface}:${version}:${group}
+ */
+ public String build() {
+ // Append the required properties
+ StringBuilder beanNameBuilder = new StringBuilder(category);
+ append(beanNameBuilder, protocol);
+ append(beanNameBuilder, interfaceClassName);
+ // Append the optional properties
+ append(beanNameBuilder, version);
+ append(beanNameBuilder, group);
+ String beanName = beanNameBuilder.toString();
+ // Resolve placeholders
+ return environment != null ? environment.resolvePlaceholders(beanName) : beanName;
+ }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigBindingBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigBindingBeanPostProcessor.java
index 96c4d3885b0..9bfa2606e2d 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigBindingBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigBindingBeanPostProcessor.java
@@ -20,6 +20,7 @@
import org.apache.dubbo.config.AbstractConfig;
import org.apache.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar;
import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfigBinding;
+import org.apache.dubbo.config.spring.context.config.DubboConfigBeanCustomizer;
import org.apache.dubbo.config.spring.context.properties.DefaultDubboConfigBinder;
import org.apache.dubbo.config.spring.context.properties.DubboConfigBinder;
@@ -30,8 +31,16 @@
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.Environment;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors;
+
/**
* Dubbo Config Binding {@link BeanPostProcessor}
*
@@ -62,6 +71,8 @@ public class DubboConfigBindingBeanPostProcessor implements BeanPostProcessor, A
private boolean ignoreInvalidFields = true;
+ private List configBeanCustomizers = Collections.emptyList();
+
/**
* @param prefix the prefix of Configuration Properties
* @param beanName the binding Bean Name
@@ -80,18 +91,34 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro
AbstractConfig dubboConfig = (AbstractConfig) bean;
- dubboConfigBinder.bind(prefix, dubboConfig);
+ bind(prefix, dubboConfig);
+
+ customize(beanName, dubboConfig);
- if (log.isInfoEnabled()) {
- log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " +
- "configuration properties : " + prefix);
- }
}
return bean;
}
+ private void bind(String prefix, AbstractConfig dubboConfig) {
+
+ dubboConfigBinder.bind(prefix, dubboConfig);
+
+ if (log.isInfoEnabled()) {
+ log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " +
+ "configuration properties : " + prefix);
+ }
+ }
+
+ private void customize(String beanName, AbstractConfig dubboConfig) {
+
+ for (DubboConfigBeanCustomizer customizer : configBeanCustomizers) {
+ customizer.customize(beanName, dubboConfig);
+ }
+
+ }
+
public boolean isIgnoreUnknownFields() {
return ignoreUnknownFields;
}
@@ -129,6 +156,14 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
@Override
public void afterPropertiesSet() throws Exception {
+ initDubboConfigBinder();
+
+ initConfigBeanCustomizers();
+
+ }
+
+ private void initDubboConfigBinder() {
+
if (dubboConfigBinder == null) {
try {
dubboConfigBinder = applicationContext.getBean(DubboConfigBinder.class);
@@ -146,6 +181,16 @@ public void afterPropertiesSet() throws Exception {
}
+ private void initConfigBeanCustomizers() {
+
+ Collection configBeanCustomizers =
+ beansOfTypeIncludingAncestors(applicationContext, DubboConfigBeanCustomizer.class).values();
+
+ this.configBeanCustomizers = new ArrayList(configBeanCustomizers);
+
+ AnnotationAwareOrderComparator.sort(this.configBeanCustomizers);
+ }
+
/**
* Create {@link DubboConfigBinder} instance.
*
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 82fb9b25eb0..35f1e1d3c4a 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,17 @@
*/
package org.apache.dubbo.config.spring.beans.factory.annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.dubbo.config.spring.ServiceBean;
@@ -30,16 +41,6 @@
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that Consumer service {@link Reference} annotated fields
@@ -155,7 +156,18 @@ private ReferenceBeanInvocationHandler(ReferenceBean referenceBean) {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- return method.invoke(bean, args);
+ Object result = null;
+ try {
+ if (bean == null) { // If the bean is not initialized, invoke init()
+ // issue: https://github.com/apache/incubator-dubbo/issues/3429
+ init();
+ }
+ result = method.invoke(bean, args);
+ } catch (InvocationTargetException e) {
+ // re-throws the actual Exception.
+ throw e.getTargetException();
+ }
+ return result;
}
private void init() {
@@ -176,7 +188,9 @@ protected String buildInjectedObjectCacheKey(Reference reference, Object bean, S
private String buildReferencedBeanName(Reference reference, Class> injectedType) {
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, injectedType, getEnvironment());
+ AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(reference, injectedType);
+
+ builder.environment(getEnvironment());
return getEnvironment().resolvePlaceholders(builder.build());
}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java
index 22fa70422d5..b67bdfc2b51 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceAnnotationBeanPostProcessor.java
@@ -16,6 +16,18 @@
*/
package org.apache.dubbo.config.spring.beans.factory.annotation;
+import static org.apache.dubbo.config.spring.util.ObjectUtils.of;
+import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
+import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR;
+import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;
+import static org.springframework.util.ClassUtils.resolveClassName;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.config.MethodConfig;
@@ -52,18 +64,6 @@
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-import static org.apache.dubbo.config.spring.util.ObjectUtils.of;
-import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
-import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR;
-import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;
-import static org.springframework.util.ClassUtils.resolveClassName;
-
/**
* {@link Service} Annotation
* {@link BeanDefinitionRegistryPostProcessor Bean Definition Registry Post Processor}
@@ -290,8 +290,9 @@ private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, Bean
*/
private String generateServiceBeanName(Service service, Class> interfaceClass, String annotatedServiceBeanName) {
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, interfaceClass, environment);
+ AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(service, interfaceClass);
+ builder.environment(environment);
return builder.build();
@@ -316,8 +317,9 @@ private Class> resolveServiceInterfaceClass(Class> annotatedServiceBeanClass
}
if (interfaceClass == null) {
-
- Class>[] allInterfaces = annotatedServiceBeanClass.getInterfaces();
+ // Find all interfaces from the annotated class
+ // To resolve an issue : https://github.com/apache/incubator-dubbo/issues/3251
+ Class>[] allInterfaces = ClassUtils.getAllInterfacesForClass(annotatedServiceBeanClass);
if (allInterfaces.length > 0) {
interfaceClass = allInterfaces[0];
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java
deleted file mode 100644
index 6cd712408be..00000000000
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilder.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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
- *
- * http://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 org.apache.dubbo.config.spring.beans.factory.annotation;
-
-import org.apache.dubbo.config.annotation.Reference;
-import org.apache.dubbo.config.annotation.Service;
-import org.apache.dubbo.config.spring.ReferenceBean;
-import org.apache.dubbo.config.spring.ServiceBean;
-
-import org.springframework.core.env.Environment;
-import org.springframework.util.StringUtils;
-
-import static org.apache.dubbo.config.spring.util.AnnotationUtils.resolveInterfaceName;
-
-
-/**
- * Dubbo {@link Service @Service} Bean Builder
- *
- * @see Service
- * @see Reference
- * @see ServiceBean
- * @see ReferenceBean
- * @since 2.6.5
- */
-class ServiceBeanNameBuilder {
-
- private static final String SEPARATOR = ":";
-
- private final String interfaceClassName;
-
- private final Environment environment;
-
- // Optional
- private String version;
-
- private String group;
-
- private ServiceBeanNameBuilder(String interfaceClassName, Environment environment) {
- this.interfaceClassName = interfaceClassName;
- this.environment = environment;
- }
-
- private ServiceBeanNameBuilder(Class> interfaceClass, Environment environment) {
- this(interfaceClass.getName(), environment);
- }
-
- private ServiceBeanNameBuilder(Service service, Class> interfaceClass, Environment environment) {
- this(resolveInterfaceName(service, interfaceClass), environment);
- this.group(service.group());
- this.version(service.version());
- }
-
- private ServiceBeanNameBuilder(Reference reference, Class> interfaceClass, Environment environment) {
- this(resolveInterfaceName(reference, interfaceClass), environment);
- this.group(reference.group());
- this.version(reference.version());
- }
-
- public static ServiceBeanNameBuilder create(Class> interfaceClass, Environment environment) {
- return new ServiceBeanNameBuilder(interfaceClass, environment);
- }
-
- public static ServiceBeanNameBuilder create(Service service, Class> interfaceClass, Environment environment) {
- return new ServiceBeanNameBuilder(service, interfaceClass, environment);
- }
-
- public static ServiceBeanNameBuilder create(Reference reference, Class> interfaceClass, Environment environment) {
- return new ServiceBeanNameBuilder(reference, interfaceClass, environment);
- }
-
- private static void append(StringBuilder builder, String value) {
- if (StringUtils.hasText(value)) {
- builder.append(SEPARATOR).append(value);
- }
- }
-
- public ServiceBeanNameBuilder group(String group) {
- this.group = group;
- return this;
- }
-
- public ServiceBeanNameBuilder version(String version) {
- this.version = version;
- return this;
- }
-
- public String build() {
- StringBuilder beanNameBuilder = new StringBuilder("ServiceBean");
- // Required
- append(beanNameBuilder, interfaceClassName);
- // Optional
- append(beanNameBuilder, version);
- append(beanNameBuilder, group);
- // Build
- String rawBeanName = beanNameBuilder.toString();
- // Resolve placeholders
- return environment.resolvePlaceholders(rawBeanName);
- }
-}
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigBindingRegistrar.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigBindingRegistrar.java
index e797e5585ec..746db48de69 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigBindingRegistrar.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigBindingRegistrar.java
@@ -18,6 +18,7 @@
import org.apache.dubbo.config.AbstractConfig;
import org.apache.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor;
+import org.apache.dubbo.config.spring.context.config.NamePropertyDefaultValueDubboConfigBeanCustomizer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -41,6 +42,8 @@
import java.util.Map;
import java.util.Set;
+import static org.apache.dubbo.config.spring.context.config.NamePropertyDefaultValueDubboConfigBeanCustomizer.BEAN_NAME;
+import static org.apache.dubbo.config.spring.util.BeanRegistrar.registerInfrastructureBean;
import static org.apache.dubbo.config.spring.util.PropertySourcesUtils.getSubProperties;
import static org.apache.dubbo.config.spring.util.PropertySourcesUtils.normalizePrefix;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
@@ -107,6 +110,8 @@ private void registerDubboConfigBeans(String prefix,
}
+ registerDubboConfigBeanCustomizers(registry);
+
}
private void registerDubboConfigBean(String beanName, Class extends AbstractConfig> configClass,
@@ -149,6 +154,10 @@ private void registerDubboConfigBindingBeanPostProcessor(String prefix, String b
}
+ private void registerDubboConfigBeanCustomizers(BeanDefinitionRegistry registry) {
+ registerInfrastructureBean(registry, BEAN_NAME, NamePropertyDefaultValueDubboConfigBeanCustomizer.class);
+ }
+
@Override
public void setEnvironment(Environment environment) {
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java
index cc5f4f8c722..d01dd398ee0 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/DubboConfigConfigurationRegistrar.java
@@ -44,10 +44,11 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B
boolean multiple = attributes.getBoolean("multiple");
- if (multiple) {
+ // Single Config Bindings
+ registerBeans(registry, DubboConfigConfiguration.Single.class);
+
+ if (multiple) { // Since 2.6.6 https://github.com/apache/incubator-dubbo/issues/3193
registerBeans(registry, DubboConfigConfiguration.Multiple.class);
- } else {
- registerBeans(registry, DubboConfigConfiguration.Single.class);
}
}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubbo.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubbo.java
index 4535f188e4c..706e288597e 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubbo.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubbo.java
@@ -76,6 +76,6 @@
* @see EnableDubboConfig#multiple()
*/
@AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
- boolean multipleConfig() default false;
+ boolean multipleConfig() default true;
}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java
index 47bafa53b54..5a9fe0d595d 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfig.java
@@ -59,7 +59,7 @@
*
* @see EnableDubboConfigBinding
* @see DubboConfigConfiguration
- * @see DubboConfigConfigurationSelector
+ * @see DubboConfigConfigurationRegistrar
* @since 2.5.8
*/
@Target({ElementType.TYPE})
@@ -72,9 +72,9 @@
/**
* It indicates whether binding to multiple Spring Beans.
*
- * @return the default value is false
+ * @return the default value is true
since 2.6.6, the value is inverse earlier.
* @revised 2.5.9
*/
- boolean multiple() default false;
+ boolean multiple() default true;
}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/DubboConfigBeanCustomizer.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/DubboConfigBeanCustomizer.java
new file mode 100644
index 00000000000..75bf0dd2eb6
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/DubboConfigBeanCustomizer.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.config.spring.context.config;
+
+import org.apache.dubbo.config.AbstractConfig;
+import org.apache.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor;
+import org.apache.dubbo.config.spring.context.properties.DubboConfigBinder;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.Ordered;
+
+/**
+ * The Bean customizer for {@link AbstractConfig Dubbo Config}. Generally, The subclass will be registered as a Spring
+ * Bean that is used to {@link #customize(String, AbstractConfig) customize} {@link AbstractConfig Dubbo Config} bean
+ * after {@link DubboConfigBinder#bind(String, AbstractConfig) its binding}.
+ *
+ * If There are multiple {@link DubboConfigBeanCustomizer} beans in the Spring {@link ApplicationContext context}, they
+ * are executed orderly, thus the subclass should be aware to implement the {@link #getOrder()} method.
+ *
+ * @see DubboConfigBinder#bind(String, AbstractConfig)
+ * @see DubboConfigBindingBeanPostProcessor
+ * @since 2.6.6
+ */
+public interface DubboConfigBeanCustomizer extends Ordered {
+
+ /**
+ * Customize {@link AbstractConfig Dubbo Config Bean}
+ *
+ * @param beanName the name of {@link AbstractConfig Dubbo Config Bean}
+ * @param dubboConfigBean the instance of {@link AbstractConfig Dubbo Config Bean}
+ */
+ void customize(String beanName, AbstractConfig dubboConfigBean);
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/NamePropertyDefaultValueDubboConfigBeanCustomizer.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/NamePropertyDefaultValueDubboConfigBeanCustomizer.java
new file mode 100644
index 00000000000..fcd053d503c
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/context/config/NamePropertyDefaultValueDubboConfigBeanCustomizer.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.config.spring.context.config;
+
+import org.apache.dubbo.config.AbstractConfig;
+
+import org.springframework.util.ReflectionUtils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import static org.apache.dubbo.config.spring.util.ObjectUtils.of;
+import static org.springframework.beans.BeanUtils.getPropertyDescriptor;
+
+/**
+ * {@link DubboConfigBeanCustomizer} for the default value for the "name" property that will be taken bean name
+ * if absent.
+ *
+ * @since 2.6.6
+ */
+public class NamePropertyDefaultValueDubboConfigBeanCustomizer implements DubboConfigBeanCustomizer {
+
+ /**
+ * The bean name of {@link NamePropertyDefaultValueDubboConfigBeanCustomizer}
+ *
+ * @since 2.7.1
+ */
+ public static final String BEAN_NAME = "namePropertyDefaultValueDubboConfigBeanCustomizer";
+
+ /**
+ * The name of property that is "name" maybe is absent in target class
+ */
+ private static final String PROPERTY_NAME = "name";
+
+ @Override
+ public void customize(String beanName, AbstractConfig dubboConfigBean) {
+
+ PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dubboConfigBean.getClass(), PROPERTY_NAME);
+
+ if (propertyDescriptor != null) { // "name" property is present
+
+ Method getNameMethod = propertyDescriptor.getReadMethod();
+
+ if (getNameMethod == null) { // if "getName" method is absent
+ return;
+ }
+
+ Object propertyValue = ReflectionUtils.invokeMethod(getNameMethod, dubboConfigBean);
+
+ if (propertyValue != null) { // If The return value of "getName" method is not null
+ return;
+ }
+
+ Method setNameMethod = propertyDescriptor.getWriteMethod();
+ if (setNameMethod != null && getNameMethod != null) { // "setName" and "getName" methods are present
+ if (Arrays.equals(of(String.class), setNameMethod.getParameterTypes())) { // the param type is String
+ // set bean name to the value of the "name" property
+ ReflectionUtils.invokeMethod(setNameMethod, dubboConfigBean, beanName);
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getOrder() {
+ return HIGHEST_PRECEDENCE;
+ }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java
index 9a99ec138fd..63517c6e52a 100644
--- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java
+++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/status/SpringStatusChecker.java
@@ -1,94 +1,101 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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
- *
- * http://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 org.apache.dubbo.config.spring.status;
-
-import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.status.Status;
-import org.apache.dubbo.common.status.StatusChecker;
-import org.apache.dubbo.config.spring.extension.SpringExtensionFactory;
-
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.Lifecycle;
-
-import java.lang.reflect.Method;
-
-/**
- * SpringStatusChecker
- */
-@Activate
-public class SpringStatusChecker implements StatusChecker {
-
- private static final Logger logger = LoggerFactory.getLogger(SpringStatusChecker.class);
-
- @Override
- public Status check() {
- ApplicationContext context = null;
- for (ApplicationContext c : SpringExtensionFactory.getContexts()) {
- if (c != null) {
- context = c;
- break;
- }
- }
-
- if (context == null) {
- return new Status(Status.Level.UNKNOWN);
- }
-
- Status.Level level = Status.Level.OK;
- if (context instanceof Lifecycle) {
- if (((Lifecycle) context).isRunning()) {
- level = Status.Level.OK;
- } else {
- level = Status.Level.ERROR;
- }
- } else {
- level = Status.Level.UNKNOWN;
- }
- StringBuilder buf = new StringBuilder();
- try {
- Class> cls = context.getClass();
- Method method = null;
- while (cls != null && method == null) {
- try {
- method = cls.getDeclaredMethod("getConfigLocations", new Class>[0]);
- } catch (NoSuchMethodException t) {
- cls = cls.getSuperclass();
- }
- }
- if (method != null) {
- if (!method.isAccessible()) {
- method.setAccessible(true);
- }
- String[] configs = (String[]) method.invoke(context, new Object[0]);
- if (configs != null && configs.length > 0) {
- for (String config : configs) {
- if (buf.length() > 0) {
- buf.append(",");
- }
- buf.append(config);
- }
- }
- }
- } catch (Throwable t) {
- logger.warn(t.getMessage(), t);
- }
- return new Status(level, buf.toString());
- }
-
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.config.spring.status;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.status.Status;
+import org.apache.dubbo.common.status.StatusChecker;
+import org.apache.dubbo.config.spring.extension.SpringExtensionFactory;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.Lifecycle;
+import org.springframework.web.context.support.GenericWebApplicationContext;
+
+import java.lang.reflect.Method;
+
+/**
+ * SpringStatusChecker
+ */
+@Activate
+public class SpringStatusChecker implements StatusChecker {
+
+ private static final Logger logger = LoggerFactory.getLogger(SpringStatusChecker.class);
+
+ @Override
+ public Status check() {
+ ApplicationContext context = null;
+ for (ApplicationContext c : SpringExtensionFactory.getContexts()) {
+ // [Issue] SpringStatusChecker execute errors on non-XML Spring configuration
+ // issue : https://github.com/apache/incubator-dubbo/issues/3615
+ if(c instanceof GenericWebApplicationContext) { // ignore GenericXmlApplicationContext
+ continue;
+ }
+
+ if (c != null) {
+ context = c;
+ break;
+ }
+ }
+
+ if (context == null) {
+ return new Status(Status.Level.UNKNOWN);
+ }
+
+ Status.Level level = Status.Level.OK;
+ if (context instanceof Lifecycle) {
+ if (((Lifecycle) context).isRunning()) {
+ level = Status.Level.OK;
+ } else {
+ level = Status.Level.ERROR;
+ }
+ } else {
+ level = Status.Level.UNKNOWN;
+ }
+ StringBuilder buf = new StringBuilder();
+ try {
+ Class> cls = context.getClass();
+ Method method = null;
+ while (cls != null && method == null) {
+ try {
+ method = cls.getDeclaredMethod("getConfigLocations", new Class>[0]);
+ } catch (NoSuchMethodException t) {
+ cls = cls.getSuperclass();
+ }
+ }
+ if (method != null) {
+ if (!method.isAccessible()) {
+ method.setAccessible(true);
+ }
+ String[] configs = (String[]) method.invoke(context, new Object[0]);
+ if (configs != null && configs.length > 0) {
+ for (String config : configs) {
+ if (buf.length() > 0) {
+ buf.append(",");
+ }
+ buf.append(config);
+ }
+ }
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ return new Status(level, buf.toString());
+ }
+
+}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilderTest.java
similarity index 51%
rename from dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java
rename to dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilderTest.java
index b3616cd58a3..1dcd11391e1 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ServiceBeanNameBuilderTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationBeanNameBuilderTest.java
@@ -16,29 +16,31 @@
*/
package org.apache.dubbo.config.spring.beans.factory.annotation;
+import static org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationBeanNameBuilderTest.GROUP;
+import static org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationBeanNameBuilderTest.VERSION;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.config.spring.api.DemoService;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.util.ReflectionUtils;
-
/**
- * {@link ServiceBeanNameBuilder} Test
+ * {@link AnnotationBeanNameBuilder} Test
*
- * @see ServiceBeanNameBuilder
- * @since 2.6.5
+ * @see AnnotationBeanNameBuilder
+ * @since 2.6.6
*/
-@Service(interfaceClass = DemoService.class, group = ServiceBeanNameBuilderTest.GROUP, version = ServiceBeanNameBuilderTest.VERSION,
+@Service(interfaceClass = DemoService.class, group = GROUP, version = VERSION,
application = "application", module = "module", registry = {"1", "2", "3"})
-public class ServiceBeanNameBuilderTest {
+public class AnnotationBeanNameBuilderTest {
- @Reference(interfaceClass = DemoService.class, group = "DUBBO", version = "1.0.0",
+ @Reference(interfaceClass = DemoService.class, group = "DUBBO", version = "${dubbo.version}",
application = "application", module = "module", registry = {"1", "2", "3"})
static final Class> INTERFACE_CLASS = DemoService.class;
@@ -46,30 +48,37 @@ public class ServiceBeanNameBuilderTest {
static final String VERSION = "1.0.0";
- static final String BEAN_NAME = "ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO";
-
- private MockEnvironment environment = new MockEnvironment();
+ private MockEnvironment environment;
- @Test
- public void testRequiredAttributes() {
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(INTERFACE_CLASS, environment);
- Assertions.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService", builder.build());
+ @Before
+ public void prepare() {
+ environment = new MockEnvironment();
+ environment.setProperty("dubbo.version", "1.0.0");
}
@Test
public void testServiceAnnotation() {
- Service service = AnnotationUtils.getAnnotation(ServiceBeanNameBuilderTest.class, Service.class);
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, INTERFACE_CLASS, environment);
- Assertions.assertEquals(BEAN_NAME,
+ Service service = AnnotationUtils.getAnnotation(AnnotationBeanNameBuilderTest.class, Service.class);
+ AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(service, INTERFACE_CLASS);
+ Assert.assertEquals("providers:dubbo:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
+ builder.build());
+
+ builder.environment(environment);
+ Assert.assertEquals("providers:dubbo:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
builder.build());
}
@Test
public void testReferenceAnnotation() {
- Reference reference = AnnotationUtils.getAnnotation(ReflectionUtils.findField(ServiceBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class);
- ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, INTERFACE_CLASS, environment);
- Assertions.assertEquals(BEAN_NAME,
+ Reference reference = AnnotationUtils.getAnnotation(ReflectionUtils.findField(AnnotationBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class);
+ AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(reference, INTERFACE_CLASS);
+ Assert.assertEquals("consumers:dubbo:org.apache.dubbo.config.spring.api.DemoService:${dubbo.version}:DUBBO",
+ builder.build());
+
+ builder.environment(environment);
+ Assert.assertEquals("consumers:dubbo:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
builder.build());
}
}
+
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java
index 0baa7bbe450..15d9fb9e550 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/AnnotationPropertyValuesAdapterTest.java
@@ -17,6 +17,12 @@
package org.apache.dubbo.config.spring.beans.factory.annotation;
+import static org.springframework.util.StringUtils.arrayToCommaDelimitedString;
+
+import java.lang.reflect.Field;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.spring.ReferenceBean;
@@ -31,12 +37,6 @@
import org.springframework.util.ReflectionUtils;
import org.springframework.validation.DataBinder;
-import java.lang.reflect.Field;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import static org.springframework.util.StringUtils.arrayToCommaDelimitedString;
-
/**
* {@link AnnotationPropertyValuesAdapter} Test
*
@@ -96,7 +96,7 @@ public Map convert(String[] source) {
Assert.assertEquals("dubbo://localhost:12345", referenceBean.getUrl());
Assert.assertEquals("client", referenceBean.getClient());
Assert.assertEquals(true, referenceBean.isGeneric());
- Assert.assertEquals(true, referenceBean.isInjvm());
+ Assert.assertNull(referenceBean.isInjvm());
Assert.assertEquals(false, referenceBean.isCheck());
Assert.assertEquals(true, referenceBean.isInit());
Assert.assertEquals(true, referenceBean.getLazy());
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigBindingBeanPostProcessorTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigBindingBeanPostProcessorTest.java
index 7811c7371f1..e679f4f976c 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigBindingBeanPostProcessorTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/DubboConfigBindingBeanPostProcessorTest.java
@@ -17,50 +17,56 @@
package org.apache.dubbo.config.spring.beans.factory.annotation;
import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.spring.context.config.NamePropertyDefaultValueDubboConfigBeanCustomizer;
import org.apache.dubbo.config.spring.context.properties.DefaultDubboConfigBinder;
-import org.apache.dubbo.config.spring.context.properties.DubboConfigBinder;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.springframework.context.annotation.AnnotationConfigApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.PropertySource;
-import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
/**
* {@link DubboConfigBindingBeanPostProcessor}
*/
-@PropertySource({"classpath:/META-INF/config.properties"})
-@Configuration
+@RunWith(SpringRunner.class)
+@ContextConfiguration(classes = {
+ DefaultDubboConfigBinder.class,
+ NamePropertyDefaultValueDubboConfigBeanCustomizer.class,
+ DubboConfigBindingBeanPostProcessorTest.class
+})
+@TestPropertySource(properties = {
+ "dubbo.application.id = dubbo-demo-application",
+ "dubbo.application.owner = mercyblitz",
+ "dubbo.application.organization = Apache",
+
+})
public class DubboConfigBindingBeanPostProcessorTest {
- @Bean("applicationBean")
+ @Bean("dubbo-demo-application")
public ApplicationConfig applicationConfig() {
return new ApplicationConfig();
}
@Bean
- public DubboConfigBinder dubboConfigBinder() {
- return new DefaultDubboConfigBinder();
+ public DubboConfigBindingBeanPostProcessor bindingBeanPostProcessor() {
+ return new DubboConfigBindingBeanPostProcessor("dubbo.application", "dubbo-demo-application");
}
+ @Autowired
+ private ApplicationContext applicationContext;
+
@Test
public void test() {
- final AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
-
- applicationContext.register(getClass());
-
- Class> processorClass = DubboConfigBindingBeanPostProcessor.class;
-
- applicationContext.registerBeanDefinition("DubboConfigBindingBeanPostProcessor", rootBeanDefinition(processorClass).addConstructorArgValue("dubbo.application").addConstructorArgValue("applicationBean").getBeanDefinition());
-
- applicationContext.refresh();
-
ApplicationConfig applicationConfig = applicationContext.getBean(ApplicationConfig.class);
- Assertions.assertEquals("dubbo-demo-application", applicationConfig.getName());
-
+ Assert.assertEquals("dubbo-demo-application", applicationConfig.getName());
+ Assert.assertEquals("mercyblitz", applicationConfig.getOwner());
+ Assert.assertEquals("Apache", applicationConfig.getOrganization());
}
}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java
index 27f6c9663ed..9018bb0e4b5 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/beans/factory/annotation/ReferenceBeanBuilderTest.java
@@ -17,6 +17,13 @@
package org.apache.dubbo.config.spring.beans.factory.annotation;
+import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;
+import static org.springframework.util.ReflectionUtils.findField;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.spring.ReferenceBean;
@@ -28,13 +35,6 @@
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;
-import static org.springframework.util.ReflectionUtils.findField;
-
/**
* {@link ReferenceBeanBuilder} Test
*
@@ -81,7 +81,7 @@ public void testBuild() throws Exception {
Assert.assertEquals("dubbo://localhost:12345", referenceBean.getUrl());
Assert.assertEquals("client", referenceBean.getClient());
Assert.assertEquals(true, referenceBean.isGeneric());
- Assert.assertEquals(true, referenceBean.isInjvm());
+ Assert.assertNull(referenceBean.isInjvm());
Assert.assertEquals(false, referenceBean.isCheck());
Assert.assertEquals(null, referenceBean.isInit());
Assert.assertEquals(true, referenceBean.getLazy());
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java
index 5f52a3e0ec0..4945be8a55b 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/EnableDubboConfigTest.java
@@ -26,7 +26,6 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.PropertySource;
@@ -101,7 +100,7 @@ private static class TestMultipleConfig {
}
- @EnableDubboConfig
+ @EnableDubboConfig(multiple = false)
@PropertySource("META-INF/config.properties")
private static class TestConfig {
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java
index eb12225588f..3aa1e86f4e6 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/annotation/provider/DemoServiceImpl.java
@@ -45,11 +45,6 @@ public String sayName(String name) {
@Override
public Box getBox() {
- return new Box() {
- @Override
- public String getName() {
- return "MyBox";
- }
- };
+ throw new UnsupportedOperationException("For Purposes!");
}
}
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/context/annotation/EnableDubboConfigTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/context/annotation/EnableDubboConfigTest.java
index fba874332b3..691f251dac6 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/context/annotation/EnableDubboConfigTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/context/context/annotation/EnableDubboConfigTest.java
@@ -25,11 +25,14 @@
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.spring.context.annotation.EnableDubboConfig;
+import org.junit.Assert;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.PropertySource;
+import java.util.Map;
+
/**
* {@link EnableDubboConfig} Test
*
@@ -93,15 +96,23 @@ public void testMultiple() {
ApplicationConfig applicationBean3 = context.getBean("applicationBean3", ApplicationConfig.class);
Assertions.assertEquals("dubbo-demo-application3", applicationBean3.getName());
+ Map protocolConfigs = context.getBeansOfType(ProtocolConfig.class);
+
+ for (Map.Entry entry : protocolConfigs.entrySet()) {
+ String beanName = entry.getKey();
+ ProtocolConfig protocol = entry.getValue();
+ Assert.assertEquals(beanName, protocol.getName());
+ }
+
}
- @EnableDubboConfig(multiple = true)
+ @EnableDubboConfig
@PropertySource("META-INF/config.properties")
private static class TestMultipleConfig {
}
- @EnableDubboConfig
+ @EnableDubboConfig(multiple = false)
@PropertySource("META-INF/config.properties")
private static class TestConfig {
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/SpringStatusCheckerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/SpringStatusCheckerTest.java
index f3be445103c..e3386e2588d 100644
--- a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/SpringStatusCheckerTest.java
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/status/SpringStatusCheckerTest.java
@@ -20,14 +20,15 @@
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.extension.SpringExtensionFactory;
+import org.junit.Assert;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.context.ApplicationContext;
import org.springframework.context.Lifecycle;
+import org.springframework.web.context.support.GenericWebApplicationContext;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -90,4 +91,14 @@ public void testWithoutLifeCycleRunning() {
interface ApplicationLifeCycle extends Lifecycle, ApplicationContext {
String[] getConfigLocations();
}
+
+ @Test
+ public void testGenericWebApplicationContext() {
+ SpringExtensionFactory.clearContexts();
+ GenericWebApplicationContext context = new GenericWebApplicationContext();
+ SpringExtensionFactory.addApplicationContext(context);
+ SpringStatusChecker checker = new SpringStatusChecker();
+ Status status = checker.check();
+ Assert.assertEquals(Status.Level.UNKNOWN, status.getLevel());
+ }
}
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/config.properties b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/config.properties
index 6e728ccb208..aa79925d4e7 100644
--- a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/config.properties
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/config.properties
@@ -14,9 +14,13 @@ dubbo.module.name = dubbo-demo-module
dubbo.registry.address = zookeeper://192.168.99.100:32770
## protocol
+dubbo.protocol.id = dubbo
dubbo.protocol.name = dubbo
dubbo.protocol.port = 20880
+dubbo.protocols.rest.port=8080
+dubbo.protocols.thrift.port=9090
+
## monitor
dubbo.monitor.address = zookeeper://127.0.0.1:32770
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/AbstractCloudNativeRegistryFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/AbstractCloudNativeRegistryFactory.java
new file mode 100644
index 00000000000..b9a14bd169b
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/AbstractCloudNativeRegistryFactory.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.registry.support.cloud;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.NamedThreadFactory;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.support.AbstractRegistryFactory;
+
+import java.util.concurrent.ScheduledExecutorService;
+
+import static java.lang.System.getProperty;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+
+/**
+ * Abstract {@link CloudNativeRegistry} Factory
+ *
+ * @param The subclass of {@link ServiceInstance}
+ * @since 2.7.1
+ */
+public abstract class AbstractCloudNativeRegistryFactory extends AbstractRegistryFactory {
+
+ private static String SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX =
+ getProperty("dubbo.services.lookup.scheduler.thread.name.prefix ", "dubbo-services-lookup-");
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final ScheduledExecutorService servicesLookupScheduler;
+
+ public AbstractCloudNativeRegistryFactory() {
+ this.servicesLookupScheduler = newSingleThreadScheduledExecutor(
+ new NamedThreadFactory(SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX));
+ }
+
+ @Override
+ protected final Registry createRegistry(URL url) {
+ return new CloudNativeRegistry(url,
+ createCloudServiceRegistry(url),
+ createCloudServiceDiscovery(url),
+ createServiceInstanceFactory(url),
+ servicesLookupScheduler
+ );
+ }
+
+ /**
+ * The subclass implement this method to create {@link CloudServiceRegistry}
+ *
+ * @param url The {@link URL} of Dubbo Registry
+ * @return non-null
+ */
+ protected abstract CloudServiceRegistry createCloudServiceRegistry(URL url);
+
+ /**
+ * The subclass implement this method to create {@link CloudServiceDiscovery}
+ *
+ * @param url The {@link URL} of Dubbo Registry
+ * @return non-null
+ */
+ protected abstract CloudServiceDiscovery createCloudServiceDiscovery(URL url);
+
+ /**
+ * The subclass implement this method to create {@link ServiceInstanceFactory}
+ *
+ * @param url The {@link URL} of Dubbo Registry
+ * @return non-null
+ */
+ protected abstract ServiceInstanceFactory createServiceInstanceFactory(URL url);
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/CloudNativeRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/CloudNativeRegistry.java
new file mode 100644
index 00000000000..7caefea8aab
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/CloudNativeRegistry.java
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.registry.support.cloud;
+
+import org.apache.dubbo.common.Constants;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.UrlUtils;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.support.FailbackRegistry;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import static java.util.Collections.singletonList;
+import static org.apache.dubbo.common.Constants.PATH_KEY;
+import static org.apache.dubbo.common.Constants.PROTOCOL_KEY;
+import static org.apache.dubbo.common.Constants.PROVIDER_SIDE;
+import static org.apache.dubbo.common.Constants.SIDE_KEY;
+
+/**
+ * Dubbo Cloud-Native Service {@link Registry} abstraction
+ *
+ * @param The subclass of {@link ServiceInstance}
+ * @since 2.7.1
+ */
+public class CloudNativeRegistry extends FailbackRegistry {
+
+ /**
+ * The parameter name of {@link #allServicesLookupInterval}
+ */
+ public static final String ALL_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.all.services.lookup.interval";
+
+ /**
+ * The parameter name of {@link #registeredServicesLookupInterval}
+ */
+ public static final String REGISTERED_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.registered.services.lookup.interval";
+
+ /**
+ * The interval in second of lookup service names(only for Dubbo-OPS)
+ */
+ private final long allServicesLookupInterval;
+
+ private final long registeredServicesLookupInterval;
+
+ private final CloudServiceRegistry cloudServiceRegistry;
+
+ private final CloudServiceDiscovery cloudServiceDiscovery;
+
+ private final ServiceInstanceFactory serviceInstanceFactory;
+
+ private final ScheduledExecutorService servicesLookupScheduler;
+
+ public CloudNativeRegistry(URL url,
+ CloudServiceRegistry cloudServiceRegistry,
+ CloudServiceDiscovery cloudServiceDiscovery,
+ ServiceInstanceFactory serviceInstanceFactory,
+ ScheduledExecutorService servicesLookupScheduler) {
+ super(url);
+ this.allServicesLookupInterval = url.getParameter(ALL_SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 30L);
+ this.registeredServicesLookupInterval = url.getParameter(REGISTERED_SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 300L);
+ this.cloudServiceRegistry = cloudServiceRegistry;
+ this.cloudServiceDiscovery = cloudServiceDiscovery;
+ this.serviceInstanceFactory = serviceInstanceFactory;
+ this.servicesLookupScheduler = servicesLookupScheduler;
+ }
+
+ protected boolean shouldRegister(S serviceInstance) {
+ Map metadata = serviceInstance.getMetadata();
+ String side = metadata.get(SIDE_KEY);
+ return PROVIDER_SIDE.equals(side); // Only register the Provider.
+ }
+
+ @Override
+ public void doRegister(URL url) {
+ S serviceInstance = serviceInstanceFactory.create(url);
+ if (shouldRegister(serviceInstance)) {
+ cloudServiceRegistry.register(serviceInstance);
+ }
+ }
+
+ @Override
+ public void doUnregister(URL url) {
+ S serviceInstance = serviceInstanceFactory.create(url);
+ if (shouldRegister(serviceInstance)) {
+ cloudServiceRegistry.deregister(serviceInstance);
+ }
+ }
+
+ @Override
+ public void doSubscribe(URL url, NotifyListener listener) {
+ List serviceNames = getServiceNames(url, listener);
+ doSubscribe(url, listener, serviceNames);
+ this.servicesLookupScheduler.scheduleAtFixedRate(() -> {
+ doSubscribe(url, listener, serviceNames);
+ }, registeredServicesLookupInterval, registeredServicesLookupInterval, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void doUnsubscribe(URL url, NotifyListener listener) {
+ if (isAdminProtocol(url)) {
+ shutdownServiceNamesLookup();
+ }
+ }
+
+ private void shutdownServiceNamesLookup() {
+ if (servicesLookupScheduler != null) {
+ servicesLookupScheduler.shutdown();
+ }
+ }
+
+ /**
+ * Get all service names
+ *
+ * @return non-null {@link List}
+ */
+ protected List getAllServiceNames() {
+ return cloudServiceDiscovery.getServices();
+ }
+
+ /**
+ * Get the service names from the specified {@link URL url}
+ *
+ * @param url {@link URL}
+ * @param listener {@link NotifyListener}
+ * @return non-null
+ */
+ private List getServiceNames(URL url, NotifyListener listener) {
+ if (isAdminProtocol(url)) {
+ initAllServicesLookupScheduler(url, listener);
+ return getServiceNamesForOps(url);
+ } else {
+ return singletonList(serviceInstanceFactory.createServiceName(url));
+ }
+ }
+
+ /**
+ * Get the service names for Dubbo OPS
+ *
+ * @param url {@link URL}
+ * @return non-null
+ */
+ private List getServiceNamesForOps(URL url) {
+ List serviceNames = getAllServiceNames();
+ filterServiceNames(serviceNames);
+ return serviceNames;
+ }
+
+
+ private boolean isAdminProtocol(URL url) {
+ return Constants.ADMIN_PROTOCOL.equals(url.getProtocol());
+ }
+
+ private void filter(Collection collection, Predicate predicate) {
+ Iterator iterator = collection.iterator();
+ while (iterator.hasNext()) {
+ T data = iterator.next();
+ if (!predicate.test(data)) { // remove if not accept
+ iterator.remove();
+ }
+ }
+ }
+
+ private void initAllServicesLookupScheduler(final URL url, final NotifyListener listener) {
+ servicesLookupScheduler.scheduleAtFixedRate(() -> {
+ List serviceNames = getAllServiceNames();
+ filterServiceNames(serviceNames);
+ doSubscribe(url, listener, serviceNames);
+ }, allServicesLookupInterval, allServicesLookupInterval, TimeUnit.SECONDS);
+ }
+
+ private void filterServiceNames(List serviceNames) {
+ filter(serviceNames, cloudServiceDiscovery::supports);
+ }
+
+ private void doSubscribe(final URL url, final NotifyListener listener, final Collection serviceNames) {
+ Collection serviceInstances = serviceNames.stream()
+ .map(cloudServiceDiscovery::getServiceInstances)
+ .flatMap(v -> v.stream())
+ .collect(Collectors.toList());
+ notifySubscriber(url, listener, serviceInstances);
+ }
+
+ /**
+ * Notify the Healthy {@link S service instance} to subscriber.
+ *
+ * @param url {@link URL}
+ * @param listener {@link NotifyListener}
+ * @param serviceInstances all {@link S registrations}
+ */
+ private void notifySubscriber(URL url, NotifyListener listener, Collection serviceInstances) {
+ Set healthyServiceInstances = new LinkedHashSet(serviceInstances);
+ // Healthy Instances
+ filterHealthyInstances(healthyServiceInstances);
+ List urls = buildURLs(url, healthyServiceInstances);
+ this.notify(url, listener, urls);
+ }
+
+ private void filterHealthyInstances(Collection serviceInstances) {
+ filter(serviceInstances, cloudServiceRegistry::isHealthy);
+ }
+
+ private List buildURLs(URL consumerURL, Collection serviceInstances) {
+ if (serviceInstances.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List urls = new LinkedList();
+ for (S serviceInstance : serviceInstances) {
+ URL url = buildURL(serviceInstance);
+ if (UrlUtils.isMatch(consumerURL, url)) {
+ urls.add(url.setPath(consumerURL.getPath()));
+ }
+ }
+ return urls;
+ }
+
+ private URL buildURL(S serviceInstance) {
+ Map metadata = serviceInstance.getMetadata();
+ String path = metadata.get(PATH_KEY);
+ String protocol = metadata.get(PROTOCOL_KEY);
+ URL url = new URL(protocol,
+ serviceInstance.getHost(),
+ serviceInstance.getPort(),
+ path,
+ serviceInstance.getMetadata());
+ return url;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return cloudServiceRegistry.isAvailable();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/CloudServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/CloudServiceDiscovery.java
new file mode 100644
index 00000000000..ff8907abb76
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/CloudServiceDiscovery.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.registry.support.cloud;
+
+import java.util.List;
+
+/**
+ * Cloud {@link ServiceInstance Service} Discovery
+ *
+ * @param The subclass of {@link ServiceInstance}
+ * @since 2.7.1
+ */
+public interface CloudServiceDiscovery {
+
+ /**
+ * The total number of all services.
+ *
+ * @return must be equal or more than 0
+ */
+ default long getTotalServices() {
+ return getServices().size();
+ }
+
+ /**
+ * Get all service names
+ *
+ * @return non-null read-only {@link List}
+ */
+ List getServices();
+
+
+ /**
+ * Get all service instances by the specified name
+ *
+ * @param serviceName the service name
+ * @return non-null read-only {@link List}
+ */
+ List getServiceInstances(String serviceName);
+
+ /**
+ * Supports the specified name of Cloud Service or not
+ *
+ * @param serviceName the specified service name
+ * @return if supports, return true
, or false
+ */
+ boolean supports(String serviceName);
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/CloudServiceRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/CloudServiceRegistry.java
new file mode 100644
index 00000000000..eb0eaf032f5
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/CloudServiceRegistry.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.registry.support.cloud;
+
+/**
+ * Cloud {@link ServiceInstance Service} Registry
+ *
+ * @param The subclass of {@link ServiceInstance}
+ * @since 2.7.1
+ */
+public interface CloudServiceRegistry extends AutoCloseable {
+
+ /**
+ * Registers the {@link ServiceInstance}.
+ *
+ * @param serviceInstance The {@link ServiceInstance}
+ */
+ void register(S serviceInstance);
+
+ /**
+ * Deregisters the {@link ServiceInstance}
+ *
+ * @param serviceInstance The {@link ServiceInstance}
+ */
+ void deregister(S serviceInstance);
+
+
+ /**
+ * Test the specified {@link ServiceInstance} is healthy or not
+ *
+ * @param serviceInstance The {@link ServiceInstance}
+ * @return true
if the specified {@link ServiceInstance} is healthy
+ */
+ boolean isHealthy(S serviceInstance);
+
+ /**
+ * Is available or not
+ *
+ * @return true
if current {@link CloudServiceRegistry} is available
+ */
+ boolean isAvailable();
+
+}
diff --git a/dubbo-test/dubbo-test-compatibility/dubbo-test-spring3/src/main/java/org/apache/dubbo/test/provider/DefaultDemoService.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/ServiceInstance.java
similarity index 56%
rename from dubbo-test/dubbo-test-compatibility/dubbo-test-spring3/src/main/java/org/apache/dubbo/test/provider/DefaultDemoService.java
rename to dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/ServiceInstance.java
index 7c747717ef9..fae97bc6374 100644
--- a/dubbo-test/dubbo-test-compatibility/dubbo-test-spring3/src/main/java/org/apache/dubbo/test/provider/DefaultDemoService.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/ServiceInstance.java
@@ -14,27 +14,47 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.test.provider;
+package org.apache.dubbo.registry.support.cloud;
-import org.apache.dubbo.config.annotation.Service;
-import org.apache.dubbo.demo.DemoService;
+import java.util.Map;
/**
- * Default {@link DemoService} implementation
+ * The Service Instance
*
- * @since 2.5.8
+ * @since 2.7.1
*/
-@Service(
- version = "2.5.8",
- application = "dubbo-annotation-provider",
- protocol = "dubbo",
- registry = "my-registry"
-)
-public class DefaultDemoService implements DemoService {
+public interface ServiceInstance {
- @Override
- public String sayHello(String name) {
- return "DefaultDemoService - sayHell() : " + name;
+ /**
+ * @return The service name
+ */
+ String getServiceName();
+
+ /**
+ * @return The Host
+ */
+ String getHost();
+
+ /**
+ * @return The service port
+ */
+ int getPort();
+
+ /**
+ * @return The read-only metadata
+ */
+ Map getMetadata();
+
+ /**
+ * @return The scheme of the service instance.
+ */
+ default String getScheme() {
+ return null;
}
+ @Override
+ boolean equals(Object o);
+
+ @Override
+ int hashCode();
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/ServiceInstanceFactory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/ServiceInstanceFactory.java
new file mode 100644
index 00000000000..7f8d21ebc92
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/support/cloud/ServiceInstanceFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.registry.support.cloud;
+
+import org.apache.dubbo.common.URL;
+
+/**
+ * {@link ServiceInstance} Factory to create a instance of {@link ServiceInstance}
+ *
+ * @param The subclass of {@link ServiceInstance}
+ * @since 2.7.1
+ */
+public interface ServiceInstanceFactory {
+
+ /**
+ * Creates a instance of {@link S}
+ *
+ * @param url The Dubbo's {@link URL}
+ * @return a instance of {@link S}, if null, it indicates the registration will not be executed.
+ */
+ S create(URL url);
+
+ /**
+ * Creates the Service Name
+ *
+ * @param url The Dubbo's {@link URL}
+ * @return non-null
+ */
+ String createServiceName(URL url);
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/pom.xml b/dubbo-registry/dubbo-registry-nacos/pom.xml
new file mode 100644
index 00000000000..2abf8b07538
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/pom.xml
@@ -0,0 +1,148 @@
+
+
+
+ org.apache.dubbo
+ dubbo-registry
+ 2.7.1-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ org.apache.dubbo
+ dubbo-registry-nacos
+ ${project.artifactId}
+ The Nacos registry module of Dubbo project
+
+
+ UTF-8
+ UTF-8
+ 0.9.0
+
+
+
+
+
+ org.apache.dubbo
+ dubbo-registry-api
+ ${project.version}
+ true
+
+
+
+ org.apache.dubbo
+ dubbo-common
+ ${project.version}
+ true
+
+
+
+ com.alibaba.nacos
+ nacos-client
+ ${nacos.version}
+ true
+
+
+
+
+ org.apache.dubbo
+ dubbo-config-api
+ ${project.version}
+ test
+
+
+
+ org.apache.dubbo
+ dubbo-serialization-hessian2
+ ${project.version}
+ test
+
+
+
+ org.apache.dubbo
+ dubbo-config-spring
+ ${project.version}
+ test
+
+
+
+ org.apache.dubbo
+ dubbo-rpc-dubbo
+ ${project.version}
+ test
+
+
+
+ org.apache.dubbo
+ dubbo-remoting-netty4
+ ${project.version}
+ test
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+ test
+
+
+
+
+ org.apache.dubbo
+ dubbo-rpc-rest
+ ${project.version}
+ test
+
+
+
+ org.jboss.resteasy
+ resteasy-jaxrs
+ test
+
+
+
+ org.jboss.resteasy
+ resteasy-client
+ test
+
+
+
+ org.jboss.resteasy
+ resteasy-netty4
+ test
+
+
+
+ javax.validation
+ validation-api
+ test
+
+
+
+ org.jboss.resteasy
+ resteasy-jackson-provider
+ test
+
+
+
+ org.jboss.resteasy
+ resteasy-jaxb-provider
+ test
+
+
+
+ org.springframework
+ spring-test
+ test
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosCloudService.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosCloudService.java
new file mode 100644
index 00000000000..2d8d4ed11a6
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosCloudService.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.registry.nacos;
+
+import com.alibaba.nacos.api.exception.NacosException;
+import com.alibaba.nacos.api.naming.NamingService;
+import com.alibaba.nacos.api.naming.pojo.Instance;
+
+import org.apache.dubbo.registry.support.cloud.CloudServiceDiscovery;
+import org.apache.dubbo.registry.support.cloud.CloudServiceRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.apache.commons.lang3.StringUtils.startsWithIgnoreCase;
+import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY;
+import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY;
+
+/**
+ * Nacos Cloud Service implements {@link CloudServiceRegistry} and {@link CloudServiceDiscovery}
+ */
+class NacosCloudService implements CloudServiceRegistry, CloudServiceDiscovery {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final NamingService namingService;
+
+ public NacosCloudService(NamingService namingService) {
+ this.namingService = namingService;
+ }
+
+ @Override
+ public List getServices() {
+ return execute(namingService ->
+ namingService.getServicesOfServer(0, Integer.MAX_VALUE).getData()
+ );
+ }
+
+ @Override
+ public List getServiceInstances(String serviceName) {
+ return execute(namingService ->
+ namingService.selectInstances(serviceName, true)
+ .stream()
+ .filter(Instance::isEnabled)
+ .map(NacosServiceInstance::new)
+ .collect(Collectors.toList())
+ );
+ }
+
+ @Override
+ public boolean supports(String serviceName) {
+ return startsWithIgnoreCase(serviceName, PROVIDERS_CATEGORY) ||
+ startsWithIgnoreCase(serviceName, CONSUMERS_CATEGORY);
+ }
+
+ @Override
+ public void register(NacosServiceInstance serviceInstance) {
+ execute(namingService -> {
+ namingService.registerInstance(serviceInstance.getServiceName(), serviceInstance.getSource());
+ return null;
+ });
+ }
+
+ @Override
+ public void deregister(NacosServiceInstance serviceInstance) {
+ execute(namingService -> {
+ namingService.deregisterInstance(serviceInstance.getServiceName(),
+ serviceInstance.getHost(), serviceInstance.getPort());
+ return null;
+ });
+ }
+
+ @Override
+ public boolean isHealthy(NacosServiceInstance serviceInstance) {
+ return serviceInstance.getSource().isHealthy();
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return "UP".equals(namingService.getServerStatus());
+ }
+
+ @Override
+ public void close() throws Exception {
+ // DO NOTHING
+ }
+
+ private T execute(NamingServiceFunction function) {
+ try {
+ return function.apply(namingService);
+ } catch (NacosException e) {
+ if (logger.isErrorEnabled()) {
+ logger.error(e.getErrMsg(), e);
+ }
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * {@link NamingService} Function
+ */
+ interface NamingServiceFunction {
+
+ /**
+ * Callback
+ *
+ * @param namingService {@link NamingService}
+ * @throws NacosException
+ */
+ T apply(NamingService namingService) throws NacosException;
+
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java
new file mode 100644
index 00000000000..02e0d61434a
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosRegistryFactory.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.registry.nacos;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.RegistryFactory;
+import org.apache.dubbo.registry.support.cloud.AbstractCloudNativeRegistryFactory;
+import org.apache.dubbo.registry.support.cloud.CloudServiceDiscovery;
+import org.apache.dubbo.registry.support.cloud.CloudServiceRegistry;
+import org.apache.dubbo.registry.support.cloud.ServiceInstanceFactory;
+
+import com.alibaba.nacos.api.NacosFactory;
+import com.alibaba.nacos.api.exception.NacosException;
+import com.alibaba.nacos.api.naming.NamingService;
+import com.alibaba.nacos.client.naming.utils.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY;
+import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME;
+import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
+import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
+import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY;
+import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
+import static com.alibaba.nacos.client.naming.utils.UtilAndComs.NACOS_NAMING_LOG_NAME;
+import static org.apache.dubbo.common.Constants.BACKUP_KEY;
+
+/**
+ * Nacos {@link RegistryFactory}
+ *
+ * @since 2.6.6
+ */
+public class NacosRegistryFactory extends AbstractCloudNativeRegistryFactory {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private Map nacosCloudServicesCache = new HashMap<>();
+
+ private NamingService buildNamingService(URL url) {
+ Properties nacosProperties = buildNacosProperties(url);
+ NamingService namingService = null;
+ try {
+ namingService = NacosFactory.createNamingService(nacosProperties);
+ } catch (NacosException e) {
+ if (logger.isErrorEnabled()) {
+ logger.error(e.getErrMsg(), e);
+ }
+ throw new IllegalStateException(e);
+ }
+ return namingService;
+ }
+
+ private Properties buildNacosProperties(URL url) {
+ Properties properties = new Properties();
+ setServerAddr(url, properties);
+ setProperties(url, properties);
+ return properties;
+ }
+
+ private void setServerAddr(URL url, Properties properties) {
+ StringBuilder serverAddrBuilder =
+ new StringBuilder(url.getHost()) // Host
+ .append(":")
+ .append(url.getPort()); // Port
+
+ // Append backup parameter as other servers
+ String backup = url.getParameter(BACKUP_KEY);
+ if (backup != null) {
+ serverAddrBuilder.append(",").append(backup);
+ }
+
+ String serverAddr = serverAddrBuilder.toString();
+ properties.put(SERVER_ADDR, serverAddr);
+ }
+
+ private void setProperties(URL url, Properties properties) {
+ putPropertyIfAbsent(url, properties, NAMESPACE);
+ putPropertyIfAbsent(url, properties, NACOS_NAMING_LOG_NAME);
+ putPropertyIfAbsent(url, properties, ENDPOINT);
+ putPropertyIfAbsent(url, properties, NAMESPACE);
+ putPropertyIfAbsent(url, properties, ACCESS_KEY);
+ putPropertyIfAbsent(url, properties, SECRET_KEY);
+ putPropertyIfAbsent(url, properties, CLUSTER_NAME);
+ }
+
+ private void putPropertyIfAbsent(URL url, Properties properties, String propertyName) {
+ String propertyValue = url.getParameter(propertyName);
+ if (StringUtils.isNotEmpty(propertyValue)) {
+ properties.setProperty(propertyName, propertyValue);
+ }
+ }
+
+ private NacosCloudService createNacosCloudService(URL url) {
+ nacosCloudServicesCache.computeIfAbsent(url, u ->
+ new NacosCloudService(buildNamingService(u))
+ );
+ return nacosCloudServicesCache.get(url);
+ }
+
+ @Override
+ protected CloudServiceRegistry createCloudServiceRegistry(URL url) {
+ return createNacosCloudService(url);
+ }
+
+ @Override
+ protected CloudServiceDiscovery createCloudServiceDiscovery(URL url) {
+ return createNacosCloudService(url);
+ }
+
+ @Override
+ protected ServiceInstanceFactory createServiceInstanceFactory(URL url) {
+ return new NacosServiceInstanceFactory();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceInstance.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceInstance.java
new file mode 100644
index 00000000000..775df9aadab
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceInstance.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.registry.nacos;
+
+import org.apache.dubbo.registry.support.cloud.ServiceInstance;
+
+import com.alibaba.nacos.api.naming.pojo.Instance;
+
+import java.util.Map;
+
+/**
+ * Nacos {@link ServiceInstance}
+ */
+public class NacosServiceInstance implements ServiceInstance {
+
+ private final Instance source;
+
+ public NacosServiceInstance(Instance instance) {
+ this.source = instance;
+ }
+
+ @Override
+ public String getServiceName() {
+ return source.getServiceName();
+ }
+
+ @Override
+ public String getHost() {
+ return source.getIp();
+ }
+
+ @Override
+ public int getPort() {
+ return source.getPort();
+ }
+
+ @Override
+ public Map getMetadata() {
+ return source.getMetadata();
+ }
+
+ @Override
+ public String getScheme() {
+ return "nacos";
+ }
+
+ public Instance getSource() {
+ return source;
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceInstanceFactory.java b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceInstanceFactory.java
new file mode 100644
index 00000000000..fba511b39d2
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/java/org/apache/dubbo/registry/nacos/NacosServiceInstanceFactory.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.registry.nacos;
+
+import com.alibaba.nacos.api.naming.pojo.Instance;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.registry.support.cloud.ServiceInstanceFactory;
+
+import java.util.LinkedHashMap;
+import java.util.Objects;
+
+import static java.lang.System.getProperty;
+import static org.apache.dubbo.common.Constants.CATEGORY_KEY;
+import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY;
+import static org.apache.dubbo.common.Constants.DEFAULT_CATEGORY;
+import static org.apache.dubbo.common.Constants.GROUP_KEY;
+import static org.apache.dubbo.common.Constants.INTERFACE_KEY;
+import static org.apache.dubbo.common.Constants.PATH_KEY;
+import static org.apache.dubbo.common.Constants.PROTOCOL_KEY;
+import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY;
+import static org.apache.dubbo.common.Constants.VERSION_KEY;
+
+/**
+ * Nacos {@link ServiceInstanceFactory}
+ *
+ * @since 2.7.1
+ */
+public class NacosServiceInstanceFactory implements ServiceInstanceFactory {
+
+ /**
+ * The separator for service name
+ */
+ private static final String SERVICE_NAME_SEPARATOR = getProperty("dubbo.service.name.separator", ":");
+
+ @Override
+ public NacosServiceInstance create(URL url) {
+ Instance instance = createInstance(url);
+ return new NacosServiceInstance(instance);
+ }
+
+ private Instance createInstance(URL url) {
+ // Append default category if absent
+ String category = url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
+ URL newURL = url.addParameter(CATEGORY_KEY, category);
+ newURL = newURL.addParameter(PROTOCOL_KEY, url.getProtocol());
+ newURL = newURL.addParameter(PATH_KEY, url.getPath());
+ String ip = url.getHost();
+ int port = url.getPort();
+ String serviceName = createServiceName(url);
+ Instance instance = new Instance();
+ instance.setServiceName(serviceName);
+ instance.setIp(ip);
+ instance.setPort(port);
+ instance.setMetadata(new LinkedHashMap<>(newURL.getParameters()));
+ return instance;
+ }
+
+ @Override
+ public String createServiceName(URL url) {
+ String category = url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
+ if (!Objects.equals(category, PROVIDERS_CATEGORY) && !Objects.equals(category, CONSUMERS_CATEGORY)) {
+ category = PROVIDERS_CATEGORY;
+ }
+ return createServiceName(url, category);
+ }
+
+ private static String createServiceName(URL url, String category) {
+ StringBuilder serviceNameBuilder = new StringBuilder(category);
+ appendIfPresent(serviceNameBuilder, url, INTERFACE_KEY);
+ appendIfPresent(serviceNameBuilder, url, VERSION_KEY);
+ appendIfPresent(serviceNameBuilder, url, GROUP_KEY);
+ return serviceNameBuilder.toString();
+ }
+
+ private static void appendIfPresent(StringBuilder target, URL url,
+ String parameterName) {
+ String parameterValue = url.getParameter(parameterName);
+ appendIfPresent(target, parameterValue);
+ }
+
+ private static void appendIfPresent(StringBuilder target, String parameterValue) {
+ if (StringUtils.isNotEmpty(parameterValue)) {
+ target.append(SERVICE_NAME_SEPARATOR).append(parameterValue);
+ }
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/org.apache.dubbo.registry.RegistryFactory b/dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/org.apache.dubbo.registry.RegistryFactory
new file mode 100644
index 00000000000..bb754674978
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/main/resources/META-INF/dubbo/org.apache.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+nacos=org.apache.dubbo.registry.nacos.NacosRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerBootstrap.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerBootstrap.java
new file mode 100644
index 00000000000..3e0caf20275
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerBootstrap.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.demo.consumer;
+
+import org.apache.dubbo.config.annotation.Reference;
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+import org.apache.dubbo.demo.service.DemoService;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.PropertySource;
+
+import javax.annotation.PostConstruct;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * {@link DemoService} consumer demo
+ */
+@EnableDubbo
+@PropertySource(value = "classpath:/consumer-config.properties")
+public class DemoServiceConsumerBootstrap {
+
+ @Reference(version = "${demo.service.version}")
+ private DemoService demoService;
+
+ @Reference(version = "${demo.service.version}", protocol = "rest")
+ private DemoService restDemoService;
+
+ @PostConstruct
+ public void init() throws InterruptedException {
+ for (int j = 0; j < 10; j++) {
+ System.out.println(demoService.sayName("小马哥(mercyblitz)"));
+// System.out.println(restDemoService.sayName("小马哥(mercyblitz)"));
+ }
+ Thread.sleep(TimeUnit.SECONDS.toMillis(5));
+ }
+
+ public static void main(String[] args) throws IOException {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.register(DemoServiceConsumerBootstrap.class);
+ context.refresh();
+ System.in.read();
+ context.close();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerXmlBootstrap.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerXmlBootstrap.java
new file mode 100644
index 00000000000..d2a7a7bd9fe
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/consumer/DemoServiceConsumerXmlBootstrap.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.demo.consumer;
+
+import org.apache.dubbo.demo.service.DemoService;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.io.IOException;
+
+/**
+ * {@link DemoService} consumer demo XML bootstrap
+ */
+public class DemoServiceConsumerXmlBootstrap {
+
+ public static void main(String[] args) throws IOException {
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
+ context.setConfigLocation("/META-INF/spring/dubbo-consumer-context.xml");
+ context.refresh();
+ System.out.println("DemoService consumer (XML) is starting...");
+ DemoService demoService = context.getBean("demoService", DemoService.class);
+ for (int i = 0; i < 10; i++) {
+ System.out.println(demoService.sayName("小马哥(mercyblitz)"));
+ }
+ System.in.read();
+ context.close();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderBootstrap.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderBootstrap.java
new file mode 100644
index 00000000000..c1d2ec4f0f5
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderBootstrap.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.demo.provider;
+
+import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
+import org.apache.dubbo.demo.service.DemoService;
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.PropertySource;
+
+import java.io.IOException;
+
+/**
+ * {@link DemoService} provider demo
+ */
+@EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.service")
+@PropertySource(value = "classpath:/provider-config.properties")
+public class DemoServiceProviderBootstrap {
+
+ public static void main(String[] args) throws IOException {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.register(DemoServiceProviderBootstrap.class);
+ context.refresh();
+ System.out.println("DemoService provider is starting...");
+ System.in.read();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderXmlBootstrap.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderXmlBootstrap.java
new file mode 100644
index 00000000000..0a37f3fabe9
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/provider/DemoServiceProviderXmlBootstrap.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.demo.provider;
+
+import org.apache.dubbo.demo.service.DemoService;
+
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.io.IOException;
+
+/**
+ * {@link DemoService} provider demo XML bootstrap
+ */
+public class DemoServiceProviderXmlBootstrap {
+
+ public static void main(String[] args) throws IOException {
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
+ context.setConfigLocation("/META-INF/spring/dubbo-provider-context.xml");
+ context.refresh();
+ System.out.println("DemoService provider (XML) is starting...");
+ System.in.read();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DefaultService.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DefaultService.java
new file mode 100644
index 00000000000..a7c87d32afd
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DefaultService.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.dubbo.demo.service;
+
+import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.rpc.RpcContext;
+import org.springframework.beans.factory.annotation.Value;
+
+
+/**
+ * Default {@link DemoService}
+ *
+ * @since 2.6.5
+ */
+@Service(version = "${demo.service.version}")
+public class DefaultService implements DemoService {
+
+ @Value("${demo.service.name}")
+ private String serviceName;
+
+ public String sayName(String name) {
+ RpcContext rpcContext = RpcContext.getContext();
+ return String.format("Service [name :%s , protocol: %s , port : %d] %s(\"%s\") : Hello,%s",
+ serviceName,
+ rpcContext.getUrl().getProtocol(),
+ rpcContext.getLocalPort(),
+ rpcContext.getMethodName(),
+ name,
+ name);
+ }
+}
diff --git a/dubbo-test/dubbo-test-compatibility/dubbo-test-spring3/src/main/java/org/apache/dubbo/test/provider/ProviderConfiguration.java b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DemoService.java
similarity index 66%
rename from dubbo-test/dubbo-test-compatibility/dubbo-test-spring3/src/main/java/org/apache/dubbo/test/provider/ProviderConfiguration.java
rename to dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DemoService.java
index d959e544516..0c808779311 100644
--- a/dubbo-test/dubbo-test-compatibility/dubbo-test-spring3/src/main/java/org/apache/dubbo/test/provider/ProviderConfiguration.java
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/java/org/apache/dubbo/demo/service/DemoService.java
@@ -14,20 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.test.provider;
+package org.apache.dubbo.demo.service;
-import org.apache.dubbo.config.spring.context.annotation.DubboComponentScan;
-
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.ImportResource;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
/**
- * Provider {@Link Configuration}
+ * DemoService
*
- * @since 2.5.8
+ * @since 2.6.5
*/
-@Configuration
-@ImportResource("META-INF/spring/dubbo-provider.xml")
-@DubboComponentScan
-public class ProviderConfiguration {
-}
+@Path("/demo-service")
+public interface DemoService {
+
+ @GET
+ String sayName(@QueryParam("name") String name);
+
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-consumer-context.xml b/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-consumer-context.xml
new file mode 100644
index 00000000000..d4ad9324816
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-consumer-context.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-provider-context.xml b/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-provider-context.xml
new file mode 100644
index 00000000000..adf937dd544
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/resources/META-INF/spring/dubbo-provider-context.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/resources/consumer-config.properties b/dubbo-registry/dubbo-registry-nacos/src/test/resources/consumer-config.properties
new file mode 100644
index 00000000000..d32e6e8a64e
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/resources/consumer-config.properties
@@ -0,0 +1,6 @@
+## Dubbo Application info
+dubbo.application.name=dubbo-consumer-demo
+## Nacos registry address
+dubbo.registry.address=nacos://127.0.0.1:8848
+# @Reference version
+demo.service.version=1.0.0
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-nacos/src/test/resources/provider-config.properties b/dubbo-registry/dubbo-registry-nacos/src/test/resources/provider-config.properties
new file mode 100644
index 00000000000..e2dd335b0ed
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-nacos/src/test/resources/provider-config.properties
@@ -0,0 +1,14 @@
+## Dubbo Application info
+dubbo.application.name=dubbo-provider-demo
+## Nacos registry address
+dubbo.registry.protocol=nacos
+dubbo.registry.address=127.0.0.1:8848
+## Exports multiple protocols
+### Dubbo Protocol using random port
+dubbo.protocols.dubbo.port=-1
+### REST protocol
+dubbo.protocols.rest.port=9090
+dubbo.protocols.rest.server=netty
+# Provider @Service info
+demo.service.version=1.0.0
+demo.service.name=demoService
\ No newline at end of file
diff --git a/dubbo-registry/pom.xml b/dubbo-registry/pom.xml
index 33a329bb0fd..ea09685010a 100644
--- a/dubbo-registry/pom.xml
+++ b/dubbo-registry/pom.xml
@@ -36,5 +36,6 @@
dubbo-registry-redis
dubbo-registry-consul
dubbo-registry-etcd3
+ dubbo-registry-nacos