serviceRestMetadata = new LinkedHashSet<>();
+
+ @Autowired
+ private MetadataResolver metadataResolver;
+
+ @Autowired
+ private MetadataConfigService metadataConfigService;
+
+ @EventListener(ServiceBeanExportedEvent.class)
+ public void recordRestMetadata(ServiceBeanExportedEvent event) throws JsonProcessingException {
+ ServiceBean serviceBean = event.getServiceBean();
+ serviceRestMetadata.addAll(metadataResolver.resolveServiceRestMetadata(serviceBean));
+ }
+
+ /**
+ * Pre-handle Spring Cloud application service registered:
+ *
+ * Put restMetadata
with the JSON format into
+ * {@link Registration#getMetadata() service instances' metadata}
+ *
+ *
+ * @param event {@link InstancePreRegisteredEvent} instance
+ */
+ @EventListener(InstancePreRegisteredEvent.class)
+ public void registerRestMetadata(InstancePreRegisteredEvent event) throws Exception {
+ Registration registration = event.getRegistration();
+ metadataConfigService.publishServiceRestMetadata(registration.getServiceId(), serviceRestMetadata);
+ }
+
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/context/DubboServiceRegistrationApplicationContextInitializer.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/context/DubboServiceRegistrationApplicationContextInitializer.java
new file mode 100644
index 00000000..f1283e91
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/context/DubboServiceRegistrationApplicationContextInitializer.java
@@ -0,0 +1,38 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.context;
+
+import org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistryFactory;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * The Dubbo services will be registered as the specified Spring cloud applications that will not be considered
+ * normal ones, but only are used to Dubbo's service discovery even if it is based on Spring Cloud Commons abstraction.
+ * However, current application will be registered by other DiscoveryClientAutoConfiguration.
+ *
+ * @author Mercy
+ */
+public class DubboServiceRegistrationApplicationContextInitializer implements
+ ApplicationContextInitializer {
+
+ @Override
+ public void initialize(ConfigurableApplicationContext applicationContext) {
+ // Set ApplicationContext into SpringCloudRegistryFactory before Dubbo Service Register
+ SpringCloudRegistryFactory.setApplicationContext(applicationContext);
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/MethodMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/MethodMetadata.java
new file mode 100644
index 00000000..18abf0e9
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/MethodMetadata.java
@@ -0,0 +1,122 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.metadata;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.apache.commons.lang3.ClassUtils;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * {@link Method} Metadata
+ *
+ * @author Mercy
+ */
+public class MethodMetadata {
+
+ private String name;
+
+ private String returnType;
+
+ private List params;
+
+ @JsonIgnore
+ private Method method;
+
+ public MethodMetadata() {
+ this.params = new LinkedList<>();
+ }
+
+ public MethodMetadata(Method method) {
+ this.name = method.getName();
+ this.returnType = ClassUtils.getName(method.getReturnType());
+ this.params = initParameters(method);
+ this.method = method;
+ }
+
+ private List initParameters(Method method) {
+ int parameterCount = method.getParameterCount();
+ if (parameterCount < 1) {
+ return Collections.emptyList();
+ }
+ List params = new ArrayList<>(parameterCount);
+ Parameter[] parameters = method.getParameters();
+ for (int i = 0; i < parameterCount; i++) {
+ Parameter parameter = parameters[i];
+ MethodParameterMetadata param = toMethodParameterMetadata(i, parameter);
+ params.add(param);
+ }
+ return params;
+ }
+
+ private MethodParameterMetadata toMethodParameterMetadata(int index, Parameter parameter) {
+ MethodParameterMetadata metadata = new MethodParameterMetadata();
+ metadata.setIndex(index);
+ metadata.setName(parameter.getName());
+ metadata.setType(parameter.getType().getTypeName());
+ return metadata;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getReturnType() {
+ return returnType;
+ }
+
+ public void setReturnType(String returnType) {
+ this.returnType = returnType;
+ }
+
+ public List getParams() {
+ return params;
+ }
+
+ public void setParams(List params) {
+ this.params = params;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MethodMetadata that = (MethodMetadata) o;
+ return Objects.equals(name, that.name) &&
+ Objects.equals(returnType, that.returnType) &&
+ Objects.equals(params, that.params);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, returnType, params);
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/MethodParameterMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/MethodParameterMetadata.java
new file mode 100644
index 00000000..fbcaad00
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/MethodParameterMetadata.java
@@ -0,0 +1,73 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.metadata;
+
+import java.lang.reflect.Method;
+import java.util.Objects;
+
+/**
+ * {@link Method} Parameter Metadata
+ *
+ * @author Mercy
+ */
+public class MethodParameterMetadata {
+
+ private int index;
+
+ private String name;
+
+ private String type;
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MethodParameterMetadata that = (MethodParameterMetadata) o;
+ return index == that.index &&
+ Objects.equals(name, that.name) &&
+ Objects.equals(type, that.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(index, name, type);
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RequestMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RequestMetadata.java
new file mode 100644
index 00000000..ae3d10dc
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RequestMetadata.java
@@ -0,0 +1,97 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.metadata;
+
+import feign.RequestTemplate;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Request Metadata
+ *
+ * @author Mercy
+ */
+public class RequestMetadata {
+
+ private String method;
+
+ private String url;
+
+ private Map> queries;
+
+ private Map> headers;
+
+ public RequestMetadata() {
+ }
+
+ public RequestMetadata(RequestTemplate requestTemplate) {
+ this.method = requestTemplate.method();
+ this.url = requestTemplate.url();
+ this.queries = requestTemplate.queries();
+ this.headers = requestTemplate.headers();
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public Map> getHeaders() {
+ return headers;
+ }
+
+ public void setHeaders(Map> headers) {
+ this.headers = headers;
+ }
+
+ public Map> getQueries() {
+ return queries;
+ }
+
+ public void setQueries(Map> queries) {
+ this.queries = queries;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RequestMetadata that = (RequestMetadata) o;
+ return Objects.equals(method, that.method) &&
+ Objects.equals(url, that.url) &&
+ Objects.equals(queries, that.queries) &&
+ Objects.equals(headers, that.headers);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, url, queries, headers);
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RestMethodMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RestMethodMetadata.java
new file mode 100644
index 00000000..5024fefa
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/RestMethodMetadata.java
@@ -0,0 +1,72 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.metadata;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Method Request Metadata
+ */
+public class RestMethodMetadata {
+
+ private MethodMetadata method;
+
+ private RequestMetadata request;
+
+ private Map> indexToName;
+
+ public MethodMetadata getMethod() {
+ return method;
+ }
+
+ public void setMethod(MethodMetadata method) {
+ this.method = method;
+ }
+
+ public RequestMetadata getRequest() {
+ return request;
+ }
+
+ public void setRequest(RequestMetadata request) {
+ this.request = request;
+ }
+
+ public Map> getIndexToName() {
+ return indexToName;
+ }
+
+ public void setIndexToName(Map> indexToName) {
+ this.indexToName = indexToName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RestMethodMetadata that = (RestMethodMetadata) o;
+ return Objects.equals(method, that.method) &&
+ Objects.equals(request, that.request) &&
+ Objects.equals(indexToName, that.indexToName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, request, indexToName);
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/ServiceRestMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/ServiceRestMetadata.java
new file mode 100644
index 00000000..d5ba11f4
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/ServiceRestMetadata.java
@@ -0,0 +1,66 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.metadata;
+
+import java.util.Set;
+
+/**
+ * Service Rest Metadata
+ *
+ * @author Mercy
+ * @see RestMethodMetadata
+ */
+public class ServiceRestMetadata {
+
+ private String name;
+
+ private Set meta;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Set getMeta() {
+ return meta;
+ }
+
+ public void setMeta(Set meta) {
+ this.meta = meta;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ServiceRestMetadata that = (ServiceRestMetadata) o;
+
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+ return meta != null ? meta.equals(that.meta) : that.meta == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (meta != null ? meta.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java
new file mode 100644
index 00000000..57a0a133
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java
@@ -0,0 +1,118 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.metadata.repository;
+
+import com.alibaba.dubbo.config.spring.ReferenceBean;
+import com.alibaba.dubbo.rpc.service.GenericService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
+import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
+import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
+import org.springframework.cloud.alibaba.dubbo.metadata.service.MetadataConfigService;
+import org.springframework.stereotype.Repository;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceGroup;
+import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceInterface;
+import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceSegments;
+import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceVersion;
+
+/**
+ * Dubbo Service Metadata {@link Repository}
+ *
+ * @author Mercy
+ */
+@Repository
+public class DubboServiceMetadataRepository {
+
+ /**
+ * Key is application name
+ * Value is Map
+ */
+ private Map> genericServicesRepository = new HashMap<>();
+
+ private Map> methodMetadataRepository = new HashMap<>();
+
+ @Autowired
+ private MetadataConfigService metadataConfigService;
+
+ @Value("${dubbo.target.protocol:dubbo}")
+ private String targetProtocol;
+
+ @Value("${dubbo.target.cluster:failover}")
+ private String targetCluster;
+
+ public void updateMetadata(String serviceName) {
+
+ Map genericServicesMap = genericServicesRepository.computeIfAbsent(serviceName, k -> new HashMap<>());
+
+ Map methodMetadataMap = methodMetadataRepository.computeIfAbsent(serviceName, k -> new HashMap<>());
+
+ Set serviceRestMetadataSet = metadataConfigService.getServiceRestMetadata(serviceName);
+
+ for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) {
+
+ ReferenceBean referenceBean = adaptReferenceBean(serviceRestMetadata);
+
+ serviceRestMetadata.getMeta().forEach(restMethodMetadata -> {
+ RequestMetadata requestMetadata = restMethodMetadata.getRequest();
+ genericServicesMap.put(requestMetadata, referenceBean.get());
+ methodMetadataMap.put(requestMetadata, restMethodMetadata.getMethod());
+ });
+ }
+ }
+
+ public GenericService getGenericService(String serviceName, RequestMetadata requestMetadata) {
+ return getGenericServicesMap(serviceName).get(requestMetadata);
+ }
+
+ public MethodMetadata getMethodMetadata(String serviceName, RequestMetadata requestMetadata) {
+ return getMethodMetadataMap(serviceName).get(requestMetadata);
+ }
+
+ private ReferenceBean adaptReferenceBean(ServiceRestMetadata serviceRestMetadata) {
+ String dubboServiceName = serviceRestMetadata.getName();
+ String[] segments = getServiceSegments(dubboServiceName);
+ String interfaceName = getServiceInterface(segments);
+ String version = getServiceVersion(segments);
+ String group = getServiceGroup(segments);
+
+ ReferenceBean referenceBean = new ReferenceBean();
+ referenceBean.setGeneric(true);
+ referenceBean.setInterface(interfaceName);
+ referenceBean.setVersion(version);
+ referenceBean.setGroup(group);
+ referenceBean.setProtocol(targetProtocol);
+ referenceBean.setCluster(targetCluster);
+
+ return referenceBean;
+ }
+
+ private Map getGenericServicesMap(String serviceName) {
+ return genericServicesRepository.getOrDefault(serviceName, Collections.emptyMap());
+ }
+
+ private Map getMethodMetadataMap(String serviceName) {
+ return methodMetadataRepository.getOrDefault(serviceName, Collections.emptyMap());
+ }
+
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/FeignMetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/FeignMetadataResolver.java
new file mode 100644
index 00000000..e7f80dd4
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/FeignMetadataResolver.java
@@ -0,0 +1,190 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.metadata.resolver;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.config.spring.ServiceBean;
+import feign.Contract;
+import feign.Feign;
+import feign.MethodMetadata;
+import feign.Util;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
+import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
+import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
+import org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry;
+import org.springframework.util.ClassUtils;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * The metadata resolver for {@link Feign}
+ *
+ * @author Mercy
+ */
+public class FeignMetadataResolver implements BeanClassLoaderAware, SmartInitializingSingleton, MetadataResolver {
+
+ private static final String[] CONTRACT_CLASS_NAMES = {
+ "feign.jaxrs2.JAXRS2Contract",
+ "org.springframework.cloud.openfeign.support.SpringMvcContract",
+ };
+
+ private final String currentApplicationName;
+
+ private final ObjectProvider contract;
+
+ private ClassLoader classLoader;
+
+ /**
+ * Feign Contracts
+ */
+ private Collection contracts;
+
+ public FeignMetadataResolver(String currentApplicationName, ObjectProvider contract) {
+ this.currentApplicationName = currentApplicationName;
+ this.contract = contract;
+ }
+
+ @Override
+ public void afterSingletonsInstantiated() {
+
+ LinkedList contracts = new LinkedList<>();
+
+ // Add injected Contract if available, for example SpringMvcContract Bean under Spring Cloud Open Feign
+ contract.ifAvailable(contracts::add);
+
+ Stream.of(CONTRACT_CLASS_NAMES)
+ .filter(this::isClassPresent) // filter the existed classes
+ .map(this::loadContractClass) // load Contract Class
+ .map(this::createContract) // create instance by the specified class
+ .forEach(contracts::add); // add the Contract instance into contracts
+
+ this.contracts = Collections.unmodifiableCollection(contracts);
+ }
+
+ private Contract createContract(Class> contractClassName) {
+ return (Contract) BeanUtils.instantiateClass(contractClassName);
+ }
+
+ private Class> loadContractClass(String contractClassName) {
+ return ClassUtils.resolveClassName(contractClassName, classLoader);
+ }
+
+ private boolean isClassPresent(String className) {
+ return ClassUtils.isPresent(className, classLoader);
+ }
+
+ @Override
+ public Set resolveServiceRestMetadata(ServiceBean serviceBean) {
+
+ Object bean = serviceBean.getRef();
+
+ Class> beanType = bean.getClass();
+
+ Set serviceRestMetadata = new LinkedHashSet<>();
+
+ Set methodRestMetadata = resolveMethodRestMetadata(beanType);
+
+ List urls = serviceBean.getExportedUrls();
+
+ urls.stream()
+ .map(SpringCloudRegistry::getServiceName)
+ .forEach(serviceName -> {
+ ServiceRestMetadata metadata = new ServiceRestMetadata();
+ metadata.setName(serviceName);
+ metadata.setMeta(methodRestMetadata);
+ serviceRestMetadata.add(metadata);
+ });
+
+ return serviceRestMetadata;
+ }
+
+ @Override
+ public Set resolveMethodRestMetadata(Class> targetType) {
+ List feignContractMethods = selectFeignContractMethods(targetType);
+ return contracts.stream()
+ .map(contract -> contract.parseAndValidatateMetadata(targetType))
+ .flatMap(v -> v.stream())
+ .map(methodMetadata -> resolveMethodRestMetadata(methodMetadata, targetType, feignContractMethods))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Select feign contract methods
+ *
+ * extract some code from {@link Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class)}
+ *
+ * @param targetType
+ * @return non-null
+ */
+ private List selectFeignContractMethods(Class> targetType) {
+ List methods = new LinkedList<>();
+ for (Method method : targetType.getMethods()) {
+ if (method.getDeclaringClass() == Object.class ||
+ (method.getModifiers() & Modifier.STATIC) != 0 ||
+ Util.isDefault(method)) {
+ continue;
+ }
+ methods.add(method);
+ }
+ return methods;
+ }
+
+ protected RestMethodMetadata resolveMethodRestMetadata(MethodMetadata methodMetadata, Class> targetType,
+ List feignContractMethods) {
+ String configKey = methodMetadata.configKey();
+ Method feignContractMethod = getMatchedFeignContractMethod(targetType, feignContractMethods, configKey);
+
+ RestMethodMetadata metadata = new RestMethodMetadata();
+
+ metadata.setRequest(new RequestMetadata(methodMetadata.template()));
+ metadata.setMethod(new org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata(feignContractMethod));
+ metadata.setIndexToName(methodMetadata.indexToName());
+
+ return metadata;
+ }
+
+ private Method getMatchedFeignContractMethod(Class> targetType, List methods, String expectedConfigKey) {
+ Method matchedMethod = null;
+ for (Method method : methods) {
+ String configKey = Feign.configKey(targetType, method);
+ if (expectedConfigKey.equals(configKey)) {
+ matchedMethod = method;
+ break;
+ }
+ }
+ return matchedMethod;
+ }
+
+ @Override
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/MetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/MetadataResolver.java
new file mode 100644
index 00000000..4956e387
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/MetadataResolver.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.springframework.cloud.alibaba.dubbo.metadata.resolver;
+
+import com.alibaba.dubbo.config.spring.ServiceBean;
+import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
+import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
+
+import java.util.Set;
+
+/**
+ * The REST metadata resolver
+ *
+ * @author Mercy
+ */
+public interface MetadataResolver {
+
+ /**
+ * Resolve the {@link ServiceRestMetadata} {@link Set set} from {@link ServiceBean}
+ *
+ * @param serviceBean {@link ServiceBean}
+ * @return non-null {@link Set}
+ */
+ Set resolveServiceRestMetadata(ServiceBean serviceBean);
+
+ /**
+ * Resolve {@link RestMethodMetadata} {@link Set set} from {@link Class target type}
+ *
+ * @param targetType {@link Class target type}
+ * @return non-null {@link Set}
+ */
+ Set resolveMethodRestMetadata(Class> targetType);
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/service/MetadataConfigService.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/service/MetadataConfigService.java
new file mode 100644
index 00000000..28b7a373
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/service/MetadataConfigService.java
@@ -0,0 +1,33 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.metadata.service;
+
+import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
+
+import java.util.Set;
+
+/**
+ * Config Service for Metadata
+ *
+ * @author Mercy
+ */
+public interface MetadataConfigService {
+
+ void publishServiceRestMetadata(String serviceName, Set serviceRestMetadata);
+
+ Set getServiceRestMetadata(String serviceName);
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/service/NacosMetadataConfigService.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/service/NacosMetadataConfigService.java
new file mode 100644
index 00000000..93ce9281
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/service/NacosMetadataConfigService.java
@@ -0,0 +1,97 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.metadata.service;
+
+import com.alibaba.nacos.api.config.ConfigService;
+import com.alibaba.nacos.api.exception.NacosException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
+import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
+
+import javax.annotation.PostConstruct;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
+
+/**
+ * Nacos {@link MetadataConfigService}
+ *
+ * @author Mercy
+ */
+public class NacosMetadataConfigService implements MetadataConfigService {
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ @Autowired
+ private NacosConfigProperties nacosConfigProperties;
+
+ private ConfigService configService;
+
+ @PostConstruct
+ public void init() {
+ this.configService = nacosConfigProperties.configServiceInstance();
+ this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
+ }
+
+ /**
+ * Get the data Id of service rest metadata
+ */
+ private static String getServiceRestMetadataDataId(String serviceName) {
+ return "metadata:rest:" + serviceName + ".json";
+ }
+
+ @Override
+ public void publishServiceRestMetadata(String serviceName, Set serviceRestMetadata) {
+ String dataId = getServiceRestMetadataDataId(serviceName);
+ String json = writeValueAsString(serviceRestMetadata);
+ try {
+ configService.publishConfig(dataId, DEFAULT_GROUP, json);
+ } catch (NacosException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Set getServiceRestMetadata(String serviceName) {
+ Set metadata = Collections.emptySet();
+ String dataId = getServiceRestMetadataDataId(serviceName);
+ try {
+ String json = configService.getConfig(dataId, DEFAULT_GROUP, 1000 * 3);
+ metadata = objectMapper.readValue(json,
+ TypeFactory.defaultInstance().constructCollectionType(LinkedHashSet.class, ServiceRestMetadata.class));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return metadata;
+ }
+
+ private String writeValueAsString(Object object) {
+ String content = null;
+ try {
+ content = objectMapper.writeValueAsString(object);
+ } catch (JsonProcessingException e) {
+ throw new IllegalArgumentException(e);
+ }
+ return content;
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboFeignClientsConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboFeignClientsConfiguration.java
new file mode 100644
index 00000000..0c4d7400
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboFeignClientsConfiguration.java
@@ -0,0 +1,65 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.openfeign;
+
+import feign.Contract;
+import feign.Feign;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration;
+import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.cloud.openfeign.FeignClientsConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * Dubbo {@link Configuration} for {@link FeignClient FeignClients}
+ *
+ * @author Mercy
+ * @see DubboOpenFeignAutoConfiguration
+ * @see org.springframework.cloud.openfeign.FeignContext#setConfigurations(List)
+ * @see FeignClientsConfiguration
+ */
+@Configuration
+public class DubboFeignClientsConfiguration {
+
+ @Autowired
+ private Contract contract;
+
+ @Autowired
+ private DubboServiceMetadataRepository dubboServiceRepository;
+
+ @Bean
+ public BeanPostProcessor beanPostProcessor() {
+ return new BeanPostProcessor() {
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if (bean instanceof Feign.Builder) {
+ Feign.Builder builder = (Feign.Builder) bean;
+ builder.invocationHandlerFactory(new DubboInvocationHandlerFactory(contract, dubboServiceRepository));
+ }
+ return bean;
+ }
+ };
+ }
+
+
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandler.java
new file mode 100644
index 00000000..622ae340
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandler.java
@@ -0,0 +1,69 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.openfeign;
+
+import com.alibaba.dubbo.rpc.service.GenericService;
+import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
+import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * Dubbo {@link GenericService} for {@link InvocationHandler}
+ *
+ * @author Mercy
+ */
+public class DubboInvocationHandler implements InvocationHandler {
+
+ private final Map genericServicesMap;
+
+ private final Map methodMetadata;
+
+ private final InvocationHandler defaultInvocationHandler;
+
+ public DubboInvocationHandler(Map genericServicesMap,
+ Map methodMetadata,
+ InvocationHandler defaultInvocationHandler) {
+ this.genericServicesMap = genericServicesMap;
+ this.methodMetadata = methodMetadata;
+ this.defaultInvocationHandler = defaultInvocationHandler;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+
+ GenericService genericService = genericServicesMap.get(method);
+
+ MethodMetadata methodMetadata = this.methodMetadata.get(method);
+
+ if (genericService == null || methodMetadata == null) {
+ return defaultInvocationHandler.invoke(proxy, method, args);
+ }
+
+ String methodName = methodMetadata.getName();
+
+ String[] parameterTypes = methodMetadata
+ .getParams()
+ .stream()
+ .map(MethodParameterMetadata::getType)
+ .toArray(String[]::new);
+
+ return genericService.$invoke(methodName, parameterTypes, args);
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandlerFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandlerFactory.java
new file mode 100644
index 00000000..4fd67f40
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandlerFactory.java
@@ -0,0 +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.springframework.cloud.alibaba.dubbo.openfeign;
+
+import com.alibaba.dubbo.rpc.service.GenericService;
+import feign.Contract;
+import feign.InvocationHandlerFactory;
+import feign.MethodMetadata;
+import feign.Target;
+import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
+import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static feign.Feign.configKey;
+
+/**
+ * Dubbo {@link InvocationHandlerFactory}
+ *
+ * @author Mercy
+ */
+public class DubboInvocationHandlerFactory implements InvocationHandlerFactory {
+
+ private final static InvocationHandlerFactory DEFAULT_INVOCATION_HANDLER_FACTORY =
+ new InvocationHandlerFactory.Default();
+
+ private final Contract contract;
+
+ private final DubboServiceMetadataRepository dubboServiceRepository;
+
+ public DubboInvocationHandlerFactory(Contract contract, DubboServiceMetadataRepository dubboServiceRepository) {
+ this.contract = contract;
+ this.dubboServiceRepository = dubboServiceRepository;
+ }
+
+ @Override
+ public InvocationHandler create(Target target, Map dispatch) {
+ // The target class annotated @FeignClient
+ Class> targetType = target.type();
+ // Resolve metadata from current @FeignClient type
+ Map methodRequestMetadataMap = resolveMethodRequestMetadataMap(targetType, dispatch.keySet());
+ // @FeignClient specifies the service name
+ String serviceName = target.name();
+ // Update specified metadata
+ dubboServiceRepository.updateMetadata(serviceName);
+
+ Map genericServicesMap = new HashMap<>();
+
+ Map methodMetadataMap = new HashMap<>();
+
+ methodRequestMetadataMap.forEach((method, requestMetadata) -> {
+ GenericService genericService = dubboServiceRepository.getGenericService(serviceName, requestMetadata);
+ org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata methodMetadata =
+ dubboServiceRepository.getMethodMetadata(serviceName, requestMetadata);
+ genericServicesMap.put(method, genericService);
+ methodMetadataMap.put(method, methodMetadata);
+ });
+
+ InvocationHandler defaultInvocationHandler = DEFAULT_INVOCATION_HANDLER_FACTORY.create(target, dispatch);
+
+ DubboInvocationHandler invocationHandler = new DubboInvocationHandler(genericServicesMap, methodMetadataMap,
+ defaultInvocationHandler);
+
+ return invocationHandler;
+ }
+
+ private Map resolveMethodRequestMetadataMap(Class> targetType, Set methods) {
+ Map requestMetadataMap = resolveRequestMetadataMap(targetType);
+ return methods.stream().collect(Collectors.toMap(method -> method, method ->
+ requestMetadataMap.get(configKey(targetType, method))
+ ));
+ }
+
+ private Map resolveRequestMetadataMap(Class> targetType) {
+ return contract.parseAndValidatateMetadata(targetType)
+ .stream().collect(Collectors.toMap(MethodMetadata::configKey, this::requestMetadata));
+ }
+
+ private RequestMetadata requestMetadata(MethodMetadata methodMetadata) {
+ return new RequestMetadata(methodMetadata.template());
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DubboRegistration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DubboRegistration.java
new file mode 100644
index 00000000..9a435ff2
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DubboRegistration.java
@@ -0,0 +1,72 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.registry;
+
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.serviceregistry.Registration;
+
+import java.net.URI;
+import java.util.Map;
+
+/**
+ * The {@link Registration} of Dubbo uses an external of {@link ServiceInstance} instance as the delegate.
+ *
+ * @author Mercy
+ */
+class DubboRegistration implements Registration {
+
+ private final ServiceInstance delegate;
+
+ public DubboRegistration(ServiceInstance delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public String getServiceId() {
+ return delegate.getServiceId();
+ }
+
+ @Override
+ public String getHost() {
+ return delegate.getHost();
+ }
+
+ @Override
+ public int getPort() {
+ return delegate.getPort();
+ }
+
+ @Override
+ public boolean isSecure() {
+ return delegate.isSecure();
+ }
+
+ @Override
+ public URI getUri() {
+ return delegate.getUri();
+ }
+
+ @Override
+ public Map getMetadata() {
+ return delegate.getMetadata();
+ }
+
+ @Override
+ public String getScheme() {
+ return delegate.getScheme();
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java
new file mode 100644
index 00000000..1904cbd5
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java
@@ -0,0 +1,443 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.registry;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.common.utils.UrlUtils;
+import com.alibaba.dubbo.registry.NotifyListener;
+import com.alibaba.dubbo.registry.RegistryFactory;
+import com.alibaba.dubbo.registry.support.FailbackRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.client.DefaultServiceInstance;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.cloud.client.serviceregistry.Registration;
+import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static com.alibaba.dubbo.common.Constants.CONFIGURATORS_CATEGORY;
+import static com.alibaba.dubbo.common.Constants.CONSUMERS_CATEGORY;
+import static com.alibaba.dubbo.common.Constants.PROTOCOL_KEY;
+import static com.alibaba.dubbo.common.Constants.PROVIDERS_CATEGORY;
+import static com.alibaba.dubbo.common.Constants.ROUTERS_CATEGORY;
+
+/**
+ * Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
+ *
+ * @author Mercy
+ */
+public class SpringCloudRegistry extends FailbackRegistry {
+
+ /**
+ * All supported categories
+ */
+ private static final String[] ALL_SUPPORTED_CATEGORIES = of(
+ PROVIDERS_CATEGORY,
+ CONSUMERS_CATEGORY,
+ ROUTERS_CATEGORY,
+ CONFIGURATORS_CATEGORY
+ );
+
+ private static final int CATEGORY_INDEX = 0;
+
+ private static final int PROTOCOL_INDEX = CATEGORY_INDEX + 1;
+
+ private static final int SERVICE_INTERFACE_INDEX = PROTOCOL_INDEX + 1;
+
+ private static final int SERVICE_VERSION_INDEX = SERVICE_INTERFACE_INDEX + 1;
+
+ private static final int SERVICE_GROUP_INDEX = SERVICE_VERSION_INDEX + 1;
+
+ private static final String WILDCARD = "*";
+
+ /**
+ * The separator for service name
+ */
+ private static final String SERVICE_NAME_SEPARATOR = ":";
+
+ private final ServiceRegistry serviceRegistry;
+
+ private final DiscoveryClient discoveryClient;
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ /**
+ * {@link ScheduledExecutorService} lookup service names(only for Dubbo-OPS)
+ */
+ private volatile ScheduledExecutorService scheduledExecutorService;
+
+ /**
+ * The interval in second of lookup service names(only for Dubbo-OPS)
+ */
+ private static final long LOOKUP_INTERVAL = Long.getLong("dubbo.service.names.lookup.interval", 30);
+
+ public SpringCloudRegistry(URL url, ServiceRegistry serviceRegistry,
+ DiscoveryClient discoveryClient) {
+ super(url);
+ this.serviceRegistry = serviceRegistry;
+ this.discoveryClient = discoveryClient;
+ }
+
+ @Override
+ protected void doRegister(URL url) {
+ final String serviceName = getServiceName(url);
+ final Registration registration = createRegistration(serviceName, url);
+ serviceRegistry.register(registration);
+ }
+
+ @Override
+ protected void doUnregister(URL url) {
+ final String serviceName = getServiceName(url);
+ final Registration registration = createRegistration(serviceName, url);
+ this.serviceRegistry.deregister(registration);
+ }
+
+ @Override
+ protected void doSubscribe(URL url, NotifyListener listener) {
+ List serviceNames = getServiceNames(url, listener);
+ doSubscribe(url, listener, serviceNames);
+ }
+
+ @Override
+ protected void doUnsubscribe(URL url, NotifyListener listener) {
+ if (isAdminProtocol(url)) {
+ shutdownServiceNamesLookup();
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return false;
+ }
+
+ private void shutdownServiceNamesLookup() {
+ if (scheduledExecutorService != null) {
+ scheduledExecutorService.shutdown();
+ }
+ }
+
+ private Registration createRegistration(String serviceName, URL url) {
+ return new DubboRegistration(createServiceInstance(serviceName, url));
+ }
+
+ private ServiceInstance createServiceInstance(String serviceName, URL url) {
+ // Append default category if absent
+ String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
+ URL newURL = url.addParameter(Constants.CATEGORY_KEY, category);
+ newURL = newURL.addParameter(Constants.PROTOCOL_KEY, url.getProtocol());
+ String ip = NetUtils.getLocalHost();
+ int port = newURL.getParameter(Constants.BIND_PORT_KEY, url.getPort());
+ DefaultServiceInstance serviceInstance = new DefaultServiceInstance(serviceName, ip, port, false);
+ serviceInstance.getMetadata().putAll(new LinkedHashMap<>(newURL.getParameters()));
+ return serviceInstance;
+ }
+
+ public static String getServiceName(URL url) {
+ String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
+ return getServiceName(url, category);
+ }
+
+ private static String getServiceName(URL url, String category) {
+ StringBuilder serviceNameBuilder = new StringBuilder(category);
+ appendIfPresent(serviceNameBuilder, url.getParameter(PROTOCOL_KEY, url.getProtocol()));
+ appendIfPresent(serviceNameBuilder, url, Constants.INTERFACE_KEY);
+ appendIfPresent(serviceNameBuilder, url, Constants.VERSION_KEY);
+ appendIfPresent(serviceNameBuilder, url, Constants.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.hasText(parameterValue)) {
+ target.append(SERVICE_NAME_SEPARATOR).append(parameterValue);
+ }
+ }
+
+ private void filterServiceNames(List serviceNames, URL url) {
+
+ final String[] categories = getCategories(url);
+
+ final String targetServiceInterface = url.getServiceInterface();
+
+ final String targetVersion = url.getParameter(Constants.VERSION_KEY);
+
+ final String targetGroup = url.getParameter(Constants.GROUP_KEY);
+
+ filter(serviceNames, new Filter() {
+ @Override
+ public boolean accept(String serviceName) {
+ // split service name to segments
+ // (required) segments[0] = category
+ // (required) segments[1] = serviceInterface
+ // (required) segments[2] = protocol
+ // (required) segments[3] = version
+ // (optional) segments[4] = group
+ String[] segments = getServiceSegments(serviceName);
+ int length = segments.length;
+ if (length < 4) { // must present 4 segments or more
+ return false;
+ }
+
+ String category = getCategory(segments);
+ if (Arrays.binarySearch(categories, category) > -1) { // no match category
+ return false;
+ }
+
+ String protocol = getProtocol(segments);
+ if (StringUtils.hasText(protocol)) {
+ return false;
+ }
+
+ String serviceInterface = getServiceInterface(segments);
+ if (!WILDCARD.equals(targetServiceInterface) &&
+ !Objects.equals(targetServiceInterface, serviceInterface)) { // no match service interface
+ return false;
+ }
+
+ String version = getServiceVersion(segments);
+ if (!WILDCARD.equals(targetVersion) &&
+ !Objects.equals(targetVersion, version)) { // no match service version
+ return false;
+ }
+
+ String group = getServiceGroup(segments);
+ if (group != null && !WILDCARD.equals(targetGroup)
+ && !Objects.equals(targetGroup, group)) { // no match service group
+ return false;
+ }
+
+ return true;
+ }
+ });
+ }
+
+ public static String[] getServiceSegments(String serviceName) {
+ return StringUtils.delimitedListToStringArray(serviceName, SERVICE_NAME_SEPARATOR);
+ }
+
+ public static String getCategory(String[] segments) {
+ return segments[CATEGORY_INDEX];
+ }
+
+ public static String getProtocol(String[] segments) {
+ return segments[PROTOCOL_INDEX];
+ }
+
+ public static String getServiceInterface(String[] segments) {
+ return segments[SERVICE_INTERFACE_INDEX];
+ }
+
+ public static String getServiceVersion(String[] segments) {
+ return segments[SERVICE_VERSION_INDEX];
+ }
+
+ public static String getServiceGroup(String[] segments) {
+ return segments.length > 4 ? segments[SERVICE_GROUP_INDEX] : null;
+ }
+
+ /**
+ * Get the categories from {@link URL}
+ *
+ * @param url {@link URL}
+ * @return non-null array
+ */
+ private String[] getCategories(URL url) {
+ return Constants.ANY_VALUE.equals(url.getServiceInterface()) ?
+ ALL_SUPPORTED_CATEGORIES : of(Constants.DEFAULT_CATEGORY);
+ }
+
+ private List getAllServiceNames() {
+ return discoveryClient.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)) {
+ scheduleServiceNamesLookup(url, listener);
+ return getServiceNamesForOps(url);
+ } else {
+ return doGetServiceNames(url);
+ }
+ }
+
+
+ private boolean isAdminProtocol(URL url) {
+ return Constants.ADMIN_PROTOCOL.equals(url.getProtocol());
+ }
+
+ private void scheduleServiceNamesLookup(final URL url, final NotifyListener listener) {
+ if (scheduledExecutorService == null) {
+ scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+ scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ List serviceNames = getAllServiceNames();
+ filter(serviceNames, new Filter() {
+ @Override
+ public boolean accept(String serviceName) {
+ boolean accepted = false;
+ for (String category : ALL_SUPPORTED_CATEGORIES) {
+ String prefix = category + SERVICE_NAME_SEPARATOR;
+ if (StringUtils.startsWithIgnoreCase(serviceName, prefix)) {
+ accepted = true;
+ break;
+ }
+ }
+ return accepted;
+ }
+ });
+ doSubscribe(url, listener, serviceNames);
+ }
+ }, LOOKUP_INTERVAL, LOOKUP_INTERVAL, TimeUnit.SECONDS);
+ }
+ }
+
+ private void doSubscribe(final URL url, final NotifyListener listener, final List serviceNames) {
+ for (String serviceName : serviceNames) {
+ List serviceInstances = discoveryClient.getInstances(serviceName);
+ notifySubscriber(url, listener, serviceInstances);
+ // TODO Support Update notification event
+ }
+ }
+
+ private List doGetServiceNames(URL url) {
+ String[] categories = getCategories(url);
+ List serviceNames = new ArrayList(categories.length);
+ for (String category : categories) {
+ final String serviceName = getServiceName(url, category);
+ serviceNames.add(serviceName);
+ }
+ return serviceNames;
+ }
+
+ /**
+ * Notify the Healthy {@link ServiceInstance service instance} to subscriber.
+ *
+ * @param url {@link URL}
+ * @param listener {@link NotifyListener}
+ * @param serviceInstances all {@link ServiceInstance instances}
+ */
+ private void notifySubscriber(URL url, NotifyListener listener, List serviceInstances) {
+ List healthyInstances = new LinkedList(serviceInstances);
+ // Healthy Instances
+ filterHealthyInstances(healthyInstances);
+ List urls = buildURLs(url, healthyInstances);
+ this.notify(url, listener, urls);
+ }
+
+ private void filterHealthyInstances(Collection instances) {
+ filter(instances, new Filter() {
+ @Override
+ public boolean accept(ServiceInstance data) {
+ // TODO check the details of status
+// return serviceRegistry.getStatus(new DubboRegistration(data)) != null;
+ return true;
+ }
+ });
+ }
+
+ private List buildURLs(URL consumerURL, Collection serviceInstances) {
+ if (serviceInstances.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List urls = new LinkedList();
+ for (ServiceInstance serviceInstance : serviceInstances) {
+ URL url = buildURL(serviceInstance);
+ if (UrlUtils.isMatch(consumerURL, url)) {
+ urls.add(url);
+ }
+ }
+ return urls;
+ }
+
+ private URL buildURL(ServiceInstance serviceInstance) {
+ URL url = new URL(serviceInstance.getMetadata().get(Constants.PROTOCOL_KEY),
+ serviceInstance.getHost(),
+ serviceInstance.getPort(),
+ serviceInstance.getMetadata());
+ return 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, url);
+ return serviceNames;
+ }
+
+ private void filter(Collection collection, Filter filter) {
+ Iterator iterator = collection.iterator();
+ while (iterator.hasNext()) {
+ T data = iterator.next();
+ if (!filter.accept(data)) { // remove if not accept
+ iterator.remove();
+ }
+ }
+ }
+
+ private static T[] of(T... values) {
+ return values;
+ }
+
+ /**
+ * A filter
+ */
+ private interface Filter {
+
+ /**
+ * Tests whether or not the specified data should be accepted.
+ *
+ * @param data The data to be tested
+ * @return true
if and only if data
+ * should be accepted
+ */
+ boolean accept(T data);
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java
new file mode 100644
index 00000000..7402ff97
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java
@@ -0,0 +1,49 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.registry;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.registry.Registry;
+import com.alibaba.dubbo.registry.RegistryFactory;
+
+import org.springframework.cloud.client.discovery.DiscoveryClient;
+import org.springframework.cloud.client.serviceregistry.Registration;
+import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
+ *
+ * @author Mercy
+ * @see RegistryFactory
+ * @see SpringCloudRegistry
+ */
+public class SpringCloudRegistryFactory implements RegistryFactory {
+
+ private static ApplicationContext applicationContext;
+
+ @Override
+ public Registry getRegistry(URL url) {
+ ServiceRegistry serviceRegistry = applicationContext.getBean(ServiceRegistry.class);
+ DiscoveryClient discoveryClient = applicationContext.getBean(DiscoveryClient.class);
+ return new SpringCloudRegistry(url, serviceRegistry, discoveryClient);
+ }
+
+ public static void setApplicationContext(ApplicationContext applicationContext) {
+ SpringCloudRegistryFactory.applicationContext = applicationContext;
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
new file mode 100644
index 00000000..77ac945a
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+spring-cloud=org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistryFactory
\ No newline at end of file
diff --git a/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..c2627f86
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,7 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration,\
+ org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration,\
+ org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboRestMetadataRegistrationAutoConfiguration
+
+org.springframework.context.ApplicationContextInitializer=\
+ org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer
diff --git a/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationAutoConfigurationTest.java b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationAutoConfigurationTest.java
new file mode 100644
index 00000000..ce32e641
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationAutoConfigurationTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.autoconfigure;
+
+import org.springframework.boot.test.context.SpringBootTest;
+
+/**
+ * {@link DubboServiceRegistrationAutoConfiguration} Test
+ *
+ * @author Mercy
+ */
+@SpringBootTest
+public class DubboServiceRegistrationAutoConfigurationTest {
+}
diff --git a/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudBootstrap.java b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudBootstrap.java
new file mode 100644
index 00000000..d7b3d13d
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudBootstrap.java
@@ -0,0 +1,79 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.bootstrap;
+
+import com.alibaba.dubbo.config.annotation.Reference;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.cloud.alibaba.dubbo.service.EchoService;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Dubbo Spring Cloud Bootstrap
+ */
+@EnableDiscoveryClient
+@EnableAutoConfiguration
+@EnableFeignClients
+@RestController
+public class DubboSpringCloudBootstrap {
+
+ @Reference(version = "1.0.0")
+ private EchoService echoService;
+
+ @Autowired
+ @Lazy
+ private FeignEchoService feignEchoService;
+
+ @GetMapping(value = "/call/echo")
+ public String echo(@RequestParam("message") String message) {
+ return feignEchoService.echo(message);
+ }
+
+ @FeignClient("spring-cloud-alibaba-dubbo")
+ public interface FeignEchoService {
+
+ @GetMapping(value = "/echo")
+ String echo(@RequestParam("message") String message);
+ }
+
+ @Bean
+ public ApplicationRunner applicationRunner() {
+ return arguments -> {
+ // Dubbo Service call
+ System.out.println(echoService.echo("mercyblitz"));
+ // Spring Cloud Open Feign REST Call
+ System.out.println(feignEchoService.echo("mercyblitz"));
+ };
+ }
+
+ public static void main(String[] args) {
+ new SpringApplicationBuilder(DubboSpringCloudBootstrap.class)
+ .run(args);
+ }
+}
+
+
+
diff --git a/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/service/DefaultEchoService.java b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/service/DefaultEchoService.java
new file mode 100644
index 00000000..e1ad9a9d
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/service/DefaultEchoService.java
@@ -0,0 +1,65 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.service;
+
+import com.alibaba.dubbo.config.annotation.Service;
+import com.alibaba.dubbo.rpc.RpcContext;
+
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+
+/**
+ * Default {@link EchoService}
+ *
+ * @author Mercy
+ */
+@Service(version = "1.0.0", protocol = {"dubbo", "rest"})
+@RestController
+@Path("/")
+public class DefaultEchoService implements EchoService {
+
+ @Override
+ @GetMapping(value = "/echo"
+// consumes = MediaType.APPLICATION_JSON_VALUE,
+// produces = MediaType.APPLICATION_JSON_UTF8_VALUE
+ )
+ @Path("/echo")
+ @GET
+// @Consumes("application/json")
+// @Produces("application/json;charset=UTF-8")
+ public String echo(@RequestParam @QueryParam("message") String message) {
+ return RpcContext.getContext().getUrl() + " [echo] : " + message;
+ }
+
+ @Override
+ @PostMapping("/plus")
+ @Path("/plus")
+ @POST
+ public String plus(@RequestParam @QueryParam("a") int a, @RequestParam @QueryParam("b") int b) {
+ return null;
+ }
+}
diff --git a/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/service/EchoService.java b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/service/EchoService.java
new file mode 100644
index 00000000..10ace8ec
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/test/java/org/springframework/cloud/alibaba/dubbo/service/EchoService.java
@@ -0,0 +1,30 @@
+/*
+ * 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.springframework.cloud.alibaba.dubbo.service;
+
+/**
+ * Echo Service
+ *
+ * @author Mercy
+ */
+public interface EchoService {
+
+ String echo(String message);
+
+ String plus(int a, int b);
+
+}
diff --git a/spring-cloud-alibaba-dubbo/src/test/resources/application.yaml b/spring-cloud-alibaba-dubbo/src/test/resources/application.yaml
new file mode 100644
index 00000000..afdfbfa7
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/test/resources/application.yaml
@@ -0,0 +1,16 @@
+dubbo:
+ scan:
+ base-packages: org.springframework.cloud.alibaba.dubbo.service
+ protocols:
+ dubbo:
+ name: dubbo
+ port: 12345
+ rest:
+ name: rest
+ port: 9090
+ server: netty
+ registry:
+ address: spring-cloud://nacos
+
+server:
+ port: 8080
\ No newline at end of file
diff --git a/spring-cloud-alibaba-dubbo/src/test/resources/bootstrap.yaml b/spring-cloud-alibaba-dubbo/src/test/resources/bootstrap.yaml
new file mode 100644
index 00000000..c3799608
--- /dev/null
+++ b/spring-cloud-alibaba-dubbo/src/test/resources/bootstrap.yaml
@@ -0,0 +1,28 @@
+spring:
+ application:
+ name: spring-cloud-alibaba-dubbo
+ cloud:
+ nacos:
+ discovery:
+ server-addr: 127.0.0.1:8848
+ config:
+ server-addr: 127.0.0.1:8848
+
+eureka:
+ client:
+ enabled: false
+
+---
+spring:
+ profiles: eureka
+ cloud:
+ nacos:
+ discovery:
+ enabled: false
+ register-enabled: false
+
+eureka:
+ client:
+ enabled: true
+ service-url:
+ defaultZone: http://127.0.0.1:8761/eureka/
\ No newline at end of file
diff --git a/spring-cloud-alibaba-examples/env-extension/src/main/java/org/springframework/alicloud/env/extension/ImportExtraConfig.java b/spring-cloud-alibaba-examples/env-extension/src/main/java/org/springframework/alicloud/env/extension/ImportExtraConfig.java
index cfea09fa..1585c4a9 100644
--- a/spring-cloud-alibaba-examples/env-extension/src/main/java/org/springframework/alicloud/env/extension/ImportExtraConfig.java
+++ b/spring-cloud-alibaba-examples/env-extension/src/main/java/org/springframework/alicloud/env/extension/ImportExtraConfig.java
@@ -1,7 +1,11 @@
package org.springframework.alicloud.env.extension;
-import java.lang.annotation.*;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml
index 868bc10c..f3bd3370 100644
--- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml
+++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml
@@ -30,7 +30,11 @@
com.alibaba.boot
dubbo-spring-boot-starter
- 0.2.0
+
+
+
+ com.alibaba
+ dubbo