You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by ti...@apache.org on 2023/05/05 02:24:24 UTC
[shenyu] branch master updated: [ISSUE #4560] spring-mvc(boot) client support collect api-meta (#4600)
This is an automated email from the ASF dual-hosted git repository.
tianpengfei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 1b961cd9b [ISSUE #4560] spring-mvc(boot) client support collect api-meta (#4600)
1b961cd9b is described below
commit 1b961cd9b432af84d49cacafcedb978a46a558e3
Author: 愿凌飞 <ti...@foxmail.com>
AuthorDate: Fri May 5 10:24:17 2023 +0800
[ISSUE #4560] spring-mvc(boot) client support collect api-meta (#4600)
* [ISSUE #4560] spring-mvc(boot) client support collect api-meta
* fix style
* move sub package
* add test
* fix something
* fix something
---------
Co-authored-by: tian-pengfei <ti...@apache.org>
Co-authored-by: xiaoyu <xi...@apache.org>
Co-authored-by: likeguo <33...@users.noreply.github.com>
Co-authored-by: dragon-zhang <zh...@apache.org>
---
.../auto/config/ClientRegisterConfiguration.java | 8 +-
...taBeanParser.java => PreApiMetaBeanParser.java} | 2 +-
.../register/SpringMvcApiBeansExtractor.java | 95 ++++++++++
.../apimeta/SpringMvcApiMetaBeanMatcher.java} | 16 +-
.../apimeta/SpringMvcApiMetaDefinitionMatcher.java | 41 +++++
.../apimeta/SpringMvcApiMetaDefinitionParser.java | 92 ++++++++++
.../apimeta/SpringMvcPreApiMetaBeanMatcher.java} | 12 +-
.../apimeta/SpringMvcPreApiMetaBeanParser.java | 65 +++++++
.../register/SpringMvcApiBeansExtractorTest.java | 77 ++++++++
.../apimeta/SpringMvcApiMetaBeanMatcherTest.java | 70 ++++++++
.../SpringMvcApiMetaDefinitionMatcherTest.java | 101 +++++++++++
.../SpringMvcApiMetaDefinitionParserTest.java | 91 ++++++++++
.../SpringMvcPreApiMetaBeanMatcherTest.java | 71 ++++++++
.../apimeta/SpringMvcPreApiMetaBeanParserTest.java | 62 +++++++
.../pom.xml | 5 +
.../ShenyuSpringMvcClientConfiguration.java | 3 +
...yuSpringMvcClientInfoRegisterConfiguration.java | 197 +++++++++++++++++++++
.../src/main/resources/META-INF/spring.factories | 3 +-
18 files changed, 999 insertions(+), 12 deletions(-)
diff --git a/shenyu-client/shenyu-client-autoconfig/src/main/java/org/apache/shenyu/client/auto/config/ClientRegisterConfiguration.java b/shenyu-client/shenyu-client-autoconfig/src/main/java/org/apache/shenyu/client/auto/config/ClientRegisterConfiguration.java
index 63ef7ae62..24597cfc2 100644
--- a/shenyu-client/shenyu-client-autoconfig/src/main/java/org/apache/shenyu/client/auto/config/ClientRegisterConfiguration.java
+++ b/shenyu-client/shenyu-client-autoconfig/src/main/java/org/apache/shenyu/client/auto/config/ClientRegisterConfiguration.java
@@ -22,7 +22,7 @@ import org.apache.shenyu.client.core.register.ApiBean;
import org.apache.shenyu.client.core.register.ClientApiRefreshedEventListener;
import org.apache.shenyu.client.core.register.extractor.ApiBeansExtractor;
import org.apache.shenyu.client.core.register.matcher.Matcher;
-import org.apache.shenyu.client.core.register.parser.ApiMetaBeanParser;
+import org.apache.shenyu.client.core.register.parser.PreApiMetaBeanParser;
import org.apache.shenyu.client.core.register.parser.ApiDocDefinitionParser;
import org.apache.shenyu.client.core.register.parser.ApiMetaDefinitionParser;
import org.apache.shenyu.client.core.register.registrar.AbstractRegistrar;
@@ -89,7 +89,7 @@ public class ClientRegisterConfiguration {
* Builds ApiMetaBeanPreRegistrar Bean.
*
* @param preApiMetaBeanMatcher apiMetaBeanPreMatcher
- * @param apiBeanMetaParser apiBeanMetaParser
+ * @param preApiBeanMetaParser preApiBeanMetaParser
* @param publisher publisher
* @param <T> ApiBean Type
* @return apiBeanPreRegistrar
@@ -97,9 +97,9 @@ public class ClientRegisterConfiguration {
@Bean(name = "PreApiMetaBeanRegistrar")
@ConditionalOnProperty(value = "shenyu.register.api.meta.enabled", matchIfMissing = true, havingValue = "true")
public <T> PreApiBeanRegistrar<T, DataTypeParent> buildApiMetaBeanPreRegistrar(final @Qualifier(PRE_API_META_BEAN_MATCHER) Matcher<ApiBean<T>> preApiMetaBeanMatcher,
- final ApiMetaBeanParser<T> apiBeanMetaParser,
+ final PreApiMetaBeanParser<T> preApiBeanMetaParser,
final ShenyuClientRegisterEventPublisher publisher) {
- return new PreApiBeanRegistrar<>(preApiMetaBeanMatcher, apiBeanMetaParser, publisher);
+ return new PreApiBeanRegistrar<>(preApiMetaBeanMatcher, preApiBeanMetaParser, publisher);
}
/**
diff --git a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/ApiMetaBeanParser.java b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/PreApiMetaBeanParser.java
similarity index 91%
copy from shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/ApiMetaBeanParser.java
copy to shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/PreApiMetaBeanParser.java
index c5b5841a8..ff5197da5 100644
--- a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/ApiMetaBeanParser.java
+++ b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/PreApiMetaBeanParser.java
@@ -20,6 +20,6 @@ package org.apache.shenyu.client.core.register.parser;
import org.apache.shenyu.client.core.register.ApiBean;
import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
-public interface ApiMetaBeanParser<T> extends Parser<MetaDataRegisterDTO, ApiBean<T>> {
+public interface PreApiMetaBeanParser<T> extends Parser<MetaDataRegisterDTO, ApiBean<T>> {
}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/SpringMvcApiBeansExtractor.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/SpringMvcApiBeansExtractor.java
new file mode 100644
index 000000000..afe5a792d
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/SpringMvcApiBeansExtractor.java
@@ -0,0 +1,95 @@
+/*
+ * 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.shenyu.client.springmvc.register;
+
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.core.register.extractor.ApiBeansExtractor;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+
+public class SpringMvcApiBeansExtractor implements ApiBeansExtractor<Object> {
+
+ private final String contextPath;
+
+ public SpringMvcApiBeansExtractor(final String contextPath) {
+ this.contextPath = contextPath;
+ }
+
+ @Override
+ public List<ApiBean<Object>> extract(final ApplicationContext applicationContext) {
+ Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(Controller.class);
+
+ List<ApiBean<Object>> apiBeans = new ArrayList<>();
+
+ beanMap.forEach((k, v) -> {
+ bean2ApiBean(k, v).ifPresent(apiBeans::add);
+ });
+ return apiBeans;
+ }
+
+ private Optional<ApiBean<Object>> bean2ApiBean(final String beanName, final Object bean) {
+
+ Class<?> targetClass = getCorrectedClass(bean);
+
+ RequestMapping classRequestMapping = AnnotationUtils.findAnnotation(targetClass, RequestMapping.class);
+
+ String beanPath = Objects.isNull(classRequestMapping) ? "" : getPath(classRequestMapping);
+
+ ApiBean<Object> apiBean = new ApiBean<>(contextPath, beanName, bean, beanPath, targetClass);
+
+ final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(targetClass);
+
+ for (Method method : methods) {
+
+ final RequestMapping methodRequestMapping =
+ AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
+ if (Objects.isNull(methodRequestMapping)) {
+ continue;
+ }
+ apiBean.addApiDefinition(method, getPath(methodRequestMapping));
+ }
+
+ return Optional.of(apiBean);
+ }
+
+ private Class<?> getCorrectedClass(final Object bean) {
+
+ Class<?> clazz = bean.getClass();
+ if (AopUtils.isAopProxy(bean)) {
+ clazz = AopUtils.getTargetClass(bean);
+ }
+ return clazz;
+ }
+
+ private String getPath(@NonNull final RequestMapping requestMapping) {
+ return Optional.of(requestMapping.path()[0]).orElse("");
+ }
+}
diff --git a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/ApiMetaBeanParser.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaBeanMatcher.java
similarity index 60%
copy from shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/ApiMetaBeanParser.java
copy to shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaBeanMatcher.java
index c5b5841a8..3a4400aba 100644
--- a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/ApiMetaBeanParser.java
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaBeanMatcher.java
@@ -15,11 +15,21 @@
* limitations under the License.
*/
-package org.apache.shenyu.client.core.register.parser;
+package org.apache.shenyu.client.springmvc.register.apimeta;
import org.apache.shenyu.client.core.register.ApiBean;
-import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
+import org.apache.shenyu.client.core.register.matcher.Matcher;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
-public interface ApiMetaBeanParser<T> extends Parser<MetaDataRegisterDTO, ApiBean<T>> {
+public class SpringMvcApiMetaBeanMatcher implements Matcher<ApiBean<Object>> {
+
+ @Override
+ public boolean match(final ApiBean<Object> element) {
+ ShenyuSpringMvcClient annotation = element.getAnnotation(ShenyuSpringMvcClient.class);
+ if (annotation != null) {
+ return !annotation.path().endsWith("/**");
+ }
+ return true;
+ }
}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionMatcher.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionMatcher.java
new file mode 100644
index 000000000..ea80e23aa
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionMatcher.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.client.springmvc.register.apimeta;
+
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.core.register.matcher.AnnotatedApiDefinitionMatcher;
+import org.apache.shenyu.client.core.register.matcher.Matcher;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
+import org.springframework.core.annotation.AnnotationUtils;
+
+public class SpringMvcApiMetaDefinitionMatcher implements Matcher<ApiBean<Object>.ApiDefinition> {
+
+ private final Matcher<ApiBean<Object>.ApiDefinition> matcher;
+
+ public SpringMvcApiMetaDefinitionMatcher() {
+
+ this.matcher = new AnnotatedApiDefinitionMatcher<>(ShenyuSpringMvcClient.class)
+ .or(api -> AnnotationUtils
+ .isAnnotationDeclaredLocally(ShenyuSpringMvcClient.class, api.getBeanClass()));
+ }
+
+ @Override
+ public boolean match(final ApiBean<Object>.ApiDefinition element) {
+ return matcher.match(element);
+ }
+}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionParser.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionParser.java
new file mode 100644
index 000000000..025f2a53b
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionParser.java
@@ -0,0 +1,92 @@
+/*
+ * 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.shenyu.client.springmvc.register.apimeta;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.core.register.parser.ApiMetaDefinitionParser;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.common.utils.PathUtils;
+import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class SpringMvcApiMetaDefinitionParser implements ApiMetaDefinitionParser<Object> {
+
+ private final Boolean addPrefixed;
+
+ private final String appName;
+
+ public SpringMvcApiMetaDefinitionParser(final Boolean addPrefixed, final String appName) {
+ this.addPrefixed = addPrefixed;
+ this.appName = appName;
+ }
+
+ @Override
+ public List<MetaDataRegisterDTO> parse(final ApiBean<Object>.ApiDefinition apiDefinition) {
+
+ ShenyuSpringMvcClient methodAnnotation = apiDefinition.getAnnotation(ShenyuSpringMvcClient.class);
+
+ String methodPath = Objects.isNull(methodAnnotation) ? StringUtils.EMPTY : methodAnnotation.path();
+
+ if (StringUtils.isEmpty(methodPath)) {
+ methodPath = apiDefinition.getMethodPath();
+ }
+
+ String apiPath = PathUtils.pathJoin(apiDefinition.getContextPath(), apiDefinition.getBeanPath(), methodPath);
+
+ String parameterTypes = Optional.ofNullable(apiDefinition.getApiMethod())
+ .map(m -> Arrays.stream(m.getParameterTypes()).map(Class::getName)
+ .collect(Collectors.joining(","))).orElse(null);
+
+ ShenyuSpringMvcClient classAnnotation = apiDefinition.getApiBean()
+ .getAnnotation(ShenyuSpringMvcClient.class);
+
+ String pathDesc = Objects.isNull(methodAnnotation) ? classAnnotation.desc() : methodAnnotation.desc();
+
+ boolean enabled = (Objects.isNull(classAnnotation) || classAnnotation.enabled())
+ && (Objects.isNull(methodAnnotation) || methodAnnotation.enabled());
+
+ String ruleName = Objects.isNull(methodAnnotation) || StringUtils.isEmpty(methodAnnotation.ruleName())
+ ? apiPath : methodAnnotation.ruleName();
+
+ boolean registerMetaData = (Objects.isNull(classAnnotation) || classAnnotation.registerMetaData())
+ && (Objects.isNull(methodAnnotation) || methodAnnotation.registerMetaData());
+
+ return Lists.newArrayList(MetaDataRegisterDTO.builder()
+ .contextPath(apiDefinition.getContextPath())
+ .addPrefixed(addPrefixed)
+ .appName(appName)
+ .serviceName(apiDefinition.getBeanClass().getName())
+ .methodName(apiDefinition.getApiMethodName())
+ .path(apiPath)
+ .pathDesc(pathDesc)
+ .parameterTypes(parameterTypes)
+ .rpcType(RpcTypeEnum.HTTP.getName())
+ .enabled(enabled)
+ .ruleName(ruleName)
+ .registerMetaData(registerMetaData)
+ .build());
+ }
+}
diff --git a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/ApiMetaBeanParser.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanMatcher.java
similarity index 62%
rename from shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/ApiMetaBeanParser.java
rename to shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanMatcher.java
index c5b5841a8..fb3dfda95 100644
--- a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/register/parser/ApiMetaBeanParser.java
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanMatcher.java
@@ -15,11 +15,17 @@
* limitations under the License.
*/
-package org.apache.shenyu.client.core.register.parser;
+package org.apache.shenyu.client.springmvc.register.apimeta;
import org.apache.shenyu.client.core.register.ApiBean;
-import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
+import org.apache.shenyu.client.core.register.matcher.Matcher;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
-public interface ApiMetaBeanParser<T> extends Parser<MetaDataRegisterDTO, ApiBean<T>> {
+public class SpringMvcPreApiMetaBeanMatcher implements Matcher<ApiBean<Object>> {
+ @Override
+ public boolean match(final ApiBean<Object> element) {
+ ShenyuSpringMvcClient annotation = element.getAnnotation(ShenyuSpringMvcClient.class);
+ return annotation != null && annotation.path().endsWith("/**");
+ }
}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanParser.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanParser.java
new file mode 100644
index 000000000..e93f8b0c6
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanParser.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.apache.shenyu.client.springmvc.register.apimeta;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.core.register.parser.PreApiMetaBeanParser;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.common.utils.PathUtils;
+import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
+
+public class SpringMvcPreApiMetaBeanParser implements PreApiMetaBeanParser<Object> {
+
+ private final Boolean addPrefixed;
+
+ private final String appName;
+
+ public SpringMvcPreApiMetaBeanParser(final Boolean addPrefixed, final String appName) {
+ this.addPrefixed = addPrefixed;
+ this.appName = appName;
+ }
+
+ @Override
+ public MetaDataRegisterDTO parse(final ApiBean<Object> apiBean) {
+ return apiBean2ApiMeta(apiBean);
+ }
+
+ private MetaDataRegisterDTO apiBean2ApiMeta(final ApiBean<Object> apiBean) {
+
+ ShenyuSpringMvcClient annotation = apiBean.getAnnotation(ShenyuSpringMvcClient.class);
+ String apiPath = PathUtils.pathJoin(apiBean.getContextPath(), annotation.path());
+
+ return MetaDataRegisterDTO.builder()
+ .contextPath(apiBean.getContextPath())
+ .addPrefixed(addPrefixed)
+ .appName(appName)
+ .serviceName(apiBean.getBeanClass().getName())
+ .methodName(null)
+ .path(apiPath)
+ .pathDesc(annotation.desc())
+ .parameterTypes(null)
+ .rpcType(RpcTypeEnum.HTTP.getName())
+ .enabled(annotation.enabled())
+ .ruleName(StringUtils.defaultIfBlank(annotation.ruleName(), apiPath))
+ .registerMetaData(annotation.registerMetaData())
+ .build();
+ }
+
+}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/SpringMvcApiBeansExtractorTest.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/SpringMvcApiBeansExtractorTest.java
new file mode 100644
index 000000000..49b3e1bc1
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/SpringMvcApiBeansExtractorTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.shenyu.client.springmvc.register;
+
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.core.register.extractor.ApiBeansExtractor;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.stereotype.Controller;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class SpringMvcApiBeansExtractorTest {
+
+ private final ApiBeansExtractor<Object> extractor = new SpringMvcApiBeansExtractor("/http");
+
+ @Test
+ public void testExtractBeans() throws NoSuchMethodException {
+
+ GenericApplicationContext applicationContext = new GenericApplicationContext();
+ applicationContext.refresh();
+ applicationContext.registerBean(ExtractorTestBean.class);
+ applicationContext.registerBean(ExtractorTestWithoutControllerBean.class);
+
+ List<ApiBean<Object>> apiBeans = extractor.extract(applicationContext);
+
+ assertThat(apiBeans.size(), is(1));
+ ApiBean<Object> apiBean = apiBeans.get(0);
+ assertThat(apiBean.getBeanClass(), is(ExtractorTestBean.class));
+ assertThat(apiBean.getBeanPath(), is("/testBean"));
+ assertThat(apiBean.getContextPath(), is("/http"));
+ assertThat(apiBean.getApiDefinitions().size(), is(1));
+
+ ApiBean<?>.ApiDefinition apiDefinition = apiBean.getApiDefinitions().get(0);
+ assertThat(apiDefinition.getApiMethod(), is(ExtractorTestBean.class.getMethod("test")));
+ assertThat(apiDefinition.getMethodPath(), is("/testMethod"));
+ }
+
+ @Controller
+ @RequestMapping("/testBean")
+ private static class ExtractorTestBean {
+ @RequestMapping("/testMethod")
+ public String test() {
+ return "";
+ }
+
+ public String testWithoutRequestMapping() {
+ return "";
+ }
+
+ }
+
+ @Service
+ private static class ExtractorTestWithoutControllerBean {
+ }
+
+}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaBeanMatcherTest.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaBeanMatcherTest.java
new file mode 100644
index 000000000..1218b8881
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaBeanMatcherTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.shenyu.client.springmvc.register.apimeta;
+
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class SpringMvcApiMetaBeanMatcherTest {
+
+ private final SpringMvcApiMetaBeanMatcher beanMatcher = new SpringMvcApiMetaBeanMatcher();
+
+ @Test
+ public void testBeanMatch() throws Exception {
+ ApiBean<Object> apiBean = createSimpleApiBean(TestBeanMatchClass.class);
+ boolean result = beanMatcher.match(apiBean);
+ assertThat(result, is(true));
+ }
+
+ @Test
+ public void testBeanMatchWithoutAnnotation() throws Exception {
+
+ ApiBean<Object> apiBean = createSimpleApiBean(TestBeanMatchWithoutAnnotationClass.class);
+ boolean result = beanMatcher.match(apiBean);
+ assertThat(result, is(true));
+ }
+
+ @Test
+ public void testBeanMatchWithStarAnnotation() throws Exception {
+
+ ApiBean<Object> apiBean = createSimpleApiBean(TestBeanMatchWithStarAnnotationClass.class);
+ boolean result = beanMatcher.match(apiBean);
+ assertThat(result, is(false));
+ }
+
+ private static ApiBean<Object> createSimpleApiBean(final Class<?> beanClass) throws Exception {
+ ApiBean<Object> apiBean = new ApiBean<>("",
+ "", beanClass.getDeclaredConstructor().newInstance(), "", beanClass);
+ return apiBean;
+ }
+
+ @ShenyuSpringMvcClient
+ static class TestBeanMatchClass {
+ }
+
+ static class TestBeanMatchWithoutAnnotationClass {
+ }
+
+ @ShenyuSpringMvcClient("/**")
+ static class TestBeanMatchWithStarAnnotationClass {
+ }
+}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionMatcherTest.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionMatcherTest.java
new file mode 100644
index 000000000..1b4c8d59a
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionMatcherTest.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.apache.shenyu.client.springmvc.register.apimeta;
+
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.lang.reflect.Method;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class SpringMvcApiMetaDefinitionMatcherTest {
+
+ private final SpringMvcApiMetaDefinitionMatcher apiDefinitionMetaMatcher =
+ new SpringMvcApiMetaDefinitionMatcher();
+
+ @Test
+ public void testMatchAnnotatedClass() throws Exception {
+ Method method = TestBeanMatchAnnotatedClass.class.getMethod("testMethod");
+ ApiBean<Object>.ApiDefinition apiDefinition =
+ createApiDefinition(TestBeanMatchAnnotatedClass.class, method, "/testMethod");
+ boolean result = apiDefinitionMetaMatcher.match(apiDefinition);
+
+ assertThat(result, is(true));
+ }
+
+ @Test
+ public void testMatchAnnotatedMethod() throws Exception {
+ Method method = TestBeanMatchClass.class.getMethod("testAnnotatedMethod");
+ ApiBean<Object>.ApiDefinition apiDefinition =
+ createApiDefinition(TestBeanMatchClass.class, method, "/testAnnotatedMethod");
+ boolean result = apiDefinitionMetaMatcher.match(apiDefinition);
+
+ assertThat(result, is(true));
+ }
+
+ @Test
+ public void tesMatchWithoutAnnotation() throws Exception {
+ Method method = TestBeanMatchClass.class.getMethod("testMethod");
+ ApiBean<Object>.ApiDefinition apiDefinition =
+ createApiDefinition(TestBeanMatchClass.class, method, "/testMethod");
+ boolean result = apiDefinitionMetaMatcher.match(apiDefinition);
+
+ assertThat(result, is(false));
+ }
+
+ private ApiBean<Object>.ApiDefinition createApiDefinition(final Class<?> beanClass, final Method method,
+ final String methodPath) throws Exception {
+ ApiBean<Object> apiBean = new ApiBean<>("/http",
+ "testBeanMatchClass", beanClass.getDeclaredConstructor().newInstance(),
+ "/testClass", beanClass);
+
+ apiBean.addApiDefinition(method, methodPath);
+ return apiBean.getApiDefinitions().get(0);
+ }
+
+ @ShenyuSpringMvcClient
+ @RestController
+ @RequestMapping("/testClass")
+ static class TestBeanMatchAnnotatedClass {
+ @RequestMapping("/testMethod")
+ public String testMethod() {
+ return "";
+ }
+ }
+
+ @RestController
+ @RequestMapping("/testClass")
+ static class TestBeanMatchClass {
+
+ @RequestMapping("/testMethod")
+ public String testMethod() {
+ return "";
+ }
+
+ @RequestMapping("/testAnnotatedMethod")
+ @ShenyuSpringMvcClient("/testAnnotatedMethodo")
+ public String testAnnotatedMethod() {
+ return "";
+ }
+ }
+}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionParserTest.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionParserTest.java
new file mode 100644
index 000000000..9a5b8a0ac
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcApiMetaDefinitionParserTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.shenyu.client.springmvc.register.apimeta;
+
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
+import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class SpringMvcApiMetaDefinitionParserTest {
+
+ private final SpringMvcApiMetaDefinitionParser apiMetaDefinitionParser =
+ new SpringMvcApiMetaDefinitionParser(false, "http");
+
+ @Test
+ public void testParse() throws Exception {
+ Method method = TestBeanMatchClass.class.getMethod("testMethod");
+ ApiBean<Object>.ApiDefinition apiDefinition =
+ createApiDefinition(TestBeanMatchClass.class, method, "/testMethod");
+
+ List<MetaDataRegisterDTO> apiMetas = apiMetaDefinitionParser.parse(apiDefinition);
+
+ assertThat(apiMetas.size(), is(1));
+ MetaDataRegisterDTO apiMeta = apiMetas.get(0);
+ assertThat(apiMeta.getPath(), is("/http/testClass/testMethod"));
+ }
+
+ @Test
+ public void testParseWithPathVariable() throws Exception {
+ Method method = TestBeanMatchClass.class.getMethod("testMethod", Integer.class);
+ ApiBean<Object>.ApiDefinition apiDefinition =
+ createApiDefinition(TestBeanMatchClass.class, method, "/testMethod/{id}/info");
+
+ List<MetaDataRegisterDTO> apiMetas = apiMetaDefinitionParser.parse(apiDefinition);
+
+ assertThat(apiMetas.size(), is(1));
+ MetaDataRegisterDTO apiMeta = apiMetas.get(0);
+ assertThat(apiMeta.getPath(), is("/http/testClass/testMethod/**/info"));
+ }
+
+ private ApiBean<Object>.ApiDefinition createApiDefinition(final Class<?> beanClass, final Method method,
+ final String methodPath) throws Exception {
+ ApiBean<Object> apiBean = new ApiBean<>("/http",
+ "testBeanMatchClass", beanClass.getDeclaredConstructor().newInstance(),
+ "/testClass", beanClass);
+
+ apiBean.addApiDefinition(method, methodPath);
+ return apiBean.getApiDefinitions().get(0);
+ }
+
+ @ShenyuSpringMvcClient
+ @RestController
+ @RequestMapping("/testClass")
+ static class TestBeanMatchClass {
+
+ @RequestMapping("/testMethod")
+ public String testMethod() {
+ return "";
+ }
+
+ @RequestMapping("/testMethod/{id}/info")
+ @ShenyuSpringMvcClient("/testMethod/**/info")
+ public String testMethod(@PathVariable final Integer id) {
+ return "";
+ }
+ }
+}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanMatcherTest.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanMatcherTest.java
new file mode 100644
index 000000000..2e3def085
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanMatcherTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.shenyu.client.springmvc.register.apimeta;
+
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class SpringMvcPreApiMetaBeanMatcherTest {
+
+ private final SpringMvcPreApiMetaBeanMatcher preApiMetaBeanMatcher = new SpringMvcPreApiMetaBeanMatcher();
+
+ @Test
+ public void testBeanMatchWithAnnotation() throws Exception {
+ ApiBean<Object> apiBean = createSimpleApiBean(TestBeanMatchClass.class);
+ boolean result = preApiMetaBeanMatcher.match(apiBean);
+ assertThat(result, is(false));
+ }
+
+ @Test
+ public void testBeanMatchWithoutAnnotation() throws Exception {
+
+ ApiBean<Object> apiBean = createSimpleApiBean(TestBeanMatchWithoutAnnotationClass.class);
+ boolean result = preApiMetaBeanMatcher.match(apiBean);
+ assertThat(result, is(false));
+ }
+
+ @Test
+ public void testBeanMatchWithStarAnnotation() throws Exception {
+
+ ApiBean<Object> apiBean = createSimpleApiBean(TestBeanMatchWithStarAnnotationClass.class);
+ boolean result = preApiMetaBeanMatcher.match(apiBean);
+ assertThat(result, is(true));
+ }
+
+ private static ApiBean<Object> createSimpleApiBean(final Class<?> beanClass) throws Exception {
+ ApiBean<Object> apiBean = new ApiBean<>("",
+ "", beanClass.getDeclaredConstructor().newInstance(), "", beanClass);
+ return apiBean;
+ }
+
+ @ShenyuSpringMvcClient
+ static class TestBeanMatchClass {
+ }
+
+ static class TestBeanMatchWithoutAnnotationClass {
+ }
+
+ @ShenyuSpringMvcClient("/**")
+ static class TestBeanMatchWithStarAnnotationClass {
+ }
+
+}
diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanParserTest.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanParserTest.java
new file mode 100644
index 000000000..07ed67c12
--- /dev/null
+++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/register/apimeta/SpringMvcPreApiMetaBeanParserTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.shenyu.client.springmvc.register.apimeta;
+
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
+import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class SpringMvcPreApiMetaBeanParserTest {
+ private final SpringMvcPreApiMetaBeanParser springMvcPreApiMetaBeanParser =
+ new SpringMvcPreApiMetaBeanParser(false, "http");
+
+ @Test
+ public void testParse() throws Exception {
+ ApiBean<Object> apiBean = new ApiBean<>("/http",
+ "testClass", TestBeanMatchClass.class.getDeclaredConstructor().newInstance(),
+ "/testClass", TestBeanMatchClass.class);
+
+ MetaDataRegisterDTO apiMeta = springMvcPreApiMetaBeanParser
+ .parse(apiBean);
+ assertThat(apiMeta.getPath(), is("/http/testClass/**"));
+ }
+
+ @ShenyuSpringMvcClient("/testClass/**")
+ @RestController
+ @RequestMapping("/testClass")
+ static class TestBeanMatchClass {
+
+ @RequestMapping("/testMethod")
+ public String testMethod() {
+ return "";
+ }
+
+ @RequestMapping("/testMethod/{id}/info")
+ @ShenyuSpringMvcClient("/testMethod/**/info")
+ public String testMethod(@PathVariable final Integer id) {
+ return "";
+ }
+ }
+}
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/pom.xml b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/pom.xml
index 8e66d7323..475599f92 100644
--- a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/pom.xml
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/pom.xml
@@ -45,6 +45,11 @@
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+ <artifactId>shenyu-client-autoconfig</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientConfiguration.java b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientConfiguration.java
index b0ad2c696..9abfd4486 100644
--- a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientConfiguration.java
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientConfiguration.java
@@ -17,6 +17,7 @@
package org.apache.shenyu.springboot.starter.client.springmvc;
+import org.apache.shenyu.client.auto.config.ClientRegisterConfiguration;
import org.apache.shenyu.client.springmvc.init.SpringMvcClientEventListener;
import org.apache.shenyu.common.enums.RpcTypeEnum;
import org.apache.shenyu.common.utils.VersionUtils;
@@ -24,6 +25,7 @@ import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository;
import org.apache.shenyu.register.common.config.ShenyuClientConfig;
import org.apache.shenyu.springboot.starter.client.common.config.ShenyuClientCommonBeanConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -48,6 +50,7 @@ public class ShenyuSpringMvcClientConfiguration {
* @return the spring mvc client bean post processor
*/
@Bean
+ @ConditionalOnMissingBean(ClientRegisterConfiguration.class)
public SpringMvcClientEventListener springHttpClientEventListener(final ShenyuClientConfig clientConfig,
final ShenyuClientRegisterRepository shenyuClientRegisterRepository) {
return new SpringMvcClientEventListener(clientConfig.getClient().get(RpcTypeEnum.HTTP.getName()), shenyuClientRegisterRepository);
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientInfoRegisterConfiguration.java b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientInfoRegisterConfiguration.java
new file mode 100644
index 000000000..cee24fbe4
--- /dev/null
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientInfoRegisterConfiguration.java
@@ -0,0 +1,197 @@
+/*
+ * 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.shenyu.springboot.starter.client.springmvc;
+
+import org.apache.shenyu.client.auto.config.ClientRegisterConfiguration;
+import org.apache.shenyu.client.core.constant.ShenyuClientConstants;
+import org.apache.shenyu.client.core.disruptor.ShenyuClientRegisterEventPublisher;
+import org.apache.shenyu.client.core.register.ApiBean;
+import org.apache.shenyu.client.core.register.ClientInfoRefreshedEventListener;
+import org.apache.shenyu.client.core.register.extractor.ApiBeansExtractor;
+import org.apache.shenyu.client.core.register.matcher.Matcher;
+import org.apache.shenyu.client.core.register.parser.ApiDocDefinitionParser;
+import org.apache.shenyu.client.core.register.parser.ApiMetaDefinitionParser;
+import org.apache.shenyu.client.core.register.parser.PreApiMetaBeanParser;
+import org.apache.shenyu.client.springmvc.register.SpringMvcApiBeansExtractor;
+import org.apache.shenyu.client.springmvc.register.apimeta.SpringMvcApiMetaDefinitionMatcher;
+import org.apache.shenyu.client.springmvc.register.apimeta.SpringMvcApiMetaBeanMatcher;
+import org.apache.shenyu.client.springmvc.register.apimeta.SpringMvcApiMetaDefinitionParser;
+import org.apache.shenyu.client.springmvc.register.apimeta.SpringMvcPreApiMetaBeanMatcher;
+import org.apache.shenyu.client.springmvc.register.apimeta.SpringMvcPreApiMetaBeanParser;
+import org.apache.shenyu.common.enums.RpcTypeEnum;
+import org.apache.shenyu.common.utils.UriUtils;
+import org.apache.shenyu.register.common.config.PropertiesConfig;
+import org.apache.shenyu.register.common.config.ShenyuClientConfig;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Properties;
+
+import static org.apache.shenyu.client.core.constant.ShenyuClientConstants.API_DOC_BEAN_MATCHER;
+import static org.apache.shenyu.client.core.constant.ShenyuClientConstants.API_DOC_DEFINITION_MATCHER;
+import static org.apache.shenyu.client.core.constant.ShenyuClientConstants.API_META_BEAN_MATCHER;
+import static org.apache.shenyu.client.core.constant.ShenyuClientConstants.API_META_DEFINITION_MATCHER;
+import static org.apache.shenyu.client.core.constant.ShenyuClientConstants.PRE_API_META_BEAN_MATCHER;
+
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnBean(ClientRegisterConfiguration.class)
+public class ShenyuSpringMvcClientInfoRegisterConfiguration {
+
+ private final boolean addPrefixed;
+
+ private final boolean isFull;
+
+ private final String contextPath;
+
+ private final String appName;
+
+ private final PropertiesConfig clientConfig;
+
+ public ShenyuSpringMvcClientInfoRegisterConfiguration(final ShenyuClientConfig clientConfig) {
+
+ this.clientConfig = clientConfig.getClient().get(RpcTypeEnum.HTTP.getName());
+
+ Properties props = this.clientConfig.getProps();
+
+ this.addPrefixed = Boolean.parseBoolean(props.getProperty(ShenyuClientConstants.ADD_PREFIXED,
+ Boolean.FALSE.toString()));
+
+ this.isFull = Boolean.parseBoolean(props.getProperty(ShenyuClientConstants.IS_FULL, Boolean.FALSE.toString()));
+
+ this.contextPath = Optional.ofNullable(props
+ .getProperty(ShenyuClientConstants.CONTEXT_PATH))
+ .map(UriUtils::repairData).orElse("");
+
+ this.appName = props.getProperty(ShenyuClientConstants.APP_NAME);
+ }
+
+ /**
+ * ClientInfoRefreshedEventListener Bean.
+ *
+ * @param publisher publisher
+ * @return clientInfoRefreshedEventListener
+ */
+ @Bean
+ public ClientInfoRefreshedEventListener clientInfoEventListener(final ShenyuClientRegisterEventPublisher publisher) {
+ return new ClientInfoRefreshedEventListener(clientConfig, publisher, RpcTypeEnum.HTTP);
+ }
+
+ /**
+ * ApiBeansExtractor Bean.
+ *
+ * @return apiBeansExtractor
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public ApiBeansExtractor<Object> apiBeansExtractor() {
+ return new SpringMvcApiBeansExtractor(contextPath);
+ }
+
+ /**
+ * ApiMetaBeanMatcher Bean.
+ *
+ * @return apiMetaBeanMatcher.
+ */
+ @Bean(name = API_META_BEAN_MATCHER)
+ @ConditionalOnMissingBean(name = API_META_BEAN_MATCHER)
+ public Matcher<ApiBean<Object>> apiMetaBeanMatcher() {
+ return new SpringMvcApiMetaBeanMatcher();
+ }
+
+ /**
+ * apiDefinitionMetaMatcher Bean.
+ *
+ * @return apiDefinitionMetaMatcher
+ */
+ @Bean(name = API_META_DEFINITION_MATCHER)
+ @ConditionalOnMissingBean(name = API_META_DEFINITION_MATCHER)
+ public Matcher<ApiBean<Object>.ApiDefinition> apiMetaDefinitionMatcher() {
+ return new SpringMvcApiMetaDefinitionMatcher();
+ }
+
+ /**
+ * ApiMetaDefinitionParser Bean.
+ *
+ * @return apiMetaParser
+ */
+ @Bean
+ public ApiMetaDefinitionParser<Object> apiMetaDefinitionParser() {
+ return new SpringMvcApiMetaDefinitionParser(addPrefixed, appName);
+ }
+
+ /**
+ * PreApiMetaBeanMatcher Bean.
+ *
+ * @return preApiMetaBeanMatcher
+ */
+ @Bean(name = PRE_API_META_BEAN_MATCHER)
+ @ConditionalOnMissingBean(name = PRE_API_META_BEAN_MATCHER)
+ public Matcher<ApiBean<Object>> preApiMetaBeanMatcher() {
+ return new SpringMvcPreApiMetaBeanMatcher();
+ }
+
+ /**
+ * apiBeanMetaParser Bean.
+ *
+ * @return apiBeanMetaParser
+ */
+ @Bean
+ public PreApiMetaBeanParser<Object> apiBeanMetaParser() {
+ return new SpringMvcPreApiMetaBeanParser(addPrefixed, appName);
+ }
+
+ /**
+ * ApiDocBeanMatcher Bean.
+ *
+ * @return apiDocBeanMatcher.
+ */
+ @Bean(name = API_DOC_BEAN_MATCHER)
+ @ConditionalOnMissingBean(name = API_DOC_BEAN_MATCHER)
+ public Matcher<ApiBean<Object>> apiDocBeanMatcher() {
+ //todo implements spring mvc doc collection
+ return e -> false;
+ }
+
+ /**
+ * ApiDocDefinitionMatcher Bean.
+ *
+ * @return apiDocDefinitionMatcher
+ */
+ @Bean(name = API_DOC_DEFINITION_MATCHER)
+ @ConditionalOnMissingBean(name = API_DOC_DEFINITION_MATCHER)
+ public Matcher<ApiBean<Object>.ApiDefinition> apiDocDefinitionMatcher() {
+ //todo implements spring mvc doc collection
+ return e -> false;
+ }
+
+ /**
+ * ApiDocDefinitionParser Bean.
+ *
+ * @return apiDocDefinitionParser
+ */
+ @Bean
+ @ConditionalOnMissingBean
+ public ApiDocDefinitionParser<Object> apiDocDefinitionParser() {
+ //todo implements spring mvc doc collection
+ return t -> Collections.emptyList();
+ }
+}
diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/resources/META-INF/spring.factories b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/resources/META-INF/spring.factories
index 9bb18b050..302750744 100644
--- a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/resources/META-INF/spring.factories
+++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/resources/META-INF/spring.factories
@@ -16,4 +16,5 @@
#
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-org.apache.shenyu.springboot.starter.client.springmvc.ShenyuSpringMvcClientConfiguration
+org.apache.shenyu.springboot.starter.client.springmvc.ShenyuSpringMvcClientConfiguration,\
+org.apache.shenyu.springboot.starter.client.springmvc.ShenyuSpringMvcClientInfoRegisterConfiguration