diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index 8c40df1b80c..76bdd1c70eb 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -194,6 +194,7 @@
1.5.0
1.23.0
3.3.0-beta.1-SNAPSHOT
+ 3.1.5
@@ -949,6 +950,11 @@
rxjava
${rxjava.version}
+
+ org.springframework.cloud
+ spring-cloud-openfeign-core
+ ${open_feign_version}
+
diff --git a/dubbo-metadata/dubbo-metadata-rest/pom.xml b/dubbo-metadata/dubbo-metadata-rest/pom.xml
index 65893ee29e2..3179070537a 100644
--- a/dubbo-metadata/dubbo-metadata-rest/pom.xml
+++ b/dubbo-metadata/dubbo-metadata-rest/pom.xml
@@ -58,6 +58,12 @@
spring-context
test
+
+
+ org.springframework.cloud
+ spring-cloud-openfeign-core
+ test
+
diff --git a/dubbo-metadata/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java b/dubbo-metadata/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java
index b96234d764b..e43b5b557d8 100644
--- a/dubbo-metadata/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java
+++ b/dubbo-metadata/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/rest/RestMetadataConstants.java
@@ -109,6 +109,11 @@ interface SPRING_MVC {
*/
String CONTROLLER_ANNOTATION_CLASS_NAME = "org.springframework.stereotype.Controller";
+ /**
+ * The annotation class name of @FeignClient
+ */
+ String FEIGN_CLIENT_CLASS_NAME = "org.springframework.cloud.openfeign.FeignClient";
+
/**
* The annotation class name of @RequestMapping
*/
@@ -161,5 +166,8 @@ interface SPRING_MVC {
* @since 2.7.9
*/
Class> ANNOTATED_ELEMENT_UTILS_CLASS = resolveClass(ANNOTATED_ELEMENT_UTILS_CLASS_NAME, getClassLoader());
+
+ Class extends Annotation> FEIGN_CLIENT_CLASS = (Class extends Annotation>) resolveClass(FEIGN_CLIENT_CLASS_NAME, getClassLoader());
+
}
}
diff --git a/dubbo-metadata/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java b/dubbo-metadata/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java
index 765b2308554..da8035915cd 100644
--- a/dubbo-metadata/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java
+++ b/dubbo-metadata/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/rest/springmvc/SpringMvcServiceRestMetadataResolver.java
@@ -39,6 +39,7 @@
import static org.apache.dubbo.common.utils.PathUtils.buildPath;
import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.ANNOTATED_ELEMENT_UTILS_CLASS;
import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.CONTROLLER_ANNOTATION_CLASS;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.FEIGN_CLIENT_CLASS;
import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_MAPPING_ANNOTATION_CLASS;
/**
@@ -58,7 +59,9 @@ public SpringMvcServiceRestMetadataResolver(ApplicationModel applicationModel) {
@Override
protected boolean supports0(Class> serviceType) {
// class @Controller or @RequestMapping
- return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS) || isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS);
+ return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS)
+ || isAnnotationPresent(serviceType, FEIGN_CLIENT_CLASS)
+ || isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS);
}
@Override
@@ -84,9 +87,10 @@ protected String resolveRequestMethod(Method serviceMethod, Class> serviceType
@Override
protected String resolveRequestPath(Method serviceMethod, Class> serviceType, Class> serviceInterfaceClass) {
- String requestBasePath = resolveRequestPath(serviceType);
+ String feignClientBasePath = resolveFeignClientBaseRequestPath(serviceType);
+ String requestMappingBasePath = resolveRequestPath(serviceType);
String requestRelativePath = resolveRequestPath(serviceMethod);
- return buildPath(requestBasePath, requestRelativePath);
+ return buildPath(feignClientBasePath, buildPath(requestMappingBasePath, requestRelativePath));
}
@Override
@@ -103,6 +107,18 @@ protected void processConsumes(Method serviceMethod, Class> serviceType, Class
addMediaTypes(serviceInterfaceClass, "consumes", consumes);
}
+ private String resolveFeignClientBaseRequestPath(AnnotatedElement annotatedElement) {
+ Annotation feignClient = findAnnotation(annotatedElement, FEIGN_CLIENT_CLASS);
+
+ String path = getAttribute(feignClient, "path");
+
+ if (path == null) {
+ return "";
+ }
+
+ return path;
+ }
+
private String resolveRequestPath(AnnotatedElement annotatedElement) {
Annotation mappingAnnotation = getRequestMapping(annotatedElement);
diff --git a/dubbo-metadata/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/rest/feign/FeignClientController.java b/dubbo-metadata/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/rest/feign/FeignClientController.java
new file mode 100644
index 00000000000..fc1fe0fdbd7
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/rest/feign/FeignClientController.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.apache.dubbo.metadata.rest.feign;
+
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+@FeignClient(path = "/feign")
+@RequestMapping("/context")
+public interface FeignClientController {
+
+ @RequestMapping(value = "/hello", method = RequestMethod.GET)
+ String hello();
+}
diff --git a/dubbo-metadata/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/rest/feign/FeignClientControllerImpl.java b/dubbo-metadata/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/rest/feign/FeignClientControllerImpl.java
new file mode 100644
index 00000000000..43333e15476
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/rest/feign/FeignClientControllerImpl.java
@@ -0,0 +1,27 @@
+/*
+ * 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.metadata.rest.feign;
+
+
+
+public class FeignClientControllerImpl implements FeignClientController{
+
+ @Override
+ public String hello() {
+ return "hello, feign";
+ }
+}
diff --git a/dubbo-metadata/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/rest/springmvc/FeignClientAnnotationTest.java b/dubbo-metadata/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/rest/springmvc/FeignClientAnnotationTest.java
new file mode 100644
index 00000000000..8a819db801e
--- /dev/null
+++ b/dubbo-metadata/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/rest/springmvc/FeignClientAnnotationTest.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.metadata.rest.springmvc;
+
+import org.apache.dubbo.metadata.rest.PathMatcher;
+import org.apache.dubbo.metadata.rest.RestMethodMetadata;
+import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
+import org.apache.dubbo.metadata.rest.feign.FeignClientController;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+public class FeignClientAnnotationTest {
+
+ private SpringMvcServiceRestMetadataResolver instance = new SpringMvcServiceRestMetadataResolver(ApplicationModel.defaultModel());
+
+ @Test
+ void testFeignClientAnnotationResolve() {
+
+ Assertions.assertEquals(true, instance.supports(FeignClientController.class));
+ Class service = FeignClientController.class;
+ ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata();
+ serviceRestMetadata.setServiceInterface(service.getName());
+
+ ServiceRestMetadata resolve = instance.resolve(service, serviceRestMetadata);
+
+ Map unContainPathVariableToServiceMap = resolve.getPathUnContainPathVariableToServiceMap();
+ RestMethodMetadata restMethodMetadata = unContainPathVariableToServiceMap.get(PathMatcher.getInvokeCreatePathMatcher("/feign/context/hello", null, null, null, "GET"));
+ Assertions.assertNotNull(restMethodMetadata);
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-rest/pom.xml b/dubbo-rpc/dubbo-rpc-rest/pom.xml
index 44b3214b1f9..e9fdd48a5ba 100644
--- a/dubbo-rpc/dubbo-rpc-rest/pom.xml
+++ b/dubbo-rpc/dubbo-rpc-rest/pom.xml
@@ -149,5 +149,11 @@
spring-context
test
+
+
+ org.springframework.cloud
+ spring-cloud-openfeign-core
+ test
+
diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/FeignClientRestProtocolTest.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/FeignClientRestProtocolTest.java
new file mode 100644
index 00000000000..2b44a3e96f8
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/FeignClientRestProtocolTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.rpc.protocol.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.protocol.rest.mvc.feign.FeignClientController;
+import org.apache.dubbo.rpc.protocol.rest.mvc.feign.FeignClientControllerImpl;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+
+public class FeignClientRestProtocolTest {
+ private Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest");
+ private ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+ private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
+
+
+ @AfterEach
+ public void tearDown() {
+ protocol.destroy();
+ FrameworkModel.destroyAll();
+ }
+
+
+ @Test
+ void testRestProtocol() {
+ URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.mvc.feign.FeignClientController");
+
+ FeignClientController server = new FeignClientControllerImpl();
+
+ url = this.registerProvider(url, server, DemoService.class);
+
+ Exporter exporter = protocol.export(proxy.getInvoker(server, FeignClientController.class, url));
+
+ FeignClientController feignClientController = this.proxy.getProxy(protocol.refer(FeignClientController.class, url));
+
+ Assertions.assertEquals("hello, feign",feignClientController.hello());
+ exporter.unexport();
+ }
+
+
+ private URL registerProvider(URL url, Object impl, Class> interfaceClass) {
+ ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
+ ProviderModel providerModel = new ProviderModel(
+ url.getServiceKey(),
+ impl,
+ serviceDescriptor,
+ null,
+ null);
+ repository.registerProvider(providerModel);
+ return url.setServiceModel(providerModel);
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/feign/FeignClientController.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/feign/FeignClientController.java
new file mode 100644
index 00000000000..ff4baa1acbb
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/feign/FeignClientController.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.apache.dubbo.rpc.protocol.rest.mvc.feign;
+
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+@FeignClient(path = "/feign")
+@RequestMapping("/context")
+public interface FeignClientController {
+
+ @RequestMapping(value = "/hello", method = RequestMethod.GET)
+ String hello();
+}
diff --git a/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/feign/FeignClientControllerImpl.java b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/feign/FeignClientControllerImpl.java
new file mode 100644
index 00000000000..591694f6077
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/feign/FeignClientControllerImpl.java
@@ -0,0 +1,27 @@
+/*
+ * 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.rpc.protocol.rest.mvc.feign;
+
+
+
+public class FeignClientControllerImpl implements FeignClientController{
+
+ @Override
+ public String hello() {
+ return "hello, feign";
+ }
+}