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