You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2020/08/26 05:23:03 UTC

[skywalking] branch master updated: Fix issue that spring mvc plugin doesn't works when running on reactive api (#5360)

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

wusheng 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 77d94b4  Fix issue that spring mvc plugin doesn't works when running on reactive api (#5360)
77d94b4 is described below

commit 77d94b4d2a906cf160cfab28fd8a045ec2a2ce1d
Author: Xin,Zhang <zh...@apache.org>
AuthorDate: Wed Aug 26 13:22:39 2020 +0800

    Fix issue that spring mvc plugin doesn't works when running on reactive api (#5360)
---
 .github/workflows/plugins-test.0.yaml              |   1 +
 .../mvc/v3/HandlerMethodInvokerInterceptor.java    |   5 +-
 .../plugin/spring/mvc/v4/SpringTestCaseHelper.java |   6 +-
 .../plugin/spring/mvc/v5/GetBeanInterceptor.java   |  27 +++--
 .../plugin/spring/mvc/v5/InvokeInterceptor.java    |  63 +++++++++++
 .../AbstractReactiveControllerInstrumentation.java | 120 +++++++++++++++++++++
 .../AbstractSpring5ReactiveInstrumentation.java    |  14 +--
 .../InvocableHandlerMethodInstrumentation.java     |  64 +++++++++++
 .../ReactiveControllerInstrumentation.java         |  13 ++-
 .../ReactiveRestControllerInstrumentation.java     |  20 ++--
 .../src/main/resources/skywalking-plugin.def       |   5 +-
 .../spring-plugins/mvc-annotation-commons/pom.xml  |   6 ++
 .../mvc/commons/JavaxServletRequestHolder.java     |  56 ++++++++++
 .../mvc/commons/JavaxServletResponseHolder.java    |  35 ++++++
 .../spring/mvc/commons/ReactiveRequestHolder.java  |  61 +++++++++++
 .../spring/mvc/commons/ReactiveResponseHolder.java |  34 ++++++
 .../plugin/spring/mvc/commons/RequestHolder.java   |  33 ++++++
 .../plugin/spring/mvc/commons/ResponseHolder.java  |  22 ++++
 .../interceptor/AbstractMethodInterceptor.java     |  56 +++++-----
 .../commons/interceptor/GetBeanInterceptor.java    |   5 +-
 .../interceptor/InvokeForRequestInterceptor.java   |  11 +-
 .../InvokeHandlerMethodInterceptor.java            |  16 ++-
 .../springmvc-reactive-scenario/bin/startup.sh     |   8 +-
 .../config/expectedData.yaml                       |  50 +++++++++
 .../springmvc-reactive-scenario/configuration.yml  |   7 +-
 .../scenarios/springmvc-reactive-scenario/pom.xml  |  97 +++++++++++++++++
 .../src/main/assembly/assembly.xml                 |  41 +++++++
 .../testcase/sc/springmvcreactive/Application.java |  29 +++++
 .../springmvcreactive/controller/Controller.java   |  46 ++++++++
 .../sc/springmvcreactive/service/TestService.java  |  42 ++++++++
 .../src/main/resources/application.yml             |  18 ++++
 .../support-version.list                           |  12 ++-
 .../testcase/sc/webflux/projectB/Application.java  |  29 +++++
 .../sc/webflux/projectB/config/CustomFilter.java   |  20 ++--
 .../controller/TestAnnotationController.java       |  20 ++--
 .../projectB/route/RoutingConfiguration.java       |  20 ++--
 .../sc/webflux/projectB/route/TestHandler.java     |  20 ++--
 37 files changed, 1009 insertions(+), 123 deletions(-)

diff --git a/.github/workflows/plugins-test.0.yaml b/.github/workflows/plugins-test.0.yaml
index 63fa1a2..c40ec57 100644
--- a/.github/workflows/plugins-test.0.yaml
+++ b/.github/workflows/plugins-test.0.yaml
@@ -56,6 +56,7 @@ jobs:
           - { name: 'grpc-scenario', title: 'gRPC 1.6.0-1.25.0 (22)' }
           - { name: 'gson-scenario', title: 'Gson (7)' }
           - { name: 'elasticjob-3.x-scenario', title: 'elasticjob-3.x-scenario (1)' }
+          - { name: 'springmvc-reactive-scenario', title: 'springmvc-reactive-scenario (7)' }
     steps:
       - uses: actions/checkout@v2
         with:
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/HandlerMethodInvokerInterceptor.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/HandlerMethodInvokerInterceptor.java
index 2123338..dd60587 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/HandlerMethodInvokerInterceptor.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/HandlerMethodInvokerInterceptor.java
@@ -19,10 +19,12 @@
 package org.apache.skywalking.apm.plugin.spring.mvc.v3;
 
 import java.lang.reflect.Method;
+import javax.servlet.http.HttpServletResponse;
 import org.apache.skywalking.apm.agent.core.context.ContextManager;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder;
 import org.springframework.web.context.request.NativeWebRequest;
 
 import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT;
@@ -38,7 +40,8 @@ public class HandlerMethodInvokerInterceptor implements InstanceMethodsAroundInt
         Object handler = allArguments[1];
         if (handler instanceof EnhancedInstance) {
             ContextManager.getRuntimeContext()
-                          .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, ((NativeWebRequest) allArguments[2]).getNativeResponse());
+                          .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder(
+                              (HttpServletResponse) ((NativeWebRequest) allArguments[2]).getNativeResponse()));
         }
     }
 
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/SpringTestCaseHelper.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/SpringTestCaseHelper.java
index ffcee2a..2447d20 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/SpringTestCaseHelper.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/spring/mvc/v4/SpringTestCaseHelper.java
@@ -22,14 +22,16 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import org.apache.skywalking.apm.agent.core.context.ContextManager;
 import org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder;
 
 public final class SpringTestCaseHelper {
 
     public final static void createCaseHandler(HttpServletRequest request, HttpServletResponse response,
         CaseHandler a) throws Throwable {
         ContextManager.createLocalSpan("For-Test");
-        ContextManager.getRuntimeContext().put(Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT, request);
-        ContextManager.getRuntimeContext().put(Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT, response);
+        ContextManager.getRuntimeContext().put(Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT, new JavaxServletRequestHolder(request));
+        ContextManager.getRuntimeContext().put(Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder(response));
         a.handleCase();
         ContextManager.stopSpan();
         ContextManager.getRuntimeContext().remove(Constants.CONTROLLER_METHOD_STACK_DEPTH);
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/GetBeanInterceptor.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/GetBeanInterceptor.java
index 51e2185..b2014c7 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/GetBeanInterceptor.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/GetBeanInterceptor.java
@@ -22,6 +22,8 @@ import org.apache.skywalking.apm.agent.core.context.ContextManager;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder;
 import org.springframework.web.context.request.NativeWebRequest;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
@@ -36,26 +38,33 @@ import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESP
 public class GetBeanInterceptor implements InstanceMethodsAroundInterceptor {
     @Override
     public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
-        MethodInterceptResult result) throws Throwable {
+                             MethodInterceptResult result) throws Throwable {
     }
 
     @Override
     public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
-        Object ret) throws Throwable {
+                              Object ret) throws Throwable {
         if (ret instanceof EnhancedInstance) {
-            ContextManager.getRuntimeContext()
-                          .put(REQUEST_KEY_IN_RUNTIME_CONTEXT, ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
-                              .getRequest());
-            ContextManager.getRuntimeContext()
-                          .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
-                              .getResponse());
+            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            if (requestAttributes != null) {
+                ContextManager.getRuntimeContext()
+                              .put(
+                                  REQUEST_KEY_IN_RUNTIME_CONTEXT,
+                                  new JavaxServletRequestHolder(requestAttributes.getRequest())
+                              );
+                ContextManager.getRuntimeContext()
+                              .put(
+                                  RESPONSE_KEY_IN_RUNTIME_CONTEXT,
+                                  new JavaxServletResponseHolder(requestAttributes.getResponse())
+                              );
+            }
         }
         return ret;
     }
 
     @Override
     public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
-        Class<?>[] argumentsTypes, Throwable t) {
+                                      Class<?>[] argumentsTypes, Throwable t) {
 
     }
 }
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/InvokeInterceptor.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/InvokeInterceptor.java
new file mode 100644
index 0000000..d5f57f7
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/InvokeInterceptor.java
@@ -0,0 +1,63 @@
+/*
+ *  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.v5;
+
+import java.lang.reflect.Method;
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.ReactiveRequestHolder;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.ReactiveResponseHolder;
+import org.springframework.web.server.ServerWebExchange;
+
+import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT;
+import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT;
+
+public class InvokeInterceptor implements InstanceMethodsAroundInterceptor {
+    @Override
+    public void beforeMethod(final EnhancedInstance objInst,
+                             final Method method,
+                             final Object[] allArguments,
+                             final Class<?>[] argumentsTypes,
+                             final MethodInterceptResult result) throws Throwable {
+        ServerWebExchange exchange = (ServerWebExchange) allArguments[0];
+        ContextManager.getRuntimeContext()
+                      .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new ReactiveResponseHolder(exchange.getResponse()));
+        ContextManager.getRuntimeContext()
+                      .put(REQUEST_KEY_IN_RUNTIME_CONTEXT, new ReactiveRequestHolder(exchange.getRequest()));
+    }
+
+    @Override
+    public Object afterMethod(final EnhancedInstance objInst,
+                              final Method method,
+                              final Object[] allArguments,
+                              final Class<?>[] argumentsTypes,
+                              final Object ret) throws Throwable {
+        return ret;
+    }
+
+    @Override
+    public void handleMethodException(final EnhancedInstance objInst,
+                                      final Method method,
+                                      final Object[] allArguments,
+                                      final Class<?>[] argumentsTypes,
+                                      final Throwable t) {
+
+    }
+}
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/reactive/AbstractReactiveControllerInstrumentation.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/reactive/AbstractReactiveControllerInstrumentation.java
new file mode 100644
index 0000000..2dac559
--- /dev/null
+++ 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/reactive/AbstractReactiveControllerInstrumentation.java
@@ -0,0 +1,120 @@
+/*
+ * 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.v5.define.reactive;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.DeclaredInstanceMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassAnnotationMatch;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants;
+import org.apache.skywalking.apm.plugin.spring.mvc.v5.define.ControllerInstrumentation;
+
+import static net.bytebuddy.matcher.ElementMatchers.any;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static org.apache.skywalking.apm.agent.core.plugin.match.MethodInheritanceAnnotationMatcher.byMethodInheritanceAnnotationMatcher;
+
+/**
+ * {@link ControllerInstrumentation} enhance all constructor and method annotated with
+ * <code>org.springframework.web.bind.annotation.RequestMapping</code> that class has
+ * <code>org.springframework.stereotype.Controller</code> annotation.
+ *
+ * <code>ControllerConstructorInterceptor</code> set the controller base path to
+ * dynamic field before execute constructor.
+ *
+ * <code>org.apache.skywalking.apm.plugin.spring.mvc.v4.RequestMappingMethodInterceptor</code> get the request path
+ * from dynamic field first, if not found, <code>RequestMappingMethodInterceptor</code> generate request path  that
+ * combine the path value of current annotation on current method and the base path and set the new path to the dynamic
+ * filed
+ */
+public abstract class AbstractReactiveControllerInstrumentation extends AbstractSpring5ReactiveInstrumentation {
+    @Override
+    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+        return new ConstructorInterceptPoint[] {
+            new ConstructorInterceptPoint() {
+                @Override
+                public ElementMatcher<MethodDescription> getConstructorMatcher() {
+                    return any();
+                }
+
+                @Override
+                public String getConstructorInterceptor() {
+                    return "org.apache.skywalking.apm.plugin.spring.mvc.v5.ControllerConstructorInterceptor";
+                }
+            }
+        };
+    }
+
+    @Override
+    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+        return new InstanceMethodsInterceptPoint[] {
+            new DeclaredInstanceMethodsInterceptPoint() {
+                @Override
+                public ElementMatcher<MethodDescription> getMethodsMatcher() {
+                    return byMethodInheritanceAnnotationMatcher(
+                        named("org.springframework.web.bind.annotation.RequestMapping"));
+                }
+
+                @Override
+                public String getMethodsInterceptor() {
+                    return Constants.REQUEST_MAPPING_METHOD_INTERCEPTOR;
+                }
+
+                @Override
+                public boolean isOverrideArgs() {
+                    return false;
+                }
+            },
+            new DeclaredInstanceMethodsInterceptPoint() {
+                @Override
+                public ElementMatcher<MethodDescription> getMethodsMatcher() {
+                    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
+                public String getMethodsInterceptor() {
+                    return Constants.REST_MAPPING_METHOD_INTERCEPTOR;
+                }
+
+                @Override
+                public boolean isOverrideArgs() {
+                    return false;
+                }
+            }
+        };
+    }
+
+    @Override
+    protected ClassMatch enhanceClass() {
+        return ClassAnnotationMatch.byClassAnnotationMatch(getEnhanceAnnotations());
+    }
+
+    protected abstract String[] getEnhanceAnnotations();
+
+}
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.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/reactive/AbstractSpring5ReactiveInstrumentation.java
similarity index 60%
copy from test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.java
copy to apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/AbstractSpring5ReactiveInstrumentation.java
index 733fdf4..8d37b84 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.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/reactive/AbstractSpring5ReactiveInstrumentation.java
@@ -15,15 +15,15 @@
  *  limitations under the License.
  */
 
-package org.apache.skywalking.apm.testcase.sc.webflux.projectB;
+package org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive;
 
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
 
-@SpringBootApplication
-public class Application {
+public abstract class AbstractSpring5ReactiveInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+    public static final String WITHNESS_CLASSES = "org.springframework.web.reactive.result.method.InvocableHandlerMethod";
 
-    public static void main(String[] args) {
-        SpringApplication.run(Application.class, args);
+    @Override
+    protected final String[] witnessClasses() {
+        return new String[] {WITHNESS_CLASSES};
     }
 }
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/reactive/InvocableHandlerMethodInstrumentation.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/reactive/InvocableHandlerMethodInstrumentation.java
new file mode 100644
index 0000000..0e09d5c
--- /dev/null
+++ 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/reactive/InvocableHandlerMethodInstrumentation.java
@@ -0,0 +1,64 @@
+/*
+ *  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.v5.define.reactive;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType;
+import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
+
+public class InvocableHandlerMethodInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+    @Override
+    protected ClassMatch enhanceClass() {
+        return byName("org.springframework.web.reactive.result.method.InvocableHandlerMethod");
+    }
+
+    @Override
+    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+        return new ConstructorInterceptPoint[0];
+    }
+
+    @Override
+    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+        return new InstanceMethodsInterceptPoint[] {
+            new InstanceMethodsInterceptPoint() {
+                @Override
+                public ElementMatcher<MethodDescription> getMethodsMatcher() {
+                    return named("getMethodArgumentValues").and(
+                        takesArgumentWithType(0, "org.springframework.web.server.ServerWebExchange"));
+                }
+
+                @Override
+                public String getMethodsInterceptor() {
+                    return "org.apache.skywalking.apm.plugin.spring.mvc.v5.InvokeInterceptor";
+                }
+
+                @Override
+                public boolean isOverrideArgs() {
+                    return false;
+                }
+            }
+        };
+    }
+}
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.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/reactive/ReactiveControllerInstrumentation.java
similarity index 68%
copy from test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.java
copy to apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/ReactiveControllerInstrumentation.java
index 733fdf4..3c1091e 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.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/reactive/ReactiveControllerInstrumentation.java
@@ -15,15 +15,14 @@
  *  limitations under the License.
  */
 
-package org.apache.skywalking.apm.testcase.sc.webflux.projectB;
+package org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive;
 
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
+public class ReactiveControllerInstrumentation extends AbstractReactiveControllerInstrumentation {
 
-@SpringBootApplication
-public class Application {
+    public static final String ENHANCE_ANNOTATION = "org.springframework.stereotype.Controller";
 
-    public static void main(String[] args) {
-        SpringApplication.run(Application.class, args);
+    @Override
+    protected String[] getEnhanceAnnotations() {
+        return new String[] {ENHANCE_ANNOTATION};
     }
 }
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.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/reactive/ReactiveRestControllerInstrumentation.java
similarity index 54%
rename from test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.java
rename to apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/v5/define/reactive/ReactiveRestControllerInstrumentation.java
index 733fdf4..693bb5d 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.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/reactive/ReactiveRestControllerInstrumentation.java
@@ -6,24 +6,24 @@
  * (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
+ *     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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
  */
 
-package org.apache.skywalking.apm.testcase.sc.webflux.projectB;
+package org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive;
 
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
+public class ReactiveRestControllerInstrumentation extends AbstractReactiveControllerInstrumentation {
 
-@SpringBootApplication
-public class Application {
+    public static final String ENHANCE_ANNOTATION = "org.springframework.web.bind.annotation.RestController";
 
-    public static void main(String[] args) {
-        SpringApplication.run(Application.class, args);
+    @Override
+    protected String[] getEnhanceAnnotations() {
+        return new String[] {ENHANCE_ANNOTATION};
     }
 }
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def
index fa3b452..38d7929 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def
@@ -16,4 +16,7 @@
 
 spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.ControllerInstrumentation
 spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.RestControllerInstrumentation
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.HandlerMethodInstrumentation
\ No newline at end of file
+spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.HandlerMethodInstrumentation
+spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive.InvocableHandlerMethodInstrumentation
+spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive.ReactiveControllerInstrumentation
+spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.reactive.ReactiveRestControllerInstrumentation
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/pom.xml
index c2c2e23..e274c38 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/pom.xml
@@ -50,6 +50,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <version>5.2.4.RELEASE</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <version>${javax-servlet-api.version}</version>
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletRequestHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletRequestHolder.java
new file mode 100644
index 0000000..8eb5705
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletRequestHolder.java
@@ -0,0 +1,56 @@
+/*
+ *  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.util.Enumeration;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+
+public class JavaxServletRequestHolder implements RequestHolder {
+
+    private final HttpServletRequest request;
+
+    public JavaxServletRequestHolder(HttpServletRequest request) {
+        this.request = request;
+    }
+
+    @Override
+    public String getHeader(final String headerName) {
+        return request.getHeader(headerName);
+    }
+
+    @Override
+    public Enumeration<String> getHeaders(final String headerName) {
+        return request.getHeaders(headerName);
+    }
+
+    @Override
+    public String requestURL() {
+        return request.getRequestURL().toString();
+    }
+
+    @Override
+    public String requestMethod() {
+        return request.getMethod();
+    }
+
+    @Override
+    public Map<String, String[]> getParameterMap() {
+        return request.getParameterMap();
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletResponseHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletResponseHolder.java
new file mode 100644
index 0000000..7fc0144
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/JavaxServletResponseHolder.java
@@ -0,0 +1,35 @@
+/*
+ *  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 javax.servlet.http.HttpServletResponse;
+
+public class JavaxServletResponseHolder implements ResponseHolder {
+
+    private final HttpServletResponse response;
+
+    public JavaxServletResponseHolder(final HttpServletResponse response) {
+        this.response = response;
+    }
+
+    @Override
+    public int statusCode() {
+        return response.getStatus();
+    }
+
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveRequestHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveRequestHolder.java
new file mode 100644
index 0000000..d713077
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveRequestHolder.java
@@ -0,0 +1,61 @@
+/*
+ *  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.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+
+public class ReactiveRequestHolder implements RequestHolder {
+    private final ServerHttpRequest serverHttpRequest;
+
+    public ReactiveRequestHolder(final ServerHttpRequest serverHttpRequest) {
+        this.serverHttpRequest = serverHttpRequest;
+    }
+
+    @Override
+    public String getHeader(final String headerName) {
+        return this.serverHttpRequest.getHeaders().getFirst(headerName);
+    }
+
+    @Override
+    public Enumeration<String> getHeaders(final String headerName) {
+        return Collections.enumeration(this.serverHttpRequest.getHeaders().get(headerName));
+    }
+
+    @Override
+    public String requestURL() {
+        return this.serverHttpRequest.getURI().toString();
+    }
+
+    @Override
+    public String requestMethod() {
+        return this.serverHttpRequest.getMethodValue();
+    }
+
+    @Override
+    public Map<String, String[]> getParameterMap() {
+        Map<String, String[]> parameterMap = new HashMap<>(this.serverHttpRequest.getQueryParams().size());
+        this.serverHttpRequest.getQueryParams().forEach((key, value) -> {
+            parameterMap.put(key, value.toArray(new String[0]));
+        });
+        return parameterMap;
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveResponseHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveResponseHolder.java
new file mode 100644
index 0000000..7ef27fb
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ReactiveResponseHolder.java
@@ -0,0 +1,34 @@
+/*
+ *  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 org.springframework.http.server.reactive.ServerHttpResponse;
+
+public class ReactiveResponseHolder implements ResponseHolder {
+
+    private final ServerHttpResponse response;
+
+    public ReactiveResponseHolder(final ServerHttpResponse response) {
+        this.response = response;
+    }
+
+    @Override
+    public int statusCode() {
+        return response.getStatusCode().value();
+    }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestHolder.java
new file mode 100644
index 0000000..8f4c933
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/RequestHolder.java
@@ -0,0 +1,33 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.skywalking.apm.plugin.spring.mvc.commons;
+
+import java.util.Enumeration;
+import java.util.Map;
+
+public interface RequestHolder {
+    String getHeader(String headerName);
+
+    Enumeration<String> getHeaders(String headerName);
+
+    String requestURL();
+
+    String requestMethod();
+
+    Map<String, String[]> getParameterMap();
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ResponseHolder.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ResponseHolder.java
new file mode 100644
index 0000000..b46c4f2
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/ResponseHolder.java
@@ -0,0 +1,22 @@
+/*
+ *  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;
+
+public interface ResponseHolder {
+    int statusCode();
+}
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/AbstractMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java
index d7817d6..9015ef8 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/AbstractMethodInterceptor.java
@@ -22,12 +22,9 @@ import java.lang.reflect.Method;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.LinkedList;
-import java.util.Map;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 import org.apache.skywalking.apm.agent.core.context.CarrierItem;
 import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
 import org.apache.skywalking.apm.agent.core.context.ContextManager;
@@ -41,6 +38,8 @@ import org.apache.skywalking.apm.agent.core.util.CollectionUtil;
 import org.apache.skywalking.apm.agent.core.util.MethodUtil;
 import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
 import org.apache.skywalking.apm.plugin.spring.mvc.commons.EnhanceRequireObjectCache;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.RequestHolder;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.ResponseHolder;
 import org.apache.skywalking.apm.plugin.spring.mvc.commons.SpringMVCPluginConfig;
 import org.apache.skywalking.apm.plugin.spring.mvc.commons.exception.IllegalMethodStackDepthException;
 import org.apache.skywalking.apm.plugin.spring.mvc.commons.exception.ServletResponseNotFoundException;
@@ -96,8 +95,8 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
             operationName = getAcceptedMethodTypes(method) + requestURL;
         }
 
-        HttpServletRequest request = (HttpServletRequest) ContextManager.getRuntimeContext()
-                                                                        .get(REQUEST_KEY_IN_RUNTIME_CONTEXT);
+        RequestHolder request = (RequestHolder) ContextManager.getRuntimeContext()
+                                                              .get(REQUEST_KEY_IN_RUNTIME_CONTEXT);
         if (request != null) {
             StackDepth stackDepth = (StackDepth) ContextManager.getRuntimeContext().get(CONTROLLER_METHOD_STACK_DEPTH);
 
@@ -110,8 +109,8 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
                 }
 
                 AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier);
-                Tags.URL.set(span, request.getRequestURL().toString());
-                Tags.HTTP.METHOD.set(span, request.getMethod());
+                Tags.URL.set(span, request.requestURL());
+                Tags.HTTP.METHOD.set(span, request.requestMethod());
                 span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION);
                 SpanLayer.asHttp(span);
 
@@ -161,8 +160,8 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
             return ret;
         }
 
-        HttpServletRequest request = (HttpServletRequest) ContextManager.getRuntimeContext()
-                                                                        .get(REQUEST_KEY_IN_RUNTIME_CONTEXT);
+        RequestHolder request = (RequestHolder) ContextManager.getRuntimeContext()
+                                                              .get(REQUEST_KEY_IN_RUNTIME_CONTEXT);
 
         if (request != null) {
             StackDepth stackDepth = (StackDepth) ContextManager.getRuntimeContext().get(CONTROLLER_METHOD_STACK_DEPTH);
@@ -175,16 +174,16 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
             AbstractSpan span = ContextManager.activeSpan();
 
             if (stackDepth.depth() == 0) {
-                HttpServletResponse response = (HttpServletResponse) ContextManager.getRuntimeContext()
-                                                                                   .get(
-                                                                                       RESPONSE_KEY_IN_RUNTIME_CONTEXT);
+                ResponseHolder response = (ResponseHolder) ContextManager.getRuntimeContext()
+                                                                         .get(
+                                                                             RESPONSE_KEY_IN_RUNTIME_CONTEXT);
                 if (response == null) {
                     throw new ServletResponseNotFoundException();
                 }
 
-                if (IS_SERVLET_GET_STATUS_METHOD_EXIST && response.getStatus() >= 400) {
+                if (IS_SERVLET_GET_STATUS_METHOD_EXIST && response.statusCode() >= 400) {
                     span.errorOccurred();
-                    Tags.STATUS_CODE.set(span, Integer.toString(response.getStatus()));
+                    Tags.STATUS_CODE.set(span, Integer.toString(response.statusCode()));
                 }
 
                 ContextManager.getRuntimeContext().remove(REQUEST_KEY_IN_RUNTIME_CONTEXT);
@@ -209,7 +208,7 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
         ContextManager.activeSpan().errorOccurred().log(t);
     }
 
-    private void collectHttpParam(HttpServletRequest request, AbstractSpan span) {
+    private void collectHttpParam(RequestHolder request, AbstractSpan span) {
         final Map<String, String[]> parameterMap = request.getParameterMap();
         if (parameterMap != null && !parameterMap.isEmpty()) {
             String tagValue = CollectionUtil.toString(parameterMap);
@@ -219,21 +218,26 @@ public abstract class AbstractMethodInterceptor implements InstanceMethodsAround
         }
     }
 
-    private void collectHttpHeaders(HttpServletRequest request, AbstractSpan span) {
+    private void collectHttpHeaders(RequestHolder request, AbstractSpan span) {
         final List<String> headersList = new LinkedList<>();
-        SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream().filter(headerName -> request.getHeaders(headerName) != null).forEach(headerName -> {
-            Enumeration<String> headerValues = request.getHeaders(headerName);
-            List<String> valueList = Collections.list(headerValues);
-            if (!CollectionUtil.isEmpty(valueList)) {
-                String headerValue = valueList.toString();
-                headersList.add(headerName + "=" + headerValue);
-            }
-        });
+        SpringMVCPluginConfig.Plugin.Http.INCLUDE_HTTP_HEADERS.stream()
+                                                              .filter(
+                                                                  headerName -> request.getHeaders(headerName) != null)
+                                                              .forEach(headerName -> {
+                                                                  Enumeration<String> headerValues = request.getHeaders(
+                                                                      headerName);
+                                                                  List<String> valueList = Collections.list(
+                                                                      headerValues);
+                                                                  if (!CollectionUtil.isEmpty(valueList)) {
+                                                                      String headerValue = valueList.toString();
+                                                                      headersList.add(headerName + "=" + headerValue);
+                                                                  }
+                                                              });
 
         if (!headersList.isEmpty()) {
             String tagValue = headersList.stream().collect(Collectors.joining("\n"));
             tagValue = SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD > 0 ?
-                    StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD) : tagValue;
+                StringUtil.cut(tagValue, SpringMVCPluginConfig.Plugin.Http.HTTP_HEADERS_LENGTH_THRESHOLD) : tagValue;
             Tags.HTTP.HEADERS.set(span, tagValue);
         }
     }
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/GetBeanInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/GetBeanInterceptor.java
index 6cc6c62..3fd2d61 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/GetBeanInterceptor.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/GetBeanInterceptor.java
@@ -23,6 +23,7 @@ import org.apache.skywalking.apm.agent.core.context.ContextManager;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder;
 import org.springframework.web.context.request.NativeWebRequest;
 import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
@@ -44,8 +45,8 @@ public class GetBeanInterceptor implements InstanceMethodsAroundInterceptor {
         Object ret) throws Throwable {
         if (ret instanceof EnhancedInstance) {
             ContextManager.getRuntimeContext()
-                          .put(REQUEST_KEY_IN_RUNTIME_CONTEXT, ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
-                              .getRequest());
+                          .put(REQUEST_KEY_IN_RUNTIME_CONTEXT, new JavaxServletRequestHolder(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
+                              .getRequest()));
         }
         return ret;
     }
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/InvokeForRequestInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeForRequestInterceptor.java
index e5430f7..4360048 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeForRequestInterceptor.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeForRequestInterceptor.java
@@ -19,10 +19,12 @@
 package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor;
 
 import java.lang.reflect.Method;
+import javax.servlet.http.HttpServletResponse;
 import org.apache.skywalking.apm.agent.core.context.ContextManager;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder;
 import org.springframework.web.context.request.NativeWebRequest;
 
 import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT;
@@ -34,20 +36,21 @@ import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESP
 public class InvokeForRequestInterceptor implements InstanceMethodsAroundInterceptor {
     @Override
     public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
-        MethodInterceptResult result) throws Throwable {
+                             MethodInterceptResult result) throws Throwable {
         ContextManager.getRuntimeContext()
-                      .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, ((NativeWebRequest) allArguments[0]).getNativeResponse());
+                      .put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder(
+                          (HttpServletResponse) ((NativeWebRequest) allArguments[0]).getNativeResponse()));
     }
 
     @Override
     public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
-        Object ret) throws Throwable {
+                              Object ret) throws Throwable {
         return ret;
     }
 
     @Override
     public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
-        Class<?>[] argumentsTypes, Throwable t) {
+                                      Class<?>[] argumentsTypes, Throwable t) {
 
     }
 }
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/InvokeHandlerMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeHandlerMethodInterceptor.java
index 8785d55..9d7df03 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeHandlerMethodInterceptor.java
+++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-commons/src/main/java/org/apache/skywalking/apm/plugin/spring/mvc/commons/interceptor/InvokeHandlerMethodInterceptor.java
@@ -19,10 +19,14 @@
 package org.apache.skywalking.apm.plugin.spring.mvc.commons.interceptor;
 
 import java.lang.reflect.Method;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import org.apache.skywalking.apm.agent.core.context.ContextManager;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
 import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletRequestHolder;
+import org.apache.skywalking.apm.plugin.spring.mvc.commons.JavaxServletResponseHolder;
 
 import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.REQUEST_KEY_IN_RUNTIME_CONTEXT;
 import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESPONSE_KEY_IN_RUNTIME_CONTEXT;
@@ -30,21 +34,23 @@ import static org.apache.skywalking.apm.plugin.spring.mvc.commons.Constants.RESP
 public class InvokeHandlerMethodInterceptor implements InstanceMethodsAroundInterceptor {
     @Override
     public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
-        MethodInterceptResult result) throws Throwable {
+                             MethodInterceptResult result) throws Throwable {
         if (allArguments[2] instanceof EnhancedInstance) {
-            ContextManager.getRuntimeContext().put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, allArguments[1]);
-            ContextManager.getRuntimeContext().put(REQUEST_KEY_IN_RUNTIME_CONTEXT, allArguments[0]);
+            ContextManager.getRuntimeContext().put(RESPONSE_KEY_IN_RUNTIME_CONTEXT, new JavaxServletResponseHolder(
+                (HttpServletResponse) allArguments[1]));
+            ContextManager.getRuntimeContext().put(REQUEST_KEY_IN_RUNTIME_CONTEXT, new JavaxServletRequestHolder(
+                (HttpServletRequest) allArguments[0]));
         }
     }
 
     @Override
     public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
-        Object ret) throws Throwable {
+                              Object ret) throws Throwable {
         return ret;
     }
 
     @Override
     public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
-        Class<?>[] argumentsTypes, Throwable t) {
+                                      Class<?>[] argumentsTypes, Throwable t) {
     }
 }
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def b/test/plugin/scenarios/springmvc-reactive-scenario/bin/startup.sh
similarity index 70%
copy from apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def
copy to test/plugin/scenarios/springmvc-reactive-scenario/bin/startup.sh
index fa3b452..a6b8ccb 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/bin/startup.sh
@@ -1,3 +1,5 @@
+#!/bin/bash
+#
 # 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
@@ -14,6 +16,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.ControllerInstrumentation
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.RestControllerInstrumentation
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.HandlerMethodInstrumentation
\ No newline at end of file
+home="$(cd "$(dirname $0)"; pwd)"
+
+java -jar ${agent_opts} ${home}/../libs/springmvc-reactive-scenario.jar &
\ No newline at end of file
diff --git a/test/plugin/scenarios/springmvc-reactive-scenario/config/expectedData.yaml b/test/plugin/scenarios/springmvc-reactive-scenario/config/expectedData.yaml
new file mode 100644
index 0000000..3659a76
--- /dev/null
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/config/expectedData.yaml
@@ -0,0 +1,50 @@
+# 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.
+
+segmentItems:
+- serviceName: springmvc-reactive-scenario
+  segmentSize: nq 0
+  segments:
+  - segmentId: not null
+    spans:
+    - operationName: H2/JDBI/PreparedStatement/executeQuery
+      operationId: 0
+      parentSpanId: 0
+      spanId: 1
+      spanLayer: Database
+      startTime: not null
+      endTime: not null
+      componentId: 32
+      isError: false
+      spanType: Exit
+      peer: not null
+      tags:
+      - {key: db.type, value: not null}
+      - {key: db.instance, value: test}
+      - {key: db.statement, value: not null}
+      skipAnalysis: 'false'
+    - operationName: /testcase/{test}
+      operationId: 0
+      parentSpanId: -1
+      spanId: 0
+      spanLayer: Http
+      startTime: not null
+      endTime: not null
+      componentId: 14
+      tags:
+      - {key: url, value: not null}
+      - {key: http.method, value: GET}
+      skipAnalysis: 'false'
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def b/test/plugin/scenarios/springmvc-reactive-scenario/configuration.yml
similarity index 70%
copy from apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def
copy to test/plugin/scenarios/springmvc-reactive-scenario/configuration.yml
index fa3b452..12cb454 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/configuration.yml
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.ControllerInstrumentation
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.RestControllerInstrumentation
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.HandlerMethodInstrumentation
\ No newline at end of file
+type: jvm
+entryService: http://localhost:8080/testcase/test
+healthCheck: http://localhost:8080/testcase/healthCheck
+startScript: ./bin/startup.sh
diff --git a/test/plugin/scenarios/springmvc-reactive-scenario/pom.xml b/test/plugin/scenarios/springmvc-reactive-scenario/pom.xml
new file mode 100644
index 0000000..aa3c075
--- /dev/null
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/pom.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  ~
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.skywalking</groupId>
+    <artifactId>springmvc-reactive-scenario</artifactId>
+    <packaging>jar</packaging>
+    <version>5.0.0</version>
+
+    <name>skywalking-springmvc-reactive-scenario</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <test.framework.version>2.1.1.RELEASE</test.framework.version>
+        <docker.image.version>${test.framework.version}</docker.image.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+            <version>${test.framework.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <version>1.4.200</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>springmvc-reactive-scenario</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.6.0</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>1.5.9.RELEASE</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>assemble</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <descriptors>
+                                <descriptor>src/main/assembly/assembly.xml</descriptor>
+                            </descriptors>
+                            <outputDirectory>./target/</outputDirectory>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/test/plugin/scenarios/springmvc-reactive-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..09e49a2
--- /dev/null
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/assembly/assembly.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  ~
+  -->
+<assembly
+    xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+    <formats>
+        <format>zip</format>
+    </formats>
+
+    <fileSets>
+        <fileSet>
+            <directory>./bin</directory>
+            <fileMode>0775</fileMode>
+        </fileSet>
+    </fileSets>
+
+    <files>
+        <file>
+            <source>${project.build.directory}/springmvc-reactive-scenario.jar</source>
+            <outputDirectory>./libs</outputDirectory>
+            <fileMode>0775</fileMode>
+        </file>
+    </files>
+</assembly>
diff --git a/test/plugin/scenarios/springmvc-reactive-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/Application.java b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/Application.java
new file mode 100644
index 0000000..4d6eae3
--- /dev/null
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/Application.java
@@ -0,0 +1,29 @@
+/*
+ *  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.apache.skywalking.apm.testcase.sc.springmvcreactive;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}
diff --git a/test/plugin/scenarios/springmvc-reactive-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/controller/Controller.java b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/controller/Controller.java
new file mode 100644
index 0000000..3359de3
--- /dev/null
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/controller/Controller.java
@@ -0,0 +1,46 @@
+/*
+ *  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.apache.skywalking.apm.testcase.sc.springmvcreactive.controller;
+
+import java.sql.SQLException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+import test.apache.skywalking.apm.testcase.sc.springmvcreactive.service.TestService;
+
+@RestController
+public class Controller {
+
+    @Autowired
+    private TestService testService;
+
+    @RequestMapping("/testcase/healthCheck")
+    public String healthCheck() {
+        return "healthCheck";
+    }
+
+    @GetMapping("/testcase/{test}")
+    public Mono<String> hello(@RequestBody(required = false) String body, @PathVariable("test") String test) throws SQLException {
+        testService.executeSQL();
+        return Mono.just("Hello World");
+    }
+}
diff --git a/test/plugin/scenarios/springmvc-reactive-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/service/TestService.java b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/service/TestService.java
new file mode 100644
index 0000000..aff108f
--- /dev/null
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/springmvcreactive/service/TestService.java
@@ -0,0 +1,42 @@
+/*
+ *  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.apache.skywalking.apm.testcase.sc.springmvcreactive.service;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import javax.annotation.PostConstruct;
+import org.springframework.stereotype.Service;
+
+@Service
+public class TestService {
+
+    private Connection connection;
+
+    @PostConstruct
+    private void setUp() throws SQLException {
+        connection = DriverManager.getConnection("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "sa", "");
+    }
+
+    public void executeSQL() throws SQLException {
+        PreparedStatement preparedStatement = connection.prepareStatement("SELECT 1 = 1");
+        preparedStatement.executeQuery();
+    }
+
+}
diff --git a/test/plugin/scenarios/springmvc-reactive-scenario/src/main/resources/application.yml b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/resources/application.yml
new file mode 100644
index 0000000..e916bcd
--- /dev/null
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/src/main/resources/application.yml
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+server.port: 8080
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def b/test/plugin/scenarios/springmvc-reactive-scenario/support-version.list
similarity index 70%
copy from apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def
copy to test/plugin/scenarios/springmvc-reactive-scenario/support-version.list
index fa3b452..553ef8e 100644
--- a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-5.x-plugin/src/main/resources/skywalking-plugin.def
+++ b/test/plugin/scenarios/springmvc-reactive-scenario/support-version.list
@@ -14,6 +14,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.ControllerInstrumentation
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.RestControllerInstrumentation
-spring-mvc-annotation-5.x=org.apache.skywalking.apm.plugin.spring.mvc.v5.define.HandlerMethodInstrumentation
\ No newline at end of file
+# 2.0.0-2.1.0 are supported, but due to the status code bug(https://github.com/spring-projects/spring-framework/issues/21901)
+# we don’t test them
+2.1.1.RELEASE
+2.1.2.RELEASE
+2.1.3.RELEASE
+2.1.4.RELEASE
+2.1.5.RELEASE
+2.1.6.RELEASE
+2.1.7.RELEASE
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.java b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.java
new file mode 100644
index 0000000..0278e99
--- /dev/null
+++ b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/Application.java
@@ -0,0 +1,29 @@
+/*
+ *  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.apache.skywalking.apm.testcase.sc.webflux.projectB;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+}
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/config/CustomFilter.java b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/config/CustomFilter.java
similarity index 56%
rename from test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/config/CustomFilter.java
rename to test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/config/CustomFilter.java
index 5d850df..001a254 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/config/CustomFilter.java
+++ b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/config/CustomFilter.java
@@ -1,21 +1,21 @@
 /*
- * 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
+ *  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.
+ *  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.testcase.sc.webflux.projectB.config;
+package test.apache.skywalking.apm.testcase.sc.webflux.projectB.config;
 
 import org.springframework.stereotype.Component;
 import org.springframework.web.server.ServerWebExchange;
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java
similarity index 64%
rename from test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java
rename to test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java
index 2b45fe9..dfd121f 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java
+++ b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/controller/TestAnnotationController.java
@@ -1,21 +1,21 @@
 /*
- * 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
+ *  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.
+ *  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.testcase.sc.webflux.projectB.controller;
+package test.apache.skywalking.apm.testcase.sc.webflux.projectB.controller;
 
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/route/RoutingConfiguration.java b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/route/RoutingConfiguration.java
similarity index 53%
rename from test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/route/RoutingConfiguration.java
rename to test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/route/RoutingConfiguration.java
index 43bc5b9..137ea5d 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/route/RoutingConfiguration.java
+++ b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/route/RoutingConfiguration.java
@@ -1,21 +1,21 @@
 /*
- * 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
+ *  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.
+ *  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.testcase.sc.webflux.projectB.route;
+package test.apache.skywalking.apm.testcase.sc.webflux.projectB.route;
 
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
diff --git a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/route/TestHandler.java b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/route/TestHandler.java
similarity index 55%
rename from test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/route/TestHandler.java
rename to test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/route/TestHandler.java
index c7e6afa..78a7cfc 100644
--- a/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/org/apache/skywalking/apm/testcase/sc/webflux/projectB/route/TestHandler.java
+++ b/test/plugin/scenarios/webflux-scenario/webflux-projectB-scenario/src/main/java/test/apache/skywalking/apm/testcase/sc/webflux/projectB/route/TestHandler.java
@@ -1,21 +1,21 @@
 /*
- * 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
+ *  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.
+ *  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.testcase.sc.webflux.projectB.route;
+package test.apache.skywalking.apm.testcase.sc.webflux.projectB.route;
 
 import org.springframework.http.MediaType;
 import org.springframework.stereotype.Component;