You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2020/02/01 08:18:33 UTC

[skywalking] branch master updated: Make MVC plugin support inherited annotations from interface (#4278)

This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/master by this push:
     new 5c6914f  Make MVC plugin support inherited annotations from interface (#4278)
5c6914f is described below

commit 5c6914fc9eea78eb7285976060690e1a1a80010f
Author: jialong <41...@qq.com>
AuthorDate: Sat Feb 1 16:18:22 2020 +0800

    Make MVC plugin support inherited annotations from interface (#4278)
---
 .../match/MethodInheritanceAnnotationMatcher.java  | 101 +++++++++++++++++++++
 .../MethodInheritanceAnnotationMatcherTest.java    |  87 ++++++++++++++++++
 .../mvc/v3/define/ControllerInstrumentation.java   |   3 +-
 .../define/AbstractControllerInstrumentation.java  |  13 +--
 .../define/AbstractControllerInstrumentation.java  |  13 +--
 .../plugin/spring/mvc/commons/ParsePathUtil.java   |  69 ++++++++++++++
 .../RequestMappingMethodInterceptor.java           |  34 ++++---
 .../interceptor/RestMappingMethodInterceptor.java  |  83 +++++++++--------
 .../spring-4.3.x-scenario/config/expectedData.yaml |  82 ++++++++++++++++-
 .../testcase/implinterface/TestCaseController.java |  38 ++++++++
 .../testcase/implinterface/TestCaseInterface.java  |  33 +++++++
 .../resttemplate/RestTemplateController.java       |   8 ++
 12 files changed, 498 insertions(+), 66 deletions(-)

diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.java
new file mode 100644
index 0000000..1b62350
--- /dev/null
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.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.skywalking.apm.agent.core.plugin.match;
+
+import net.bytebuddy.build.HashCodeAndEqualsPlugin;
+import net.bytebuddy.description.annotation.AnnotationList;
+import net.bytebuddy.description.annotation.AnnotationSource;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.method.MethodList;
+import net.bytebuddy.description.method.ParameterList;
+import net.bytebuddy.description.type.TypeDefinition;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.description.type.TypeList;
+import net.bytebuddy.matcher.CollectionItemMatcher;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import java.util.Objects;
+
+import static net.bytebuddy.matcher.ElementMatchers.annotationType;
+
+/**
+ * Matching used to match method annotations, Can match annotations on interface methods
+ * @author jialong
+ */
+@HashCodeAndEqualsPlugin.Enhance
+public class MethodInheritanceAnnotationMatcher<T extends MethodDescription> extends ElementMatcher.Junction.AbstractBase<T> {
+    /**
+     * The matcher to be applied to the provided annotation list.
+     */
+    private final ElementMatcher<? super AnnotationList> matcher;
+
+    /**
+     * Creates a new matcher for the annotations of an annotated element.
+     *
+     * @param matcher The matcher to be applied to the provided annotation list.
+     */
+    public MethodInheritanceAnnotationMatcher(ElementMatcher<? super AnnotationList> matcher) {
+        this.matcher = matcher;
+    }
+
+    @Override
+    public boolean matches(T target) {
+        if (matcher.matches(target.getDeclaredAnnotations())) {
+            return true;
+        }
+        String name = target.getName();
+        ParameterList<?> parameters = target.getParameters();
+
+        TypeDefinition declaringType = target.getDeclaringType();
+        return recursiveMatches(declaringType, name, parameters);
+    }
+
+
+    private boolean recursiveMatches(TypeDefinition typeDefinition, String methodName, ParameterList<?> parameters) {
+        TypeList.Generic interfaces = typeDefinition.getInterfaces();
+        for (TypeDescription.Generic implInterface : interfaces) {
+            if (recursiveMatches(implInterface, methodName, parameters)) {
+                return true;
+            }
+            MethodList<MethodDescription.InGenericShape> declaredMethods = implInterface.getDeclaredMethods();
+            for (MethodDescription declaredMethod : declaredMethods) {
+                if (Objects.equals(declaredMethod.getName(), methodName) && parameterEquals(parameters, declaredMethod.getParameters())) {
+                    return matcher.matches(declaredMethod.getDeclaredAnnotations());
+                }
+            }
+        }
+        return false;
+    }
+
+
+    private boolean parameterEquals(ParameterList<?> source, ParameterList<?> impl) {
+        if (source.size() != impl.size()) {
+            return false;
+        }
+        for (int i = 0; i < source.size(); i++) {
+            if (!Objects.equals(source.get(i).getType(), impl.get(i).getType())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static <T extends AnnotationSource> ElementMatcher.Junction<T> byMethodInheritanceAnnotationMatcher(ElementMatcher<? super TypeDescription> matcher) {
+        return new MethodInheritanceAnnotationMatcher(new CollectionItemMatcher<>(annotationType(matcher)));
+    }
+}
diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/MethodInheritanceAnnotationMatcherTest.java b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/MethodInheritanceAnnotationMatcherTest.java
new file mode 100644
index 0000000..6555d1c
--- /dev/null
+++ b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/plugin/bytebuddy/MethodInheritanceAnnotationMatcherTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.skywalking.apm.agent.core.plugin.bytebuddy;
+
+import net.bytebuddy.description.annotation.AnnotationSource;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static org.apache.skywalking.apm.agent.core.plugin.match.MethodInheritanceAnnotationMatcher.byMethodInheritanceAnnotationMatcher;
+
+/**
+ * @author jialong
+ */
+public class MethodInheritanceAnnotationMatcherTest {
+
+    @Test
+    public void testMatch() throws Exception {
+        ElementMatcher.Junction<AnnotationSource> matcher = byMethodInheritanceAnnotationMatcher(named("org.apache.skywalking.apm.agent.core.plugin.bytebuddy.MethodInheritanceAnnotationMatcherTest$TestAnnotaion"));
+
+        Assert.assertTrue(matcher.matches(new MethodDescription.ForLoadedMethod(TestBean.class.getMethod("test1", String.class))));
+        Assert.assertTrue(matcher.matches(new MethodDescription.ForLoadedMethod(TestBean.class.getMethod("test2", String.class))));
+        Assert.assertTrue(matcher.matches(new MethodDescription.ForLoadedMethod(TestBean.class.getMethod("test3", String.class))));
+    }
+
+    private class TestBean implements TestInterface1, TestInterface3 {
+        @Override
+        public String test1(String test) {
+            return null;
+        }
+
+        @Override
+        public String test2(String test) {
+            return null;
+        }
+
+        @Override
+        public String test3(String test) {
+            return null;
+        }
+    }
+
+    private interface TestInterface1 extends TestInterface2 {
+        @TestAnnotaion
+        String test1(String test);
+    }
+
+    private interface TestInterface2 {
+        @TestAnnotaion
+        String test2(String test);
+    }
+
+    private interface TestInterface3 {
+        @TestAnnotaion
+        String test3(String test);
+    }
+
+    @Target({ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @Documented
+    public @interface TestAnnotaion {
+    }
+
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v3/define/ControllerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v3/define/ControllerInstrumentation.java
index a63bfac..887f49d 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v3/define/ControllerInstrumentation.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v3/define/ControllerInstrumentation.java
@@ -28,6 +28,7 @@ import org.apache.skywalking.apm.agent.core.plugin.match.ClassAnnotationMatch;
 import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
 
 import static net.bytebuddy.matcher.ElementMatchers.*;
+import static org.apache.skywalking.apm.agent.core.plugin.match.MethodInheritanceAnnotationMatcher.byMethodInheritanceAnnotationMatcher;
 import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_MAPPING_METHOD_INTERCEPTOR;
 
 /**
@@ -65,7 +66,7 @@ public class ControllerInstrumentation extends AbstractSpring3Instrumentation {
             new DeclaredInstanceMethodsInterceptPoint() {
                 @Override
                 public ElementMatcher<MethodDescription> getMethodsMatcher() {
-                    return isAnnotatedWith(named(REQUEST_MAPPING_ENHANCE_ANNOTATION));
+                    return byMethodInheritanceAnnotationMatcher(named(REQUEST_MAPPING_ENHANCE_ANNOTATION));
                 }
 
                 @Override
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/define/AbstractControllerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/define/AbstractControllerInstrumentation.java
index 72fccc4..95b6df7 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/define/AbstractControllerInstrumentation.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/define/AbstractControllerInstrumentation.java
@@ -28,6 +28,7 @@ import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
 import org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants;
 
 import static net.bytebuddy.matcher.ElementMatchers.*;
+import static org.apache.skywalking.apm.agent.core.plugin.match.MethodInheritanceAnnotationMatcher.byMethodInheritanceAnnotationMatcher;
 
 /**
  * {@link ControllerInstrumentation} enhance all constructor and method annotated with
@@ -68,7 +69,7 @@ public abstract class AbstractControllerInstrumentation extends AbstractSpring4I
             new DeclaredInstanceMethodsInterceptPoint() {
                 @Override
                 public ElementMatcher<MethodDescription> getMethodsMatcher() {
-                    return isAnnotatedWith(named("org.springframework.web.bind.annotation.RequestMapping"));
+                    return byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.RequestMapping"));
                 }
 
                 @Override
@@ -84,11 +85,11 @@ public abstract class AbstractControllerInstrumentation extends AbstractSpring4I
             new DeclaredInstanceMethodsInterceptPoint() {
                 @Override
                 public ElementMatcher<MethodDescription> getMethodsMatcher() {
-                    return isAnnotatedWith(named("org.springframework.web.bind.annotation.GetMapping"))
-                        .or(isAnnotatedWith(named("org.springframework.web.bind.annotation.PostMapping")))
-                        .or(isAnnotatedWith(named("org.springframework.web.bind.annotation.PutMapping")))
-                        .or(isAnnotatedWith(named("org.springframework.web.bind.annotation.DeleteMapping")))
-                        .or(isAnnotatedWith(named("org.springframework.web.bind.annotation.PatchMapping")));
+                    return byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.GetMapping"))
+                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PostMapping")))
+                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PutMapping")))
+                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.DeleteMapping")))
+                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PatchMapping")));
                 }
 
                 @Override
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/AbstractControllerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/AbstractControllerInstrumentation.java
index 7f2372a..e4cee47 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/AbstractControllerInstrumentation.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/AbstractControllerInstrumentation.java
@@ -27,6 +27,7 @@ import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
 import org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants;
 
 import static net.bytebuddy.matcher.ElementMatchers.*;
+import static org.apache.skywalking.apm.agent.core.plugin.match.MethodInheritanceAnnotationMatcher.byMethodInheritanceAnnotationMatcher;
 
 /**
  * {@link ControllerInstrumentation} enhance all constructor and method annotated with
@@ -67,7 +68,7 @@ public abstract class AbstractControllerInstrumentation extends AbstractSpring5I
             new DeclaredInstanceMethodsInterceptPoint() {
                 @Override
                 public ElementMatcher<MethodDescription> getMethodsMatcher() {
-                    return isAnnotatedWith(named("org.springframework.web.bind.annotation.RequestMapping"));
+                    return byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.RequestMapping"));
                 }
 
                 @Override
@@ -83,11 +84,11 @@ public abstract class AbstractControllerInstrumentation extends AbstractSpring5I
             new DeclaredInstanceMethodsInterceptPoint() {
                 @Override
                 public ElementMatcher<MethodDescription> getMethodsMatcher() {
-                    return isAnnotatedWith(named("org.springframework.web.bind.annotation.GetMapping"))
-                        .or(isAnnotatedWith(named("org.springframework.web.bind.annotation.PostMapping")))
-                        .or(isAnnotatedWith(named("org.springframework.web.bind.annotation.PutMapping")))
-                        .or(isAnnotatedWith(named("org.springframework.web.bind.annotation.DeleteMapping")))
-                        .or(isAnnotatedWith(named("org.springframework.web.bind.annotation.PatchMapping")));
+                    return byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.GetMapping"))
+                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PostMapping")))
+                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PutMapping")))
+                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.DeleteMapping")))
+                        .or(byMethodInheritanceAnnotationMatcher(named("org.springframework.web.bind.annotation.PatchMapping")));
                 }
 
                 @Override
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ParsePathUtil.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ParsePathUtil.java
new file mode 100644
index 0000000..17737b7
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ParsePathUtil.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.skywalking.apm.plugin.spring.mvc.commons;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+
+/**
+ * Tools for parsing path from annotation
+ * @author jialong
+ */
+public class ParsePathUtil {
+
+    public static String recursiveParseMethodAnnotaion(Method method, Function<Method, String> parseFunc) {
+        String result = parseFunc.apply(method);
+        if (Objects.isNull(result)) {
+            Class<?> declaringClass = method.getDeclaringClass();
+            result = recursiveMatches(declaringClass, method.getName(), method.getParameters(), parseFunc);
+        }
+        return Optional.ofNullable(result).orElse("");
+    }
+
+    private static String recursiveMatches(Class claz, String methodName, Parameter[] parameters, Function<Method, String> parseFunc) {
+        Class<?>[] interfaces = claz.getInterfaces();
+        for (Class<?> implInterface : interfaces) {
+            String path = recursiveMatches(implInterface, methodName, parameters, parseFunc);
+            if (Objects.nonNull(path)) {
+                return path;
+            }
+            Method[] declaredMethods = implInterface.getDeclaredMethods();
+            for (Method declaredMethod : declaredMethods) {
+                if (Objects.equals(declaredMethod.getName(), methodName) && parameterEquals(parameters, declaredMethod.getParameters())) {
+                    return parseFunc.apply(declaredMethod);
+                }
+            }
+        }
+        return null;
+    }
+
+    private static boolean parameterEquals(Parameter[] p1, Parameter[] p2) {
+        if (p1.length != p1.length) {
+            return false;
+        }
+        for (int i = 0; i < p1.length; i++) {
+            if (!Objects.equals(p1[i].getType().getName(), p2[i].getType().getName())) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/RequestMappingMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/RequestMappingMethodInterceptor.java
index 208800d..70ab5fd 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/RequestMappingMethodInterceptor.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/RequestMappingMethodInterceptor.java
@@ -18,6 +18,7 @@
 
 package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor;
 
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.ParsePathUtil;
 import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.web.bind.annotation.RequestMapping;
 
@@ -32,21 +33,28 @@ import java.lang.reflect.Method;
 public class RequestMappingMethodInterceptor extends AbstractMethodInterceptor {
     @Override
     public String getRequestURL(Method method) {
-        String requestURL = "";
-        RequestMapping methodRequestMapping = AnnotationUtils.getAnnotation(method, RequestMapping.class);
-        if (methodRequestMapping.value().length > 0) {
-            requestURL = methodRequestMapping.value()[0];
-        } else if (methodRequestMapping.path().length > 0) {
-            requestURL = methodRequestMapping.path()[0];
-        }
-        return requestURL;
+        return ParsePathUtil.recursiveParseMethodAnnotaion(method, m -> {
+            String requestURL = null;
+            RequestMapping methodRequestMapping = AnnotationUtils.getAnnotation(m, RequestMapping.class);
+            if (methodRequestMapping != null) {
+                if (methodRequestMapping.value().length > 0) {
+                    requestURL = methodRequestMapping.value()[0];
+                } else if (methodRequestMapping.path().length > 0) {
+                    requestURL = methodRequestMapping.path()[0];
+                }
+            }
+            return requestURL;
+        });
     }
 
     @Override
     public String getAcceptedMethodTypes(Method method) {
-        RequestMapping methodRequestMapping = AnnotationUtils.getAnnotation(method, RequestMapping.class);
-        StringBuilder methodTypes = new StringBuilder();
-        if (methodRequestMapping.method().length > 0) {
+        return ParsePathUtil.recursiveParseMethodAnnotaion(method, m -> {
+            RequestMapping methodRequestMapping = AnnotationUtils.getAnnotation(m, RequestMapping.class);
+            if (methodRequestMapping == null || methodRequestMapping.method().length == 0) {
+                return null;
+            }
+            StringBuilder methodTypes = new StringBuilder();
             methodTypes.append("{");
             for (int i = 0; i < methodRequestMapping.method().length; i++) {
                 methodTypes.append(methodRequestMapping.method()[i].toString());
@@ -55,7 +63,7 @@ public class RequestMappingMethodInterceptor extends AbstractMethodInterceptor {
                 }
             }
             methodTypes.append("}");
-        }
-        return methodTypes.toString();
+            return methodTypes.toString();
+        });
     }
 }
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/RestMappingMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/RestMappingMethodInterceptor.java
index d122604..05066b3 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/RestMappingMethodInterceptor.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/RestMappingMethodInterceptor.java
@@ -18,8 +18,13 @@
 
 package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor;
 
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.ParsePathUtil;
 import org.springframework.core.annotation.AnnotationUtils;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
 
 import java.lang.reflect.Method;
 
@@ -34,44 +39,46 @@ import java.lang.reflect.Method;
 public class RestMappingMethodInterceptor extends AbstractMethodInterceptor {
     @Override
     public String getRequestURL(Method method) {
-        String requestURL = "";
-        GetMapping getMapping = AnnotationUtils.getAnnotation(method, GetMapping.class);
-        PostMapping postMapping = AnnotationUtils.getAnnotation(method, PostMapping.class);
-        PutMapping putMapping = AnnotationUtils.getAnnotation(method, PutMapping.class);
-        DeleteMapping deleteMapping = AnnotationUtils.getAnnotation(method, DeleteMapping.class);
-        PatchMapping patchMapping = AnnotationUtils.getAnnotation(method, PatchMapping.class);
-        if (getMapping != null) {
-            if (getMapping.value().length > 0) {
-                requestURL = getMapping.value()[0];
-            } else if (getMapping.path().length > 0) {
-                requestURL = getMapping.path()[0];
+        return ParsePathUtil.recursiveParseMethodAnnotaion(method, m -> {
+            String requestURL = null;
+            GetMapping getMapping = AnnotationUtils.getAnnotation(m, GetMapping.class);
+            PostMapping postMapping = AnnotationUtils.getAnnotation(m, PostMapping.class);
+            PutMapping putMapping = AnnotationUtils.getAnnotation(m, PutMapping.class);
+            DeleteMapping deleteMapping = AnnotationUtils.getAnnotation(m, DeleteMapping.class);
+            PatchMapping patchMapping = AnnotationUtils.getAnnotation(m, PatchMapping.class);
+            if (getMapping != null) {
+                if (getMapping.value().length > 0) {
+                    requestURL = getMapping.value()[0];
+                } else if (getMapping.path().length > 0) {
+                    requestURL = getMapping.path()[0];
+                }
+            } else if (postMapping != null) {
+                if (postMapping.value().length > 0) {
+                    requestURL = postMapping.value()[0];
+                } else if (postMapping.path().length > 0) {
+                    requestURL = postMapping.path()[0];
+                }
+            } else if (putMapping != null) {
+                if (putMapping.value().length > 0) {
+                    requestURL = putMapping.value()[0];
+                } else if (putMapping.path().length > 0) {
+                    requestURL = putMapping.path()[0];
+                }
+            } else if (deleteMapping != null) {
+                if (deleteMapping.value().length > 0) {
+                    requestURL = deleteMapping.value()[0];
+                } else if (deleteMapping.path().length > 0) {
+                    requestURL = deleteMapping.path()[0];
+                }
+            } else if (patchMapping != null) {
+                if (patchMapping.value().length > 0) {
+                    requestURL = patchMapping.value()[0];
+                } else if (patchMapping.path().length > 0) {
+                    requestURL = patchMapping.path()[0];
+                }
             }
-        } else if (postMapping != null) {
-            if (postMapping.value().length > 0) {
-                requestURL = postMapping.value()[0];
-            } else if (postMapping.path().length > 0) {
-                requestURL = postMapping.path()[0];
-            }
-        } else if (putMapping != null) {
-            if (putMapping.value().length > 0) {
-                requestURL = putMapping.value()[0];
-            } else if (putMapping.path().length > 0) {
-                requestURL = putMapping.path()[0];
-            }
-        } else if (deleteMapping != null) {
-            if (deleteMapping.value().length > 0) {
-                requestURL = deleteMapping.value()[0];
-            } else if (deleteMapping.path().length > 0) {
-                requestURL = deleteMapping.path()[0];
-            }
-        } else if (patchMapping != null) {
-            if (patchMapping.value().length > 0) {
-                requestURL = patchMapping.value()[0];
-            } else if (patchMapping.path().length > 0) {
-                requestURL = patchMapping.path()[0];
-            }
-        }
-        return requestURL;
+            return requestURL;
+        });
     }
 
     @Override
diff --git a/test/plugin/scenarios/spring-4.3.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/spring-4.3.x-scenario/config/expectedData.yaml
index 982805a..cc2ac08 100644
--- a/test/plugin/scenarios/spring-4.3.x-scenario/config/expectedData.yaml
+++ b/test/plugin/scenarios/spring-4.3.x-scenario/config/expectedData.yaml
@@ -21,11 +21,11 @@ registryItems:
   operationNames:
     - spring-4.3.x-scenario: [/create/, '/delete/{id}', /inherit/child/test, /healthCheck,
                               '/get/{id}', '/update/{id}', /case/resttemplate,
-                              /case/spring3]
+                              /case/spring3, /impl/requestmapping, /impl/restmapping]
   heartbeat: []
 segmentItems:
   - applicationCode: spring-4.3.x-scenario
-    segmentSize: ge 8
+    segmentSize: ge 10
     segments:
       - segmentId: not null
         spans:
@@ -224,6 +224,52 @@ segmentItems:
                  entryServiceInstanceId: 1}
       - segmentId: not null
         spans:
+          - operationName: /impl/requestmapping
+            operationId: 0
+            parentSpanId: -1
+            spanId: 0
+            spanLayer: Http
+            startTime: nq 0
+            endTime: nq 0
+            componentId: 14
+            componentName: ''
+            isError: false
+            spanType: Entry
+            peer: ''
+            peerId: 0
+            tags:
+              - {key: url, value: 'http://localhost:8080/spring-4.3.x-scenario/impl/requestmapping'}
+              - {key: http.method, value: GET}
+            refs:
+              - {parentEndpointId: 0, parentEndpoint: /case/resttemplate, networkAddressId: 0,
+                 entryEndpointId: 0, refType: CrossProcess, parentSpanId: 7, parentTraceSegmentId: not null,
+                 parentServiceInstanceId: 1, networkAddress: 'localhost:8080', entryEndpoint: /case/resttemplate,
+                 entryServiceInstanceId: 1}
+      - segmentId: not null
+        spans:
+          - operationName: /impl/restmapping
+            operationId: 0
+            parentSpanId: -1
+            spanId: 0
+            spanLayer: Http
+            startTime: nq 0
+            endTime: nq 0
+            componentId: 14
+            componentName: ''
+            isError: false
+            spanType: Entry
+            peer: ''
+            peerId: 0
+            tags:
+              - {key: url, value: 'http://localhost:8080/spring-4.3.x-scenario/impl/restmapping'}
+              - {key: http.method, value: GET}
+            refs:
+              - {parentEndpointId: 0, parentEndpoint: /case/resttemplate, networkAddressId: 0,
+                 entryEndpointId: 0, refType: CrossProcess, parentSpanId: 8, parentTraceSegmentId: not null,
+                 parentServiceInstanceId: 1, networkAddress: 'localhost:8080', entryEndpoint: /case/resttemplate,
+                 entryServiceInstanceId: 1}
+      - segmentId: not null
+        spans:
           - operationName: /spring-4.3.x-scenario/case/spring3/
             operationId: 0
             parentSpanId: 0
@@ -320,6 +366,38 @@ segmentItems:
             tags:
               - {key: http.method, value: GET}
               - {key: url, value: 'http://localhost:8080/spring-4.3.x-scenario/inherit/child/test'}
+          - operationName: /spring-4.3.x-scenario/impl/requestmapping
+            operationId: 0
+            parentSpanId: 0
+            spanId: 7
+            spanLayer: Http
+            startTime: nq 0
+            endTime: nq 0
+            componentId: 12
+            componentName: ''
+            isError: false
+            spanType: Exit
+            peer: localhost:8080
+            peerId: 0
+            tags:
+              - {key: http.method, value: GET}
+              - {key: url, value: 'http://localhost:8080/spring-4.3.x-scenario/impl/requestmapping'}
+          - operationName: /spring-4.3.x-scenario/impl/restmapping
+            operationId: 0
+            parentSpanId: 0
+            spanId: 8
+            spanLayer: Http
+            startTime: nq 0
+            endTime: nq 0
+            componentId: 12
+            componentName: ''
+            isError: false
+            spanType: Exit
+            peer: localhost:8080
+            peerId: 0
+            tags:
+              - {key: http.method, value: GET}
+              - {key: url, value: 'http://localhost:8080/spring-4.3.x-scenario/impl/restmapping'}
           - operationName: /case/resttemplate
             operationId: 0
             parentSpanId: -1
diff --git a/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/implinterface/TestCaseController.java b/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/implinterface/TestCaseController.java
new file mode 100644
index 0000000..62e1e08
--- /dev/null
+++ b/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/implinterface/TestCaseController.java
@@ -0,0 +1,38 @@
+/*
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements.  See the NOTICE file distributed with
+ *   this work for additional information regarding copyright ownership.
+ *   The ASF licenses this file to You under the Apache License, Version 2.0
+ *    (the "License"); you may not use this file except in compliance with
+ *    the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ *
+ */
+package test.org.apache.skywalking.apm.testcase.implinterface;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author jialong
+ */
+@RestController
+public class TestCaseController implements TestCaseInterface {
+
+    @Override
+    public String implRequestMappingAnnotationTestCase() {
+        return "implRequestMappingAnnotationTestCase";
+    }
+
+    @Override
+    public String implRestAnnotationTestCase() {
+        return "implRestAnnotationTestCase";
+    }
+}
diff --git a/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/implinterface/TestCaseInterface.java b/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/implinterface/TestCaseInterface.java
new file mode 100644
index 0000000..3cbe84f
--- /dev/null
+++ b/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/implinterface/TestCaseInterface.java
@@ -0,0 +1,33 @@
+/*
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements.  See the NOTICE file distributed with
+ *   this work for additional information regarding copyright ownership.
+ *   The ASF licenses this file to You under the Apache License, Version 2.0
+ *    (the "License"); you may not use this file except in compliance with
+ *    the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ *
+ */
+package test.org.apache.skywalking.apm.testcase.implinterface;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+/**
+ * @author jialong
+ */
+public interface TestCaseInterface {
+    @RequestMapping("/impl/requestmapping")
+    String implRequestMappingAnnotationTestCase();
+
+    @GetMapping("/impl/restmapping")
+    String implRestAnnotationTestCase();
+}
diff --git a/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/resttemplate/RestTemplateController.java b/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/resttemplate/RestTemplateController.java
index bd55203..9a830bb 100644
--- a/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/resttemplate/RestTemplateController.java
+++ b/test/plugin/scenarios/spring-4.3.x-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/resttemplate/RestTemplateController.java
@@ -66,6 +66,14 @@ public class RestTemplateController {
         response = new OkHttpClient().newCall(inheritRequest).execute();
         logger.info(response.toString());
 
+        Request implRequestMappingRequest = new Request.Builder().url(url + "/impl/requestmapping").build();
+        response = new OkHttpClient().newCall(implRequestMappingRequest).execute();
+        logger.info(response.toString());
+
+        Request implRestMappingRequest = new Request.Builder().url(url + "/impl/restmapping").build();
+        response = new OkHttpClient().newCall(implRestMappingRequest).execute();
+        logger.info(response.toString());
+
         return SUCCESS;
     }