Merge branch 'master' into dependabot/maven/dubbo-api-docs/com.puppycrawl.tools-checkstyle-8.29
diff --git a/.codecov.yml b/.codecov.yml
index 1acf443..8a9fd1b 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,3 +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.
+#
 coverage:
   status:
     # pull-requests only
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..bd06c1c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -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.
+#
+
+blank_issues_enabled: false
+contact_links:
+  - name: Question & FAQ & Proposal
+    url: https://github.com/apache/dubbo/issues/new/choose
+    about: Please submit the issue or discussion to the apache/dubbo repository.
diff --git a/.github/ISSUE_TEMPLATE/dubbo-spi-extensions-feature-request-template.md b/.github/ISSUE_TEMPLATE/dubbo-spi-extensions-feature-request-template.md
deleted file mode 100644
index c394c1d..0000000
--- a/.github/ISSUE_TEMPLATE/dubbo-spi-extensions-feature-request-template.md
+++ /dev/null
@@ -1,26 +0,0 @@
----
-name: Dubbo-SPI-Extensions feature request template about: If you would like to request a feature to
-Dubbo-SPI-Extensions, please use this template.
-
----
-
-- [ ] I have searched the [issues](https://github.com/apache/dubbo-spi-extensions/issues) of this repository and believe
-  that this is not a duplicate.
-- [ ] I have checked the [REAMDE](https://github.com/apache/dubbo-admin/blob/dubbo-spi-extensions/README.md) of this
-  repository and believe that this is not a duplicate.
-
-### Is your feature request related to a problem? Please describe.
-
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-### Describe the solution you'd like
-
-A clear and concise description of what you want to happen.
-
-### Describe alternatives you've considered
-
-A clear and concise description of any alternative solutions or features you've considered.
-
-### Additional context
-
-Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/dubbo-spi-extensions-issue-report-template.md b/.github/ISSUE_TEMPLATE/dubbo-spi-extensions-issue-report-template.md
deleted file mode 100644
index 78d4d54..0000000
--- a/.github/ISSUE_TEMPLATE/dubbo-spi-extensions-issue-report-template.md
+++ /dev/null
@@ -1,39 +0,0 @@
----
-name: Dubbo-SPI-Extensions issue report template about: If you would like to report a issue to Dubbo-SPI-Extensions,
-please use this template.
-
----
-
-- [ ] I have searched the [issues](https://github.com/apache/dubbo-spi-extensions/issues) of this repository and believe
-  that this is not a duplicate.
-- [ ] I have checked the [REAMDE](https://github.com/apache/dubbo-admin/blob/dubbo-spi-extensions/README.md) of this
-  repository and believe that this is not a duplicate.
-
-### Environment
-
-* Dubbo version: xxx
-* Operating System version: xxx
-* Java version: xxx
-* Module name: For example, dubbo-api-docs
-
-### Steps to reproduce this issue
-
-1. xxx
-2. xxx
-3. xxx
-
-Pls. provide [GitHub address] to reproduce this issue.
-
-### Expected Result
-
-What do you expected from the above steps?
-
-### Actual Result
-
-What actually happens?
-
-If there is an exception, please attach the exception trace:
-
-```
-Just put your stack trace here!
-```
diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml
new file mode 100644
index 0000000..805733a
--- /dev/null
+++ b/.github/dependabot.yaml
@@ -0,0 +1,15 @@
+version: 2
+updates:
+
+  - package-ecosystem: "maven"
+    directory: "/"
+    open-pull-requests-limit: 20
+    # Ignore major version updates
+    ignore:
+      - dependency-name: "*"
+        update-types: ["version-update:semver-major"]
+    schedule:
+      interval: "weekly"
+      day: "monday"
+      time: "03:00"
+      timezone: "US/Eastern"
diff --git a/.github/workflows/build-and-test-pr.yml b/.github/workflows/build-and-test-pr.yml
new file mode 100644
index 0000000..8dcdee3
--- /dev/null
+++ b/.github/workflows/build-and-test-pr.yml
@@ -0,0 +1,340 @@
+name: Build and Test For PR
+
+on: [push, pull_request, workflow_dispatch]
+
+permissions:
+  contents: read
+
+env:
+  FORK_COUNT: 2
+  FAIL_FAST: 0
+  SHOW_ERROR_DETAIL: 1
+  #multi-version size limit
+  VERSIONS_LIMIT: 4
+  JACOCO_ENABLE: true
+  CANDIDATE_VERSIONS: '
+    spring.version:5.3.24;
+    spring-boot.version:2.7.6;
+    '
+
+jobs:
+  # Check ASF License
+  check-license:
+    name: "Check License"
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+      - name: Check License
+        uses: apache/skywalking-eyes@v0.5.0
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - name: Restore Maven local repository cache
+        uses: actions/cache@v4
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: Check Dependencies' License
+        uses: apache/skywalking-eyes/dependency@e1a02359b239bd28de3f6d35fdc870250fa513d5
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          config: .licenserc.yaml
+          mode: check
+  # Build dubbo-build-tool
+  build-tools:
+    name: "Build-tools"
+    needs: check-license
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+      fail-fast: false
+    outputs:
+      cache-key: ${{ steps.dubbo-build-tools.cache }}
+    steps:
+      - name: Support long paths on Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        name: build tools
+        with:
+          repository: 'apache/dubbo'
+          ref: '3.2'
+          path: dubbo
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: "Build tools"
+        run: |
+          cd ./dubbo
+          ./mvnw --batch-mode -U -e --no-transfer-progress install -pl dubbo-build-tools -am -DskipTests=true
+  # Build from source code
+  build-source:
+    name: "Build Dubbo-SPI-Extensions"
+    needs: [check-license, build-tools]
+    runs-on: ubuntu-latest
+    # output dubbo-spi-extensions version to others jobs
+    outputs:
+      version: ${{ steps.dubbo-spi-extensions-version.outputs.version }}
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          path: dubbo-spi-extensions
+      - uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean compile -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+      - name: "Build Dubbo-SPI-Extensions with Maven"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper
+      - name: "Pack class result"
+        run: |
+          shopt -s globstar
+          zip ${{ github.workspace }}/class.zip **/target/classes/* -r
+      - name: "Upload class result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: "class-file"
+          path: ${{ github.workspace }}/class.zip
+      - name: "Pack checkstyle file if failure"
+        if: failure()
+        run: zip ${{ github.workspace }}/checkstyle.zip *checkstyle* -r
+      - name: "Upload checkstyle file if failure"
+        if: failure()
+        uses: actions/upload-artifact@v4
+        with:
+          name: "checkstyle-file"
+          path: ${{ github.workspace }}/checkstyle.zip
+      - name: "Upload coverage to Codecov"
+        uses: codecov/codecov-action@v4
+      - name: "Calculate Dubbo-SPI-Extensions Version"
+        id: dubbo-spi-extensions-version
+        run: |
+          REVISION=`awk '/<revision>[^<]+<\/revision>/{gsub(/<revision>|<\/revision>/,"",$1);print $1;exit;}' ./dubbo-spi-extensions/pom.xml`
+          echo "version=$REVISION" >> $GITHUB_OUTPUT
+          echo "dubbo-spi-extensions version: $REVISION"
+  # Download dependencies Prepare for unit test
+  unit-test-prepare:
+    name: "Preparation for Unit Test On ${{ matrix.os }}"
+    needs: [check-license]
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+      fail-fast: false
+    env:
+      ZOOKEEPER_VERSION: 3.6.3
+    steps:
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+      - name: "Set up msys2 if necessary"
+        if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        uses: msys2/setup-msys2@v2
+        with:
+          release: false  # support cache, see https://github.com/msys2/setup-msys2#context
+      - name: "Download zookeeper binary archive in Linux OS"
+        if: ${{ startsWith( matrix.os, 'ubuntu') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/zookeeper
+          wget -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.stu.edu.tw/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+          echo "list the downloaded zookeeper binary archive"
+          ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+      - name: "Download zookeeper binary archive in Windows OS"
+        if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        shell: msys2 {0}
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/zookeeper
+          wget -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.stu.edu.tw/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+          echo "list the downloaded zookeeper binary archive"
+          ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      - name: "Create Secret"
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/rsa
+          cd ${{ github.workspace }}/.tmp/rsa
+          openssl genrsa -out rsa_private.pem 1024
+          openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem
+          echo "Current workflow run id: ${{ github.run_id }}"
+          echo "Start Print Rsa Public Key ---"
+          cat rsa_public.pem
+          echo "--- End Print Rsa Public Key"
+  # Start unit test
+  unit-test:
+    needs: [check-license, build-source, unit-test-prepare]
+    name: "Unit Test ${{ matrix.os }} with JDK ${{ matrix.jdk }}"
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+        jdk: [ 8, 11 ]
+      fail-fast: false
+    env:
+      DISABLE_FILE_SYSTEM_TEST: true
+      CURRENT_ROLE: ${{ matrix.case-role }}
+      DUBBO_DEFAULT_SERIALIZATION: fastjson2
+    steps:
+      - name: Support Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: |
+          git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: "Set up JDK ${{ matrix.jdk }}"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      #      - name: "Get sonarcloud token"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        run: |
+      #          curl "http://dubbo-vm.apache.org:8000/token?workflow_id=${{ github.run_id }}" -o ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token
+      #          openssl rsautl -decrypt -in ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token -out ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token -inkey ${{ github.workspace }}/.tmp/rsa/rsa_private.pem
+      #      - name: "Test with Maven with SonarCloud Scan"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        timeout-minutes: 70
+      #        env:
+      #          # Needed to get some information about the pull request, if any
+      #          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      #        run: |
+      #          source ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token
+      #          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Pjacoco,jdk15ge-simple,'!jdk15ge',jacoco089 -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=apache_dubbo -DtrimStackTrace=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper -Dsonar.coverage.jacoco.xmlReportPaths=dubbo-test/dubbo-dependencies-all/target/site/jacoco-aggregate/jacoco.xml -Dsonar.login=${SONAR_TOKEN}
+      - name: "Test with Maven without SonarCloud Scan On Linux"
+        if: ${{ startsWith( matrix.os, 'linux') || startsWith( matrix.os, 'ubuntu') }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=false" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Test with Maven without SonarCloud Scan On Windows"
+        if: ${{ startsWith( matrix.os, 'windows')  }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Upload surefire-reports if failure"
+        if: ${{ failure() }}
+        uses: actions/upload-artifact@v4
+        with:
+          name: surefire-reports-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/surefire-reports/**"
+      - name: "Upload coverage result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: coverage-result-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/site/**/jacoco.xml"
+
+#  error-code-inspecting:
+#    needs: [check-license, build-tools]
+#    runs-on: ubuntu-latest
+#    steps:
+#      - uses: actions/checkout@v4
+#        with:
+#          path: "./dubbo-spi-extensions"
+#      - uses: actions/checkout@v4
+#        with:
+#          repository: 'apache/dubbo-test-tools'
+#          ref: main
+#          path: "./dubbo-test-tools"
+#      - name: "Set up JDK 21"
+#        uses: actions/setup-java@v4
+#        with:
+#          distribution: 'zulu'
+#          java-version: 21
+#      - name: Restore Maven local repository cache
+#        uses: actions/cache@v4
+#        with:
+#          path: ~/.m2/repository
+#          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#          restore-keys: |
+#            ${{ runner.os }}-maven-
+#            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-spi-extensions
+#          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+#      - name: "Run Error Code Inspecting"
+#        env:
+#          DUBBO_ECI_REPORT_AS_ERROR: true
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector
+#          ../mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo-spi-extensions
+#      - name: "Upload error code inspection result"
+#        # always() should not be used here, since we don't need to handle the 'canceled' situation.
+#        if: ${{ success() || failure() }}
+#        uses: actions/upload-artifact@v4
+#        with:
+#          name: "error-inspection-result"
+#          path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt
diff --git a/.github/workflows/build-and-test-scheduled-3.1.yml b/.github/workflows/build-and-test-scheduled-3.1.yml
new file mode 100644
index 0000000..ec264fd
--- /dev/null
+++ b/.github/workflows/build-and-test-scheduled-3.1.yml
@@ -0,0 +1,346 @@
+name: Build and Test Scheduled On 3.1
+
+on:
+  schedule:
+    - cron: '0 0/6 * * *'
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+env:
+  FORK_COUNT: 2
+  FAIL_FAST: 0
+  SHOW_ERROR_DETAIL: 1
+  #multi-version size limit
+  VERSIONS_LIMIT: 4
+  JACOCO_ENABLE: true
+  CANDIDATE_VERSIONS: '
+    spring.version:5.3.24;
+    spring-boot.version:2.7.6;
+    '
+
+jobs:
+  # Check ASF License
+  check-license:
+    name: "Check License"
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          ref: '3.1.0'
+      - name: Check License
+        uses: apache/skywalking-eyes@v0.5.0
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - name: Restore Maven local repository cache
+        uses: actions/cache@v4
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: Check Dependencies' License
+        uses: apache/skywalking-eyes/dependency@e1a02359b239bd28de3f6d35fdc870250fa513d5
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          config: .licenserc.yaml
+          mode: check
+  # Build dubbo-build-tool
+  build-tools:
+    name: "Build-tools"
+    needs: check-license
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+      fail-fast: false
+    outputs:
+      cache-key: ${{ steps.dubbo-build-tools.cache }}
+    steps:
+      - name: Support long paths on Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        name: build tools
+        with:
+          repository: 'apache/dubbo'
+          ref: '3.1'
+          path: dubbo
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: "Build tools"
+        run: |
+          cd ./dubbo
+          ./mvnw --batch-mode -U -e --no-transfer-progress install -pl dubbo-build-tools -am -DskipTests=true
+  # Build from source code
+  build-source:
+    name: "Build Dubbo-SPI-Extensions"
+    needs: [check-license, build-tools]
+    runs-on: ubuntu-latest
+    # output dubbo-spi-extensions version to others jobs
+    outputs:
+      version: ${{ steps.dubbo-spi-extensions-version.outputs.version }}
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          ref: "3.1.0"
+          path: dubbo-spi-extensions
+      - uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean compile -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+      - name: "Build Dubbo-SPI-Extensions with Maven"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper
+      - name: "Pack class result"
+        run: |
+          shopt -s globstar
+          zip ${{ github.workspace }}/class.zip **/target/classes/* -r
+      - name: "Upload class result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: "class-file"
+          path: ${{ github.workspace }}/class.zip
+      - name: "Pack checkstyle file if failure"
+        if: failure()
+        run: zip ${{ github.workspace }}/checkstyle.zip *checkstyle* -r
+      - name: "Upload checkstyle file if failure"
+        if: failure()
+        uses: actions/upload-artifact@v4
+        with:
+          name: "checkstyle-file"
+          path: ${{ github.workspace }}/checkstyle.zip
+      - name: "Upload coverage to Codecov"
+        uses: codecov/codecov-action@v4
+      - name: "Calculate Dubbo-SPI-Extensions Version"
+        id: dubbo-spi-extensions-version
+        run: |
+          REVISION=`awk '/<revision>[^<]+<\/revision>/{gsub(/<revision>|<\/revision>/,"",$1);print $1;exit;}' ./dubbo-spi-extensions/pom.xml`
+          echo "version=$REVISION" >> $GITHUB_OUTPUT
+          echo "dubbo-spi-extensions version: $REVISION"
+  # Download dependencies Prepare for unit test
+  unit-test-prepare:
+    name: "Preparation for Unit Test On ${{ matrix.os }}"
+    needs: [check-license]
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+      fail-fast: false
+    env:
+      ZOOKEEPER_VERSION: 3.6.3
+    steps:
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+      - name: "Set up msys2 if necessary"
+        if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        uses: msys2/setup-msys2@v2
+        with:
+          release: false  # support cache, see https://github.com/msys2/setup-msys2#context
+      - name: "Download zookeeper binary archive in Linux OS"
+        if: ${{ startsWith( matrix.os, 'ubuntu') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/zookeeper
+          wget -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.stu.edu.tw/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+          echo "list the downloaded zookeeper binary archive"
+          ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+      - name: "Download zookeeper binary archive in Windows OS"
+        if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        shell: msys2 {0}
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/zookeeper
+          wget -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.stu.edu.tw/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+          echo "list the downloaded zookeeper binary archive"
+          ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      - name: "Create Secret"
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/rsa
+          cd ${{ github.workspace }}/.tmp/rsa
+          openssl genrsa -out rsa_private.pem 1024
+          openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem
+          echo "Current workflow run id: ${{ github.run_id }}"
+          echo "Start Print Rsa Public Key ---"
+          cat rsa_public.pem
+          echo "--- End Print Rsa Public Key"
+  # Start unit test
+  unit-test:
+    needs: [check-license, build-source, unit-test-prepare]
+    name: "Unit Test ${{ matrix.os }} with JDK ${{ matrix.jdk }}"
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+        jdk: [ 8, 11 ]
+      fail-fast: false
+    env:
+      DISABLE_FILE_SYSTEM_TEST: true
+      CURRENT_ROLE: ${{ matrix.case-role }}
+      DUBBO_DEFAULT_SERIALIZATION: fastjson2
+    steps:
+      - name: Support Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: |
+          git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: "Set up JDK ${{ matrix.jdk }}"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      #      - name: "Get sonarcloud token"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        run: |
+      #          curl "http://dubbo-vm.apache.org:8000/token?workflow_id=${{ github.run_id }}" -o ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token
+      #          openssl rsautl -decrypt -in ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token -out ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token -inkey ${{ github.workspace }}/.tmp/rsa/rsa_private.pem
+      #      - name: "Test with Maven with SonarCloud Scan"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        timeout-minutes: 70
+      #        env:
+      #          # Needed to get some information about the pull request, if any
+      #          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      #        run: |
+      #          source ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token
+      #          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Pjacoco,jdk15ge-simple,'!jdk15ge',jacoco089 -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=apache_dubbo -DtrimStackTrace=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper -Dsonar.coverage.jacoco.xmlReportPaths=dubbo-test/dubbo-dependencies-all/target/site/jacoco-aggregate/jacoco.xml -Dsonar.login=${SONAR_TOKEN}
+      - name: "Test with Maven without SonarCloud Scan On Linux"
+        if: ${{ startsWith( matrix.os, 'linux') || startsWith( matrix.os, 'ubuntu') }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=false" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Test with Maven without SonarCloud Scan On Windows"
+        if: ${{ startsWith( matrix.os, 'windows')  }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Upload surefire-reports if failure"
+        if: ${{ failure() }}
+        uses: actions/upload-artifact@v4
+        with:
+          name: surefire-reports-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/surefire-reports/**"
+      - name: "Upload coverage result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: coverage-result-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/site/**/jacoco.xml"
+
+#  error-code-inspecting:
+#    needs: [check-license, build-tools]
+#    runs-on: ubuntu-latest
+#    steps:
+#      - uses: actions/checkout@v4
+#        with:
+#          path: "./dubbo-spi-extensions"
+#      - uses: actions/checkout@v4
+#        with:
+#          repository: 'apache/dubbo-test-tools'
+#          ref: main
+#          path: "./dubbo-test-tools"
+#      - name: "Set up JDK 21"
+#        uses: actions/setup-java@v4
+#        with:
+#          distribution: 'zulu'
+#          java-version: 21
+#      - name: Restore Maven local repository cache
+#        uses: actions/cache@v4
+#        with:
+#          path: ~/.m2/repository
+#          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#          restore-keys: |
+#            ${{ runner.os }}-maven-
+#            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-spi-extensions
+#          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+#      - name: "Run Error Code Inspecting"
+#        env:
+#          DUBBO_ECI_REPORT_AS_ERROR: true
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector
+#          ../mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo-spi-extensions
+#      - name: "Upload error code inspection result"
+#        # always() should not be used here, since we don't need to handle the 'canceled' situation.
+#        if: ${{ success() || failure() }}
+#        uses: actions/upload-artifact@v4
+#        with:
+#          name: "error-inspection-result"
+#          path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt
diff --git a/.github/workflows/build-and-test-scheduled-3.2.yml b/.github/workflows/build-and-test-scheduled-3.2.yml
new file mode 100644
index 0000000..3879c8b
--- /dev/null
+++ b/.github/workflows/build-and-test-scheduled-3.2.yml
@@ -0,0 +1,346 @@
+name: Build and Test Scheduled On 3.2
+
+on:
+  schedule:
+    - cron: '0 0/6 * * *'
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+env:
+  FORK_COUNT: 2
+  FAIL_FAST: 0
+  SHOW_ERROR_DETAIL: 1
+  #multi-version size limit
+  VERSIONS_LIMIT: 4
+  JACOCO_ENABLE: true
+  CANDIDATE_VERSIONS: '
+    spring.version:5.3.24;
+    spring-boot.version:2.7.6;
+    '
+
+jobs:
+  # Check ASF License
+  check-license:
+    name: "Check License"
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          ref: '3.2.0'
+      - name: Check License
+        uses: apache/skywalking-eyes@v0.5.0
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - name: Restore Maven local repository cache
+        uses: actions/cache@v4
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: Check Dependencies' License
+        uses: apache/skywalking-eyes/dependency@e1a02359b239bd28de3f6d35fdc870250fa513d5
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          config: .licenserc.yaml
+          mode: check
+  # Build dubbo-build-tool
+  build-tools:
+    name: "Build-tools"
+    needs: check-license
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+      fail-fast: false
+    outputs:
+      cache-key: ${{ steps.dubbo-build-tools.cache }}
+    steps:
+      - name: Support long paths on Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        name: build tools
+        with:
+          repository: 'apache/dubbo'
+          ref: '3.2'
+          path: dubbo
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: "Build tools"
+        run: |
+          cd ./dubbo
+          ./mvnw --batch-mode -U -e --no-transfer-progress install -pl dubbo-build-tools -am -DskipTests=true
+  # Build from source code
+  build-source:
+    name: "Build Dubbo-SPI-Extensions"
+    needs: [check-license, build-tools]
+    runs-on: ubuntu-latest
+    # output dubbo-spi-extensions version to others jobs
+    outputs:
+      version: ${{ steps.dubbo-spi-extensions-version.outputs.version }}
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          ref: "3.2.0"
+          path: dubbo-spi-extensions
+      - uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean compile -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+      - name: "Build Dubbo-SPI-Extensions with Maven"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper
+      - name: "Pack class result"
+        run: |
+          shopt -s globstar
+          zip ${{ github.workspace }}/class.zip **/target/classes/* -r
+      - name: "Upload class result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: "class-file"
+          path: ${{ github.workspace }}/class.zip
+      - name: "Pack checkstyle file if failure"
+        if: failure()
+        run: zip ${{ github.workspace }}/checkstyle.zip *checkstyle* -r
+      - name: "Upload checkstyle file if failure"
+        if: failure()
+        uses: actions/upload-artifact@v4
+        with:
+          name: "checkstyle-file"
+          path: ${{ github.workspace }}/checkstyle.zip
+      - name: "Upload coverage to Codecov"
+        uses: codecov/codecov-action@v4
+      - name: "Calculate Dubbo-SPI-Extensions Version"
+        id: dubbo-spi-extensions-version
+        run: |
+          REVISION=`awk '/<revision>[^<]+<\/revision>/{gsub(/<revision>|<\/revision>/,"",$1);print $1;exit;}' ./dubbo-spi-extensions/pom.xml`
+          echo "version=$REVISION" >> $GITHUB_OUTPUT
+          echo "dubbo-spi-extensions version: $REVISION"
+  # Download dependencies Prepare for unit test
+  unit-test-prepare:
+    name: "Preparation for Unit Test On ${{ matrix.os }}"
+    needs: [check-license]
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+      fail-fast: false
+    env:
+      ZOOKEEPER_VERSION: 3.6.3
+    steps:
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+      - name: "Set up msys2 if necessary"
+        if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        uses: msys2/setup-msys2@v2
+        with:
+          release: false  # support cache, see https://github.com/msys2/setup-msys2#context
+      - name: "Download zookeeper binary archive in Linux OS"
+        if: ${{ startsWith( matrix.os, 'ubuntu') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/zookeeper
+          wget -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.stu.edu.tw/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+          echo "list the downloaded zookeeper binary archive"
+          ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+      - name: "Download zookeeper binary archive in Windows OS"
+        if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        shell: msys2 {0}
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/zookeeper
+          wget -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.stu.edu.tw/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+          echo "list the downloaded zookeeper binary archive"
+          ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      - name: "Create Secret"
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/rsa
+          cd ${{ github.workspace }}/.tmp/rsa
+          openssl genrsa -out rsa_private.pem 1024
+          openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem
+          echo "Current workflow run id: ${{ github.run_id }}"
+          echo "Start Print Rsa Public Key ---"
+          cat rsa_public.pem
+          echo "--- End Print Rsa Public Key"
+  # Start unit test
+  unit-test:
+    needs: [check-license, build-source, unit-test-prepare]
+    name: "Unit Test ${{ matrix.os }} with JDK ${{ matrix.jdk }}"
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+        jdk: [ 8, 11 ]
+      fail-fast: false
+    env:
+      DISABLE_FILE_SYSTEM_TEST: true
+      CURRENT_ROLE: ${{ matrix.case-role }}
+      DUBBO_DEFAULT_SERIALIZATION: fastjson2
+    steps:
+      - name: Support Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: |
+          git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: "Set up JDK ${{ matrix.jdk }}"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      #      - name: "Get sonarcloud token"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        run: |
+      #          curl "http://dubbo-vm.apache.org:8000/token?workflow_id=${{ github.run_id }}" -o ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token
+      #          openssl rsautl -decrypt -in ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token -out ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token -inkey ${{ github.workspace }}/.tmp/rsa/rsa_private.pem
+      #      - name: "Test with Maven with SonarCloud Scan"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        timeout-minutes: 70
+      #        env:
+      #          # Needed to get some information about the pull request, if any
+      #          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      #        run: |
+      #          source ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token
+      #          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Pjacoco,jdk15ge-simple,'!jdk15ge',jacoco089 -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=apache_dubbo -DtrimStackTrace=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper -Dsonar.coverage.jacoco.xmlReportPaths=dubbo-test/dubbo-dependencies-all/target/site/jacoco-aggregate/jacoco.xml -Dsonar.login=${SONAR_TOKEN}
+      - name: "Test with Maven without SonarCloud Scan On Linux"
+        if: ${{ startsWith( matrix.os, 'linux') || startsWith( matrix.os, 'ubuntu') }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=false" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Test with Maven without SonarCloud Scan On Windows"
+        if: ${{ startsWith( matrix.os, 'windows')  }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Upload surefire-reports if failure"
+        if: ${{ failure() }}
+        uses: actions/upload-artifact@v4
+        with:
+          name: surefire-reports-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/surefire-reports/**"
+      - name: "Upload coverage result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: coverage-result-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/site/**/jacoco.xml"
+
+#  error-code-inspecting:
+#    needs: [check-license, build-tools]
+#    runs-on: ubuntu-latest
+#    steps:
+#      - uses: actions/checkout@v4
+#        with:
+#          path: "./dubbo-spi-extensions"
+#      - uses: actions/checkout@v4
+#        with:
+#          repository: 'apache/dubbo-test-tools'
+#          ref: main
+#          path: "./dubbo-test-tools"
+#      - name: "Set up JDK 21"
+#        uses: actions/setup-java@v4
+#        with:
+#          distribution: 'zulu'
+#          java-version: 21
+#      - name: Restore Maven local repository cache
+#        uses: actions/cache@v4
+#        with:
+#          path: ~/.m2/repository
+#          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#          restore-keys: |
+#            ${{ runner.os }}-maven-
+#            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-spi-extensions
+#          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+#      - name: "Run Error Code Inspecting"
+#        env:
+#          DUBBO_ECI_REPORT_AS_ERROR: true
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector
+#          ../mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo-spi-extensions
+#      - name: "Upload error code inspection result"
+#        # always() should not be used here, since we don't need to handle the 'canceled' situation.
+#        if: ${{ success() || failure() }}
+#        uses: actions/upload-artifact@v4
+#        with:
+#          name: "error-inspection-result"
+#          path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt
diff --git a/.github/workflows/build-and-test-scheduled-main.yml b/.github/workflows/build-and-test-scheduled-main.yml
new file mode 100644
index 0000000..47ad3ca
--- /dev/null
+++ b/.github/workflows/build-and-test-scheduled-main.yml
@@ -0,0 +1,331 @@
+name: Build and Test Scheduled On master
+
+on:
+  schedule:
+    - cron: '0 0/6 * * *'
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+env:
+  FORK_COUNT: 2
+  FAIL_FAST: 0
+  SHOW_ERROR_DETAIL: 1
+  #multi-version size limit
+  VERSIONS_LIMIT: 4
+  JACOCO_ENABLE: true
+  CANDIDATE_VERSIONS: '
+    spring.version:5.3.24;
+    spring-boot.version:2.7.6;
+    '
+
+jobs:
+  # Check ASF License
+  check-license:
+    name: "Check License"
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          ref: 'master'
+      - name: Check License
+        uses: apache/skywalking-eyes@v0.5.0
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - name: Restore Maven local repository cache
+        uses: actions/cache@v4
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: Check Dependencies' License
+        uses: apache/skywalking-eyes/dependency@e1a02359b239bd28de3f6d35fdc870250fa513d5
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          config: .licenserc.yaml
+          mode: check
+  # Build dubbo-build-tool
+  build-tools:
+    name: "Build-tools"
+    needs: check-license
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+      fail-fast: false
+    outputs:
+      cache-key: ${{ steps.dubbo-build-tools.cache }}
+    steps:
+      - name: Support long paths on Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        name: build tools
+        with:
+          repository: 'apache/dubbo'
+          ref: '3.2'
+          path: dubbo
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: "Build tools"
+        run: |
+          cd ./dubbo
+          ./mvnw --batch-mode -U -e --no-transfer-progress install -pl dubbo-build-tools -am -DskipTests=true
+  # Build from source code
+  build-source:
+    name: "Build Dubbo-SPI-Extensions"
+    needs: [check-license, build-tools]
+    runs-on: ubuntu-latest
+    # output dubbo-spi-extensions version to others jobs
+    outputs:
+      version: ${{ steps.dubbo-spi-extensions-version.outputs.version }}
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          ref: "master"
+          path: dubbo-spi-extensions
+      - uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean compile -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+      - name: "Build Dubbo-SPI-Extensions with Maven"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper
+      - name: "Pack class result"
+        run: |
+          shopt -s globstar
+          zip ${{ github.workspace }}/class.zip **/target/classes/* -r
+      - name: "Upload class result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: "class-file"
+          path: ${{ github.workspace }}/class.zip
+      - name: "Pack checkstyle file if failure"
+        if: failure()
+        run: zip ${{ github.workspace }}/checkstyle.zip *checkstyle* -r
+      - name: "Upload checkstyle file if failure"
+        if: failure()
+        uses: actions/upload-artifact@v4
+        with:
+          name: "checkstyle-file"
+          path: ${{ github.workspace }}/checkstyle.zip
+      - name: "Upload coverage to Codecov"
+        uses: codecov/codecov-action@v4
+      - name: "Calculate Dubbo-SPI-Extensions Version"
+        id: dubbo-spi-extensions-version
+        run: |
+          REVISION=`awk '/<revision>[^<]+<\/revision>/{gsub(/<revision>|<\/revision>/,"",$1);print $1;exit;}' ./dubbo-spi-extensions/pom.xml`
+          echo "version=$REVISION" >> $GITHUB_OUTPUT
+          echo "dubbo-spi-extensions version: $REVISION"
+  # Download dependencies Prepare for unit test
+  unit-test-prepare:
+    name: "Preparation for Unit Test"
+    needs: [check-license]
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+    env:
+      ZOOKEEPER_VERSION: 3.6.3
+    steps:
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+      - name: "Set up msys2 if necessary"
+        if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        uses: msys2/setup-msys2@v2
+        with:
+          release: false  # support cache, see https://github.com/msys2/setup-msys2#context
+      - name: "Download zookeeper binary archive in Linux OS"
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/zookeeper
+          wget -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.stu.edu.tw/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+          echo "list the downloaded zookeeper binary archive"
+          ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      - name: "Create Secret"
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/rsa
+          cd ${{ github.workspace }}/.tmp/rsa
+          openssl genrsa -out rsa_private.pem 1024
+          openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem
+          echo "Current workflow run id: ${{ github.run_id }}"
+          echo "Start Print Rsa Public Key ---"
+          cat rsa_public.pem
+          echo "--- End Print Rsa Public Key"
+  # Start unit test
+  unit-test:
+    needs: [check-license, build-source, unit-test-prepare]
+    name: "Unit Test ${{ matrix.os }} with JDK ${{ matrix.jdk }}"
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+        jdk: [ 8, 11 ]
+      fail-fast: false
+    env:
+      DISABLE_FILE_SYSTEM_TEST: true
+      CURRENT_ROLE: ${{ matrix.case-role }}
+      DUBBO_DEFAULT_SERIALIZATION: fastjson2
+    steps:
+      - name: Support Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: |
+          git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: "Set up JDK ${{ matrix.jdk }}"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      #      - name: "Get sonarcloud token"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        run: |
+      #          curl "http://dubbo-vm.apache.org:8000/token?workflow_id=${{ github.run_id }}" -o ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token
+      #          openssl rsautl -decrypt -in ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token -out ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token -inkey ${{ github.workspace }}/.tmp/rsa/rsa_private.pem
+      #      - name: "Test with Maven with SonarCloud Scan"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        timeout-minutes: 70
+      #        env:
+      #          # Needed to get some information about the pull request, if any
+      #          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      #        run: |
+      #          source ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token
+      #          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Pjacoco,jdk15ge-simple,'!jdk15ge',jacoco089 -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=apache_dubbo -DtrimStackTrace=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper -Dsonar.coverage.jacoco.xmlReportPaths=dubbo-test/dubbo-dependencies-all/target/site/jacoco-aggregate/jacoco.xml -Dsonar.login=${SONAR_TOKEN}
+      - name: "Test with Maven without SonarCloud Scan On Linux"
+        if: ${{ startsWith( matrix.os, 'linux') || startsWith( matrix.os, 'ubuntu') }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=false" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Test with Maven without SonarCloud Scan On Windows"
+        if: ${{ startsWith( matrix.os, 'windows')  }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Upload surefire-reports if failure"
+        if: ${{ failure() }}
+        uses: actions/upload-artifact@v4
+        with:
+          name: surefire-reports-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/surefire-reports/**"
+      - name: "Upload coverage result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: coverage-result-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/site/**/jacoco.xml"
+
+#  error-code-inspecting:
+#    needs: [check-license, build-tools]
+#    runs-on: ubuntu-latest
+#    steps:
+#      - uses: actions/checkout@v4
+#        with:
+#          path: "./dubbo-spi-extensions"
+#      - uses: actions/checkout@v4
+#        with:
+#          repository: 'apache/dubbo-test-tools'
+#          ref: main
+#          path: "./dubbo-test-tools"
+#      - name: "Set up JDK 21"
+#        uses: actions/setup-java@v4
+#        with:
+#          distribution: 'zulu'
+#          java-version: 21
+#      - name: Restore Maven local repository cache
+#        uses: actions/cache@v4
+#        with:
+#          path: ~/.m2/repository
+#          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#          restore-keys: |
+#            ${{ runner.os }}-maven-
+#            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-spi-extensions
+#          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+#      - name: "Run Error Code Inspecting"
+#        env:
+#          DUBBO_ECI_REPORT_AS_ERROR: true
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector
+#          ../mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo-spi-extensions
+#      - name: "Upload error code inspection result"
+#        # always() should not be used here, since we don't need to handle the 'canceled' situation.
+#        if: ${{ success() || failure() }}
+#        uses: actions/upload-artifact@v4
+#        with:
+#          name: "error-inspection-result"
+#          path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index e881190..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,68 +0,0 @@
-name: CI
-
-on: [ push, pull_request ]
-
-jobs:
-  build:
-    runs-on: ${{ matrix.os }}
-    strategy:
-      fail-fast: false
-      matrix:
-        os: [ ubuntu-latest]
-        jdk: [ 8, 11 ]
-    steps:
-      - uses: actions/checkout@v2
-        with:
-          path: dubbo-spi-extensions
-      - uses: actions/checkout@v2
-        with:
-          repository: 'apache/dubbo'
-          ref: '3.0'
-          path: dubbo
-      - name: "Set up JDK ${{ matrix.jdk }}"
-        uses: actions/setup-java@v1
-        with:
-          java-version: ${{ matrix.jdk }}
-      - uses: actions/cache@v2
-        name: "Cache local Maven repository"
-        with:
-          path: ~/.m2/repository
-          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
-          restore-keys: |
-            ${{ runner.os }}-maven-
-      - name: "Build tools"
-        run: |
-          cd ./dubbo
-          ./mvnw --batch-mode -U -e --no-transfer-progress install -pl dubbo-build-tools -am -DskipTests=true
-      - name: "Test with Maven"
-        timeout-minutes: 40
-        if: ${{ startsWith( matrix.os, 'ubuntu') }}
-        run: |
-          cd ./dubbo-spi-extensions
-          ./mvnw --batch-mode -U -e --no-transfer-progress clean test verify -Pjacoco -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true
-      - name: "Test with Maven"
-        timeout-minutes: 50
-        if: ${{ startsWith( matrix.os) }}
-        run: |
-          cd ./dubbo-spi-extensions
-          ./mvnw --batch-mode -U -e --no-transfer-progress clean test verify -Pjacoco -D"http.keepAlive=false" -D"maven.wagon.http.pool=false" -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=true -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true"
-      - name: "Pack rat file if failure"
-        if: failure()
-        run: 7z a ${{ github.workspace }}/rat.zip *rat.txt -r
-      - name: "Upload rat file if failure"
-        if: failure()
-        uses: actions/upload-artifact@v2
-        with:
-          name: "rat-file"
-          path: ${{ github.workspace }}/rat.zip
-      - name: "Pack checkstyle file if failure"
-        if: failure()
-        run: 7z a ${{ github.workspace }}/checkstyle.zip *checkstyle* -r
-      - name: "Upload checkstyle file if failure"
-        if: failure()
-        uses: actions/upload-artifact@v2
-        with:
-          name: "checkstyle-file"
-          path: ${{ github.workspace }}/checkstyle.zip
-      - name: "Upload coverage to Codecov"
-        uses: codecov/codecov-action@v1
diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml
index e2a0861..48ec1b8 100644
--- a/.github/workflows/conformance.yml
+++ b/.github/workflows/conformance.yml
@@ -23,19 +23,20 @@
     name: "Build Extensions"
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v4
         with:
           path: dubbo-spi-extensions
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v4
         with:
           repository: 'apache/dubbo'
           ref: '3.0'
           path: dubbo
       - name: Set up JDK 8
-        uses: actions/setup-java@v1
+        uses: actions/setup-java@v4
         with:
+          distribution: 'zulu'
           java-version: 8
-      - uses: actions/cache@v2
+      - uses: actions/cache@v4
         name: "Cache local Maven repository"
         with:
           path: ~/.m2/repository
@@ -52,8 +53,8 @@
           ./mvnw --batch-mode -U -e --no-transfer-progress install -am -DskipTests=true
       - name: "Build Scenarios"
         run: |
-          cd ./dubbo-spi-extensions/test/scenarios
-          ../../mvnw --batch-mode -U -e --no-transfer-progress install -am -DskipTests=true
+          cd ./dubbo-spi-extensions/test
+          ../mvnw --batch-mode -U -e --no-transfer-progress install -am -DskipTests=true
 
   prepare_test:
     name: "Prepare Test"
@@ -62,12 +63,12 @@
       #'JOB_COUNT' MUST match 'job_id' list of 'testjob'
       JOB_COUNT: 3
     steps:
-      - uses: actions/checkout@v1
+      - uses: actions/checkout@v4
       - name: Prepare test list
         run: |
           bash ./test/scripts/prepare-test.sh
       - name: Upload test list
-        uses: actions/upload-artifact@v2
+        uses: actions/upload-artifact@v4
         with:
           name: test-list
           path: test/jobs
@@ -81,24 +82,26 @@
     strategy:
       fail-fast: false
       matrix:
-        java: [ 8,11 ]
+        # use the unsafe only run on the jdk8
+        java: [ 8 ]
         #testjob id list MUST match 'JOB_COUNT' of 'prepare_test'
         job_id: [ 1,2,3 ]
     steps:
-      - uses: actions/checkout@v1
+      - uses: actions/checkout@v4
       - name: Set up JDK ${{matrix.java}}
-        uses: actions/setup-java@v1
+        uses: actions/setup-java@v4
         with:
+          distribution: 'zulu'
           java-version: ${{matrix.java}}
       - name: Cache local Maven repository
-        uses: actions/cache@v2
+        uses: actions/cache@v4
         with:
           path: ~/.m2/repository
           key: ${{ runner.os }}-extensions-maven${{ hashFiles('**/pom.xml') }}
           restore-keys: |
             ${{ runner.os }}-extensions-maven
       - name: Download test list
-        uses: actions/download-artifact@v2
+        uses: actions/download-artifact@v4
         with:
           name: test-list
           path: test/jobs/
@@ -107,30 +110,59 @@
           cd test && bash ./build-test-image.sh
       - name: Run tests
         run: cd test && bash ./run-tests.sh
+      - name: Upload log if test failed
+        if: failure()
+        uses: actions/upload-artifact@v4
+        with:
+          name: test-log-${{matrix.java}}-${{matrix.job_id}}
+          path: "**/test/scenarios/**/logs/*"
       - name: Upload test result
         if: always()
-        uses: actions/upload-artifact@v2
+        uses: actions/upload-artifact@v4
         with:
-          name: test-result
+          name: test-result-${{matrix.job_id}}
           path: test/jobs/*-result*
 
-  test_result:
+  merge_test:
     needs: [ testjob ]
-    name: 'Test Result (Java${{matrix.java}})'
-    if: always()
+    name: 'Merge Test Result (Java${{matrix.java}})'
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        java: [ 8,11]
+        java: [ 8 ]
     env:
       JAVA_VER: ${{matrix.java}}
     steps:
-      - uses: actions/checkout@v2
-      - name: Download test result
-        uses: actions/download-artifact@v2
+      - name: Merge Artifacts
+        uses: actions/upload-artifact/merge@v4
         with:
-          name: test-result
-          path: test/jobs/
-      - name: Merge test result - java ${{matrix.java}}
-        run: ./test/scripts/merge-test-results.sh
+          name: Merge-test-result-Java${{matrix.java}}
+          separate-directories: true
+          pattern: test-result-*
+          delete-merged: true
+
+#  test_result:
+#    needs: [ testjob ]
+#    name: 'Test Result (Java${{matrix.java}})'
+#    if: always()
+#    runs-on: ubuntu-latest
+#    strategy:
+#      fail-fast: false
+#      matrix:
+#        java: [ 8 ]
+#    env:
+#      JAVA_VER: ${{matrix.java}}
+#    steps:
+#      - uses: actions/checkout@v4
+#      - name: Download test result
+#        uses: actions/download-artifact@v4
+#        with:
+#          path: test/jobs/
+#      - name: Merge test result - java ${{matrix.java}}
+#        run: ./test/scripts/merge-test-results.sh
+#      - name: Upload merge test result
+#        uses: actions/upload-artifact@v4
+#        with:
+#          name: merge-test-result-${{matrix.java}}
+#          path: test/jobs/*-result*
diff --git a/.github/workflows/release-test.yml b/.github/workflows/release-test.yml
new file mode 100644
index 0000000..0905728
--- /dev/null
+++ b/.github/workflows/release-test.yml
@@ -0,0 +1,346 @@
+name: Release Test
+
+on:
+  push:
+    branches:
+      - '**-release'
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+env:
+  FORK_COUNT: 2
+  FAIL_FAST: 0
+  SHOW_ERROR_DETAIL: 1
+  #multi-version size limit
+  VERSIONS_LIMIT: 4
+  JACOCO_ENABLE: true
+  CANDIDATE_VERSIONS: '
+    spring.version:5.3.24;
+    spring-boot.version:2.7.6;
+    '
+
+jobs:
+  # Check ASF License
+  check-license:
+    name: "Check License"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Check License
+        uses: apache/skywalking-eyes@v0.5.0
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - name: Restore Maven local repository cache
+        uses: actions/cache@v4
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - name: Check Dependencies' License
+        uses: apache/skywalking-eyes/dependency@e1a02359b239bd28de3f6d35fdc870250fa513d5
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          config: .licenserc.yaml
+          mode: check
+  # Build dubbo-build-tool
+  build-tools:
+    name: "Build-tools"
+    needs: check-license
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+      fail-fast: false
+    outputs:
+      cache-key: ${{ steps.dubbo-build-tools.cache }}
+    steps:
+      - name: Support long paths on Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        name: build tools
+        with:
+          repository: 'apache/dubbo'
+          ref: '3.2'
+          path: dubbo
+      - name: "Set up JDK 21"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 21
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+          restore-keys: |
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+            ${{ runner.os }}-maven-
+      - name: "Build tools"
+        run: |
+          cd ./dubbo
+          ./mvnw --batch-mode -U -e --no-transfer-progress install -pl dubbo-build-tools -am -DskipTests=true
+  # Build from source code
+  build-source:
+    name: "Build Dubbo-SPI-Extensions"
+    needs: [check-license]
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+    # output dubbo-spi-extensions version to others jobs
+    outputs:
+      version: ${{ steps.dubbo-spi-extensions-version.outputs.version }}
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          path: dubbo-spi-extensions
+      - uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+            ${{ runner.os }}-maven-
+      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean compile -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+      - name: "Build Dubbo-SPI-Extensions with Maven"
+        run: |
+          cd ./dubbo-spi-extensions
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean source:jar install -Pjacoco,checkstyle -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper
+      - name: "Pack class result"
+        run: |
+          shopt -s globstar
+          zip ${{ github.workspace }}/class.zip **/target/classes/* -r
+      - name: "Upload class result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: "class-file"
+          path: ${{ github.workspace }}/class.zip
+      - name: "Pack checkstyle file if failure"
+        if: failure()
+        run: zip ${{ github.workspace }}/checkstyle.zip *checkstyle* -r
+      - name: "Upload checkstyle file if failure"
+        if: failure()
+        uses: actions/upload-artifact@v4
+        with:
+          name: "checkstyle-file"
+          path: ${{ github.workspace }}/checkstyle.zip
+      - name: "Upload coverage to Codecov"
+        uses: codecov/codecov-action@v4
+      - name: "Calculate Dubbo-SPI-Extensions Version"
+        id: dubbo-spi-extensions-version
+        run: |
+          REVISION=`awk '/<revision>[^<]+<\/revision>/{gsub(/<revision>|<\/revision>/,"",$1);print $1;exit;}' ./dubbo-spi-extensions/pom.xml`
+          echo "version=$REVISION" >> $GITHUB_OUTPUT
+          echo "dubbo-spi-extensions version: $REVISION"
+  # Download dependencies Prepare for unit test
+  unit-test-prepare:
+    name: "Preparation for Unit Test On ${{ matrix.os }}"
+    needs: [check-license]
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+      fail-fast: false
+    env:
+      ZOOKEEPER_VERSION: 3.6.3
+    steps:
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+      - name: "Set up msys2 if necessary"
+        if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        uses: msys2/setup-msys2@v2
+        with:
+          release: false  # support cache, see https://github.com/msys2/setup-msys2#context
+      - name: "Download zookeeper binary archive in Linux OS"
+        if: ${{ startsWith( matrix.os, 'ubuntu') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/zookeeper
+          wget -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.stu.edu.tw/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+          echo "list the downloaded zookeeper binary archive"
+          ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+      - name: "Download zookeeper binary archive in Windows OS"
+        if: ${{ startsWith( matrix.os, 'windows') && steps.cache-zookeeper.outputs.cache-hit != 'true' }}
+        shell: msys2 {0}
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/zookeeper
+          wget -c https://archive.apache.org/dist/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c https://apache.website-solution.net/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.stu.edu.tw/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://ftp.jaist.ac.jp/pub/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://apache.mirror.cdnetworks.com/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz ||
+          wget -c http://mirror.apache-kr.org/apache/zookeeper/zookeeper-${{ env.ZOOKEEPER_VERSION }}/apache-zookeeper-${{ env.ZOOKEEPER_VERSION }}-bin.tar.gz -O ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+          echo "list the downloaded zookeeper binary archive"
+          ls -al ${{ github.workspace }}/.tmp/zookeeper/apache-zookeeper-bin.tar.gz
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      - name: "Create Secret"
+        run: |
+          mkdir -p ${{ github.workspace }}/.tmp/rsa
+          cd ${{ github.workspace }}/.tmp/rsa
+          openssl genrsa -out rsa_private.pem 1024
+          openssl rsa -in rsa_private.pem -pubout -out rsa_public.pem
+          echo "Current workflow run id: ${{ github.run_id }}"
+          echo "Start Print Rsa Public Key ---"
+          cat rsa_public.pem
+          echo "--- End Print Rsa Public Key"
+  # Start unit test
+  unit-test:
+    needs: [check-license, build-source, unit-test-prepare]
+    name: "Unit Test ${{ matrix.os }} with JDK ${{ matrix.jdk }}"
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest, windows-latest ]
+        jdk: [ 8, 11 ]
+      fail-fast: false
+    env:
+      DISABLE_FILE_SYSTEM_TEST: true
+      CURRENT_ROLE: ${{ matrix.case-role }}
+      DUBBO_DEFAULT_SERIALIZATION: fastjson2
+    steps:
+      - name: Support Windows
+        if: ${{ startsWith( matrix.os, 'windows') }}
+        run: |
+          git config --system core.longpaths true
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: "Set up JDK ${{ matrix.jdk }}"
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'zulu'
+          java-version: 8
+      - uses: actions/cache@v4
+        name: "Cache local Maven repository"
+        with:
+          path: ~/.m2/repository
+          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}-${{ github.run_id }}
+          restore-keys: |
+            ${{ runner.os }}-maven-
+            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+      - uses: actions/cache@v4
+        name: "Cache zookeeper binary archive"
+        id: "cache-zookeeper"
+        with:
+          path: ${{ github.workspace }}/.tmp/zookeeper
+          key: zookeeper-${{ runner.os }}-${{ env.ZOOKEEPER_VERSION }}
+          restore-keys: |
+            zookeeper-${{ runner.os }}-
+      - name: "Upload surefire-reports if failure"
+        if: ${{ failure() }}
+        uses: actions/upload-artifact@v4
+        with:
+          name: surefire-reports-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/surefire-reports/**"
+      - uses: actions/cache@v4
+        name: "Cache secret key"
+        id: "cache-secret-cert"
+        with:
+          path: ${{ github.workspace }}/.tmp/rsa
+          key: secret-rsa-${{ runner.os }}-${{ github.run_id }}
+      #      - name: "Get sonarcloud token"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        run: |
+      #          curl "http://dubbo-vm.apache.org:8000/token?workflow_id=${{ github.run_id }}" -o ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token
+      #          openssl rsautl -decrypt -in ${{ github.workspace }}/.tmp/encrypted-sonarcloud-token -out ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token -inkey ${{ github.workspace }}/.tmp/rsa/rsa_private.pem
+      #      - name: "Test with Maven with SonarCloud Scan"
+      #        if: ${{ github.repository == 'apache/dubbo-spi-extensions' }}
+      #        timeout-minutes: 70
+      #        env:
+      #          # Needed to get some information about the pull request, if any
+      #          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      #        run: |
+      #          source ${{ github.workspace }}/.tmp/decrypted-sonarcloud-token
+      #          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Pjacoco,jdk15ge-simple,'!jdk15ge',jacoco089 -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=apache -Dsonar.projectKey=apache_dubbo -DtrimStackTrace=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 -Dmaven.wagon.http.retryHandler.count=5 -DskipTests=false -DskipIntegrationTests=false -Dcheckstyle.skip=false -Dcheckstyle_unix.skip=false -Drat.skip=false -Dmaven.javadoc.skip=true -DembeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper -Dsonar.coverage.jacoco.xmlReportPaths=dubbo-test/dubbo-dependencies-all/target/site/jacoco-aggregate/jacoco.xml -Dsonar.login=${SONAR_TOKEN}
+      - name: "Test with Maven without SonarCloud Scan On Linux"
+        if: ${{ startsWith( matrix.os, 'linux') || startsWith( matrix.os, 'ubuntu') }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=false" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Test with Maven without SonarCloud Scan On Windows"
+        if: ${{ startsWith( matrix.os, 'windows')  }}
+        timeout-minutes: 70
+        run: |
+          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast clean test verify -D"maven.wagon.httpconnectionManager.ttlSeconds=120" -Pjacoco -DtrimStackTrace=false -D"maven.wagon.http.retryHandler.count=5" -DskipTests=false -DskipIntegrationTests=false -D"checkstyle.skip=false" -D"checkstyle_unix.skip=true" -D"rat.skip=false" -D"maven.javadoc.skip=true" -D"embeddedZookeeperPath=${{ github.workspace }}/.tmp/zookeeper"
+      - name: "Upload coverage result"
+        uses: actions/upload-artifact@v4
+        with:
+          name: coverage-result-${{ matrix.os }}-${{ matrix.jdk }}
+          path: "**/target/site/**/jacoco.xml"
+
+#  error-code-inspecting:
+#    needs: [check-license, build-tools]
+#    runs-on: ubuntu-latest
+#    steps:
+#      - uses: actions/checkout@v4
+#        with:
+#          path: "./dubbo-spi-extensions"
+#      - uses: actions/checkout@v4
+#        with:
+#          repository: 'apache/dubbo-test-tools'
+#          ref: main
+#          path: "./dubbo-test-tools"
+#      - name: "Set up JDK 21"
+#        uses: actions/setup-java@v4
+#        with:
+#          distribution: 'zulu'
+#          java-version: 21
+#      - name: Restore Maven local repository cache
+#        uses: actions/cache@v4
+#        with:
+#          path: ~/.m2/repository
+#          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#          restore-keys: |
+#            ${{ runner.os }}-maven-
+#            ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+#      - name: "Compile Dubbo-SPI-Extensions (Linux)"
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-spi-extensions
+#          ./mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C clean install -DskipTests=true -DskipIntegrationTests=true -Dcheckstyle.skip=true -Dcheckstyle_unix.skip=true -Drat.skip=true -Dmaven.javadoc.skip=true
+#      - name: "Run Error Code Inspecting"
+#        env:
+#          DUBBO_ECI_REPORT_AS_ERROR: true
+#        run: |
+#          cd ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector
+#          ../mvnw --batch-mode --no-snapshot-updates -e --no-transfer-progress --fail-fast -T 2C package exec:java -Ddubbo.eci.report-as-error=${DUBBO_ECI_REPORT_AS_ERROR} -Dmaven.test.skip=true -Dmaven.test.skip.exec=true -Ddubbo.eci.path=${{ github.workspace }}/dubbo-spi-extensions
+#      - name: "Upload error code inspection result"
+#        # always() should not be used here, since we don't need to handle the 'canceled' situation.
+#        if: ${{ success() || failure() }}
+#        uses: actions/upload-artifact@v4
+#        with:
+#          name: "error-inspection-result"
+#          path: ${{ github.workspace }}/dubbo-test-tools/dubbo-error-code-inspector/error-inspection-result.txt
diff --git a/.gitignore b/.gitignore
index 4614666..1e091f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,3 +44,6 @@
 
 # test
 /test/jobs/
+
+# rust ignore
+*.lock
diff --git a/.licenserc.yaml b/.licenserc.yaml
new file mode 100644
index 0000000..87bad49
--- /dev/null
+++ b/.licenserc.yaml
@@ -0,0 +1,213 @@
+header:
+  license:
+    spdx-id: Apache-2.0
+    content: |
+      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.
+
+  paths-ignore:
+    - '**/*.versionsBackup'
+    - '**/.idea/'
+    - '**/*.iml'
+    - '**/.settings/*'
+    - '**/.classpath'
+    - '**/.project'
+    - '**/target/**'
+    - '**/generated/**'
+    - '**/*.log'
+    - '**/codestyle/*'
+    - '**/resources/META-INF/**'
+    - '**/resources/mockito-extensions/**'
+    - '**/*.proto'
+    - '**/*.cache'
+    - '**/*.txt'
+    - '**/*.load'
+    - '**/*.flex'
+    - '**/*.fc'
+    - '**/*.javascript'
+    - '**/*.properties'
+    - '**/*.sh'
+    - '**/*.bat'
+    - '**/*.md'
+    - '**/*.svg'
+    - '**/*.png'
+    - '**/*.json'
+    - '**/*.conf'
+    - '**/*.ftl'
+    - '**/*.tpl'
+    - '**/*.factories'
+    - '**/*.handlers'
+    - '**/*.schemas'
+    - '**/*.nojekyll'
+    - '.git/'
+    - '.github/**'
+    - '**/.gitignore'
+    - '**/.helmignore'
+    - '.repository/'
+    - 'compiler/**'
+    - '.gitmodules'
+    - '.mvn'
+    - 'mvnw'
+    - 'mvnw.cmd'
+    - 'LICENSE'
+    - 'NOTICE'
+    - 'CNAME'
+    - 'Jenkinsfile'
+    - '**/vendor/**'
+    - '**/src/test/resources/certs/**'
+    - '**/src/test/resources/definition/**'
+    # Generate class
+    - 'dubbo-rpc-extensions/dubbo-rpc-native-thrift/src/test/java/org/apache/dubbo/rpc/protocol/nativethrift/DemoService.java'
+    - 'dubbo-rpc-extensions/dubbo-rpc-native-thrift/src/test/java/org/apache/dubbo/rpc/protocol/nativethrift/UserService.java'
+    # other license
+    - 'test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-avro-test/src/main/java/org/apache/dubbo/test/serialization/avro/EmbeddedZooKeeper.java'
+    - 'test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fastjson-test/src/main/java/org/apache/dubbo/test/serialization/fastjson/EmbeddedZooKeeper.java'
+    - 'test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fst-test/src/main/java/org/apache/dubbo/test/serialization/fst/EmbeddedZooKeeper.java'
+    - 'test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-gson-test/src/main/java/org/apache/dubbo/test/serialization/gson/EmbeddedZooKeeper.java'
+    - 'test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-kryo-test/src/main/java/org/apache/dubbo/test/serialization/kryo/EmbeddedZooKeeper.java'
+    - 'test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protostuff-test/src/main/java/org/apache/dubbo/test/serialization/protostuff/EmbeddedZooKeeper.java'
+
+  comment: on-failure
+
+  license-location-threshold: 130
+
+dependency:
+  files:
+    - pom.xml
+    - dubbo-extensions-dependencies-bom/pom.xml
+  licenses:
+    - name: com.alibaba.spring:spring-context-support
+      license: Apache-2.0
+    - name: com.fasterxml.jackson.core:jackson-annotations
+      license: Apache-2.0
+    - name: com.fasterxml.jackson.core:jackson-core
+      license: Apache-2.0
+    - name: com.fasterxml.jackson.core:jackson-databind
+      license: Apache-2.0
+    - name: com.fasterxml.jackson.dataformat:jackson-dataformat-yaml
+      license: Apache-2.0
+    - name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310
+      license: Apache-2.0
+    - name: com.google.code.gson:gson
+      license: Apache-2.0
+    - name: com.google.guava:listenablefuture
+      license: Apache-2.0
+    - name: com.salesforce.servicelibs:grpc-contrib
+      license: BSD 3-clause
+    - name: com.squareup.okhttp3:logging-interceptor
+      license: Apache-2.0
+    - name: com.squareup.okhttp3:okhttp
+      license: Apache-2.0
+    - name: com.squareup.okio:okio
+      license: Apache-2.0
+    - name: com.sun.xml.fastinfoset:FastInfoset
+      license: Apache-2.0
+    - name: io.envoyproxy.controlplane:api
+      license: Apache-2.0
+    - name: io.swagger:swagger-annotations
+      license: Apache-2.0
+    - name: io.swagger:swagger-models
+      license: Apache-2.0
+    - name: org.springframework.boot:spring-boot
+      license: Apache-2.0
+    - name: org.springframework.boot:spring-boot-actuator
+      license: Apache-2.0
+    - name: org.springframework.boot:spring-boot-autoconfigure
+      license: Apache-2.0
+    - name: org.springframework.boot:spring-boot-configuration-processor
+      license: Apache-2.0
+    - name: org.springframework.boot:spring-boot-starter
+      license: Apache-2.0
+    - name: org.springframework.boot:spring-boot-starter-actuator
+      license: Apache-2.0
+    - name: org.springframework.boot:spring-boot-starter-logging
+      license: Apache-2.0
+    - name: org.springframework.boot:spring-boot-starter-tomcat
+      license: Apache-2.0
+    - name: org.springframework.boot:spring-boot-starter-web
+      license: Apache-2.0
+    - name: org.slf4j:slf4j-api
+      license: MIT
+    - name: org.slf4j:slf4j-log4j12
+      license: MIT
+    - name: org.jboss.resteasy:resteasy-jaxrs
+      license: Apache-2.0
+    - name: org.jboss.resteasy:resteasy-client
+      license: Apache-2.0
+    - name: org.jboss.resteasy:resteasy-netty4
+      license: Apache-2.0
+    - name: org.jboss.resteasy:resteasy-jdk-http
+      license: Apache-2.0
+    - name: org.jboss.resteasy:resteasy-jackson-provider
+      license: Apache-2.0
+    - name: org.jboss.resteasy:resteasy-jaxb-provider
+      license: Apache-2.0
+    - name: net.jcip:jcip-annotations
+      license: Apache-2.0
+    - name: org.apache.zookeeper:zookeeper
+      license: Apache-2.0
+    - name: org.apache.zookeeper:zookeeper-jute
+      license: Apache-2.0
+    - name: net.bytebuddy:byte-buddy
+      license: Apache-2.0
+    - name: javax.enterprise:cdi-api
+      license: Apache-2.0
+    - name: org.codehaus.plexus:plexus-component-annotations
+      license: Apache-2.0
+    - name: org.slf4j:jcl-over-slf4j
+      license: Apache-2.0
+    - name: org.slf4j:jul-to-slf4j
+      license: Apache-2.0
+    - name: org.codehaus.plexus:plexus-interpolation
+      license: Apache-2.0
+    - name: org.sonatype.plexus:plexus-sec-dispatcher
+      license: Apache-2.0
+    - name: org.sonatype.plexus:plexus-cipher
+      license: Apache-2.0
+    - name: com.google.protobuf:protobuf-java
+      license: BSD 3-clause
+    - name: com.google.protobuf:protobuf-java-util
+      license: BSD 3-clause
+    # multi license
+    - name: org.javassist:javassist
+      license: Apache-2.0
+    - name: javax.annotation:javax.annotation-api
+      license: CDDL-1.0
+    - name: com.salesforce.servicelibs:jprotoc
+      license: CDDL-1.0
+    - name: org.checkerframework:checker-compat-qual
+      license: MIT
+    - name: ch.qos.logback:logback-classic
+      license: EPL-1.0
+    - name: ch.qos.logback:logback-core
+      license: EPL-1.0
+    - name: javax.servlet:javax.servlet-api
+      license: CDDL-1.1
+    - name: com.sun.activation:javax.activation
+      license: CDDL-1.1
+    - name: javax.activation:activation
+      license: CDDL-1.1
+    - name: jakarta.annotation:jakarta.annotation-api
+      license: EPL-2.0
+    - name: org.glassfish:jakarta.el
+      license: EPL-2.0
+    - name: org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec
+      license: CDDL-1.1
+    - name: org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_2.1_spec
+      license: EPL-2.0
+    - name: org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec
+      license: EPL-2.0
+  excludes:
+    - name: javax.xml.bind:jsr173_api
diff --git a/NOTICE b/NOTICE
index 80fab2e..6eac862 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
 Apache Dubbo
-Copyright 2018-2022 The Apache Software Foundation
+Copyright 2018-2024 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/README.md b/README.md
index 3037d58..3b964e1 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@
 [![Maven Central](https://img.shields.io/maven-central/v/org.apache.dubbo/dubbo-spi-extensions.svg)](https://search.maven.org/search?q=g:org.apache.dubbo%20AND%20a:dubbo-spi-extensions)
 [![GitHub release](https://img.shields.io/github/release/apache/dubbo-spi-extensions.svg)]
 
+[中文](./README_CN.md)
+
 The purpose of dubbo-spi-extensions is to provide open, community-driven, reusable components to build microservice programs with different needs. These components extend the core of the Apache Dubbo project, but they are separated and decoupled.
 
 Developers can flexibly choose the required extension dependencies to develop microservice programs based on their needs. The available extensions are as follows:Developers can flexibly choose the required extension dependencies to develop microservice programs based on their needs. 
@@ -50,6 +52,8 @@
 - [dubbo-mock-extensions](dubbo-mock-extensions)
   - [dubbo-mock-admin](dubbo-mock-extensions/dubbo-mock-admin)
   - [dubbo-mock-api](dubbo-mock-extensions/dubbo-mock-api)
+- [dubbo-proxy-extensions](dubbo-proxy-extensions)
+  - [dubbo-proxy-bytebuddy](dubbo-proxy-extensions/dubbo-proxy-bytebuddy)
 - [dubbo-registry-extensions](dubbo-registry-extensions)
   - [dubbo-registry-consul](dubbo-registry-extensions/dubbo-registry-consul)
   - [dubbo-registry-dns](dubbo-registry-extensions/dubbo-registry-dns)
@@ -76,6 +80,7 @@
   - [dubbo-rpc-webservice](dubbo-rpc-extensions/dubbo-rpc-webservice)
 - [dubbo-serialization-extensions](dubbo-serialization-extensions)
   - [dubbo-serialization-avro](dubbo-serialization-extensions/dubbo-serialization-avro)
+  - [dubbo-serialization-common](dubbo-serialization-extensions/dubbo-serialization-common)
   - [dubbo-serialization-fastjson](dubbo-serialization-extensions/dubbo-serialization-fastjson)
   - [dubbo-serialization-fst](dubbo-serialization-extensions/dubbo-serialization-fst)
   - [dubbo-serialization-fury](dubbo-serialization-extensions/dubbo-serialization-fury)
@@ -89,6 +94,14 @@
   - [dubbo-serialization-test](dubbo-serialization-extensions/dubbo-serialization-test)
 - [dubbo-tag-extensions](dubbo-tag-extensions)
   - [dubbo-tag-subnets](dubbo-tag-extensions/dubbo-tag-subnets)
+- [dubbo-wasm](dubbo-wasm)
+  - [dubbo-wasm-api](dubbo-wasm/dubbo-wasm-api)
+  - [dubbo-wasm-cluster-api](dubbo-wasm/dubbo-wasm-cluster-api)
+  - [dubbo-wasm-common-api](dubbo-wasm/dubbo-wasm-common-api)
+  - [dubbo-wasm-registry-api](dubbo-wasm/dubbo-wasm-registry-api)
+  - [dubbo-wasm-remoting-api](dubbo-wasm/dubbo-wasm-remoting-api)
+  - [dubbo-wasm-rpc-api](dubbo-wasm/dubbo-wasm-rpc-api)
+  - [dubbo-wasm-test](dubbo-wasm/dubbo-wasm-test)
 - [dubbo-xds](dubbo-xds)
 
 ## Contribution
diff --git a/README_CN.md b/README_CN.md
index e69de29..e173e12 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -0,0 +1,117 @@
+# dubbo-spi-extensions
+[![Build Status](https://travis-ci.org/apache/dubbo-spi-extensions.svg?branch=master)](https://travis-ci.org/apache/dubbo-spi-extensions)
+[![codecov](https://codecov.io/gh/apache/dubbo-spi-extensions/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo-spi-extensions)
+[![Maven Central](https://img.shields.io/maven-central/v/org.apache.dubbo/dubbo-spi-extensions.svg)](https://search.maven.org/search?q=g:org.apache.dubbo%20AND%20a:dubbo-spi-extensions)
+[![GitHub release](https://img.shields.io/github/release/apache/dubbo-spi-extensions.svg)]
+
+[English](./README.md)
+
+dubbo-spi-extensions的目的是提供开放的、社区驱动的、可重用的组件,用于构建具有不同需求的微服务程序。这些组件扩展了Apache Dubbo项目的核心,但它们是分离的且解耦的。
+
+开发者可以根据自己的需求灵活选择所需的扩展依赖,开发基于微服务的程序。现有的扩展如下:开发者可以根据自己的需求灵活选择所需的扩展依赖,开发基于微服务的程序。
+
+有关版本发布说明,请参阅文档:
+- [Release](https://cn.dubbo.apache.org/zh-cn/download/spi-extensions/)
+- [Reference](https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/spi/overview/)
+
+现有的扩展如下:
+
+- [dubbo-api-docs](dubbo-api-docs)
+  - [dubbo-api-docs-annotations](dubbo-api-docs/dubbo-api-docs-annotations)
+  - [dubbo-api-docs-core](dubbo-api-docs/dubbo-api-docs-core)
+  - [dubbo-api-docs-examples](dubbo-api-docs/dubbo-api-docs-examples)
+- [dubbo-cluster-extensions](dubbo-cluster-extensions)
+  - [dubbo-cluster-broadcast-1](dubbo-cluster-extensions/dubbo-cluster-broadcast-1)
+  - [dubbo-cluster-loadbalance-peakewma](dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma)
+  - [dubbo-cluster-polaris-dubbo2](dubbo-cluster-extensions/dubbo-cluster-polaris-dubbo2)
+  - [dubbo-cluster-specify-address-common](dubbo-cluster-extensions/dubbo-cluster-specify-address-common)
+  - [dubbo-cluster-specify-address-dubbo2](dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo2)
+  - [dubbo-cluster-specify-address-dubbo3](dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo3)
+- [dubbo-common-extensions](dubbo-common-extensions)
+- [dubbo-configcenter-extensions](dubbo-configcenter-extensions)
+  - [dubbo-configcenter-consul](dubbo-configcenter-extensions/dubbo-configcenter-consul)
+  - [dubbo-configcenter-etcd](dubbo-configcenter-extensions/dubbo-configcenter-etcd)
+- [dubbo-cross-thread-extensions](dubbo-cross-thread-extensions)
+- [dubbo-extensions-dependencies-bom](dubbo-extensions-dependencies-bom)
+- [dubbo-extensions-distribution](dubbo-extensions-distribution)
+  - [dubbo-apache-release](dubbo-extensions-distribution/dubbo-apache-release)
+  - [dubbo-bom](dubbo-extensions-distribution/dubbo-bom)
+- [dubbo-filter-extensions](dubbo-filter-extensions)
+  - [dubbo-filter-polaris-dubbo2](dubbo-filter-extensions/dubbo-filter-polaris-dubbo2)
+    - [dubbo-filter-polaris-circuitbreaker-dubbo2](dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-circuitbreaker-dubbo2)
+    - [dubbo-filter-polaris-ratelimit-dubbo2](dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-ratelimit-dubbo2)
+  - [dubbo-filter-seata](dubbo-filter-extensions/dubbo-filter-seata)
+- [dubbo-gateway-extensions](dubbo-gateway-extensions)
+  - [dubbo-gateway-common](dubbo-gateway-extensions/dubbo-gateway-common)
+  - [dubbo-gateway-consumer](dubbo-gateway-extensions/dubbo-gateway-consumer)
+  - [dubbo-gateway-provider](dubbo-gateway-extensions/dubbo-gateway-provider)
+- [dubbo-kubernetes](dubbo-kubernetes)
+- [dubbo-metadata-report-extensions](dubbo-metadata-report-extensions)
+  - [dubbo-metadata-report-consul](dubbo-metadata-report-extensions/dubbo-metadata-report-consul)
+  - [dubbo-metadata-report-etcd](dubbo-metadata-report-extensions/dubbo-metadata-report-etcd)
+- [dubbo-mock-extensions](dubbo-mock-extensions)
+  - [dubbo-mock-admin](dubbo-mock-extensions/dubbo-mock-admin)
+  - [dubbo-mock-api](dubbo-mock-extensions/dubbo-mock-api)
+- [dubbo-proxy-extensions](dubbo-proxy-extensions)
+  - [dubbo-proxy-bytebuddy](dubbo-proxy-extensions/dubbo-proxy-bytebuddy)
+- [dubbo-registry-extensions](dubbo-registry-extensions)
+  - [dubbo-registry-consul](dubbo-registry-extensions/dubbo-registry-consul)
+  - [dubbo-registry-dns](dubbo-registry-extensions/dubbo-registry-dns)
+  - [dubbo-registry-etcd3](dubbo-registry-extensions/dubbo-registry-etcd3)
+  - [dubbo-registry-nameservice](dubbo-registry-extensions/dubbo-registry-nameservice)
+  - [dubbo-registry-polaris](dubbo-registry-extensions/dubbo-registry-polaris)
+  - [dubbo-registry-redis](dubbo-registry-extensions/dubbo-registry-redis)
+  - [dubbo-registry-sofa](dubbo-registry-extensions/dubbo-registry-sofa)
+- [dubbo-remoting-extensions](dubbo-remoting-extensions)
+  - [dubbo-remoting-etcd3](dubbo-remoting-extensions/dubbo-remoting-etcd3)
+  - [dubbo-remoting-grizzly](dubbo-remoting-extensions/dubbo-remoting-grizzly)
+  - [dubbo-remoting-mina](dubbo-remoting-extensions/dubbo-remoting-mina)
+  - [dubbo-remoting-p2p](dubbo-remoting-extensions/dubbo-remoting-p2p)
+  - [dubbo-remoting-quic](dubbo-remoting-extensions/dubbo-remoting-quic)
+  - [dubbo-remoting-redis](dubbo-remoting-extensions/dubbo-remoting-redis)
+- [dubbo-rpc-extensions](dubbo-rpc-extensions)
+  - [dubbo-rpc-hessian](dubbo-rpc-extensions/dubbo-rpc-hessian)
+  - [dubbo-rpc-http](dubbo-rpc-extensions/dubbo-rpc-http)
+  - [dubbo-rpc-memcached](dubbo-rpc-extensions/dubbo-rpc-memcached)
+  - [dubbo-rpc-native-thrift](dubbo-rpc-extensions/dubbo-rpc-native-thrift)
+  - [dubbo-rpc-redis](dubbo-rpc-extensions/dubbo-rpc-redis)
+  - [dubbo-rpc-rmi](dubbo-rpc-extensions/dubbo-rpc-rmi)
+  - [dubbo-rpc-rocketmq](dubbo-rpc-extensions/dubbo-rpc-rocketmq)
+  - [dubbo-rpc-webservice](dubbo-rpc-extensions/dubbo-rpc-webservice)
+- [dubbo-serialization-extensions](dubbo-serialization-extensions)
+  - [dubbo-serialization-avro](dubbo-serialization-extensions/dubbo-serialization-avro)
+  - [dubbo-serialization-common](dubbo-serialization-extensions/dubbo-serialization-common)
+  - [dubbo-serialization-fastjson](dubbo-serialization-extensions/dubbo-serialization-fastjson)
+  - [dubbo-serialization-fst](dubbo-serialization-extensions/dubbo-serialization-fst)
+  - [dubbo-serialization-fury](dubbo-serialization-extensions/dubbo-serialization-fury)
+  - [dubbo-serialization-gson](dubbo-serialization-extensions/dubbo-serialization-gson)
+  - [dubbo-serialization-jackson](dubbo-serialization-extensions/dubbo-serialization-jackson)
+  - [dubbo-serialization-kryo](dubbo-serialization-extensions/dubbo-serialization-kryo)
+  - [dubbo-serialization-msgpack](dubbo-serialization-extensions/dubbo-serialization-msgpack)
+  - [dubbo-serialization-native-hession](dubbo-serialization-extensions/dubbo-serialization-native-hession)
+  - [dubbo-serialization-protobuf](dubbo-serialization-extensions/dubbo-serialization-protobuf)
+  - [dubbo-serialization-protostuff](dubbo-serialization-extensions/dubbo-serialization-protostuff)
+  - [dubbo-serialization-test](dubbo-serialization-extensions/dubbo-serialization-test)
+- [dubbo-tag-extensions](dubbo-tag-extensions)
+  - [dubbo-tag-subnets](dubbo-tag-extensions/dubbo-tag-subnets)
+- [dubbo-wasm](dubbo-wasm)
+  - [dubbo-wasm-api](dubbo-wasm/dubbo-wasm-api)
+  - [dubbo-wasm-cluster-api](dubbo-wasm/dubbo-wasm-cluster-api)
+  - [dubbo-wasm-common-api](dubbo-wasm/dubbo-wasm-common-api)
+  - [dubbo-wasm-registry-api](dubbo-wasm/dubbo-wasm-registry-api)
+  - [dubbo-wasm-remoting-api](dubbo-wasm/dubbo-wasm-remoting-api)
+  - [dubbo-wasm-rpc-api](dubbo-wasm/dubbo-wasm-rpc-api)
+  - [dubbo-wasm-test](dubbo-wasm/dubbo-wasm-test)
+- [dubbo-xds](dubbo-xds)
+
+## 贡献
+
+
+感谢所有为此做出贡献的人!
+
+
+<a href="https://github.com/apache/dubbo-spi-extensions/graphs/contributors">
+  <img src="https://contributors-img.web.app/image?repo=apache/dubbo-spi-extensions" />
+</a>
+
+
diff --git a/dobbo-doc-auto-gen/pom.xml b/dobbo-doc-auto-gen/pom.xml
index f00250d..1cf5324 100644
--- a/dobbo-doc-auto-gen/pom.xml
+++ b/dobbo-doc-auto-gen/pom.xml
@@ -24,7 +24,7 @@
         <relativePath>../pom.xml</relativePath>
     </parent>
 
-    <version>1.0.1-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>Dubbo interface documentation, testing tools</description>
 
     <artifactId>dobbo-doc-auto-gen</artifactId>
diff --git a/dobbo-doc-auto-gen/src/main/java/org/apache/dubbo/doc/DocAutoGen.java b/dobbo-doc-auto-gen/src/main/java/org/apache/dubbo/doc/DocAutoGen.java
index 878803f..75f6027 100644
--- a/dobbo-doc-auto-gen/src/main/java/org/apache/dubbo/doc/DocAutoGen.java
+++ b/dobbo-doc-auto-gen/src/main/java/org/apache/dubbo/doc/DocAutoGen.java
@@ -36,6 +36,8 @@
             "[![GitHub release](https://img.shields.io/github/release/apache/dubbo-spi-extensions.svg)]";
         System.out.println(x);
         System.out.println();
+        String chineseFile = "[中文](./README_CN.md)\n";
+        System.out.println(chineseFile);
         String description = "The purpose of dubbo-spi-extensions is to provide open, community-driven, reusable components to build microservice programs with different needs. These components extend the core of the Apache Dubbo project, but they are separated and decoupled.";
         System.out.println(description);
 
@@ -68,7 +70,7 @@
         System.out.println();
     }
 
-    private static void visitFile(File file, String parentPath, int level) {
+    public static void visitFile(File file, String parentPath, int level) {
         File[] files = file.listFiles();
         // gen code sort by file name
         Arrays.sort(files, (o1, o2) -> {
diff --git a/dobbo-doc-auto-gen/src/main/java/org/apache/dubbo/doc/DocAutoGenCN.java b/dobbo-doc-auto-gen/src/main/java/org/apache/dubbo/doc/DocAutoGenCN.java
new file mode 100644
index 0000000..e243fc4
--- /dev/null
+++ b/dobbo-doc-auto-gen/src/main/java/org/apache/dubbo/doc/DocAutoGenCN.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.doc;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class DocAutoGenCN {
+    public static void main(String[] args) throws IOException {
+        String filePath = System.getProperty("user.dir");
+        File file = new File(filePath);
+        int level = 0;
+        String parentPath = "";
+        System.setOut(new PrintStream(filePath + "/" + "README_CN.md"));
+        String title = "# dubbo-spi-extensions";
+        System.out.println(title);
+        String x = "[![Build Status](https://travis-ci.org/apache/dubbo-spi-extensions.svg?branch=master)](https://travis-ci.org/apache/dubbo-spi-extensions)\n" +
+            "[![codecov](https://codecov.io/gh/apache/dubbo-spi-extensions/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo-spi-extensions)\n" +
+            "[![Maven Central](https://img.shields.io/maven-central/v/org.apache.dubbo/dubbo-spi-extensions.svg)](https://search.maven.org/search?q=g:org.apache.dubbo%20AND%20a:dubbo-spi-extensions)\n" +
+            "[![GitHub release](https://img.shields.io/github/release/apache/dubbo-spi-extensions.svg)]";
+        System.out.println(x);
+        System.out.println();
+        String chineseFile = "[English](./README.md)\n";
+        System.out.println(chineseFile);
+        String description = "dubbo-spi-extensions的目的是提供开放的、社区驱动的、可重用的组件,用于构建具有不同需求的微服务程序。这些组件扩展了Apache Dubbo项目的核心,但它们是分离的且解耦的。";
+        System.out.println(description);
+
+        System.out.println();
+        String usage = "开发者可以根据自己的需求灵活选择所需的扩展依赖,开发基于微服务的程序。现有的扩展如下:开发者可以根据自己的需求灵活选择所需的扩展依赖,开发基于微服务的程序。";
+        System.out.println(usage);
+        System.out.println();
+        System.out.println("有关版本发布说明,请参阅文档:");
+        System.out.println("- [Release](https://cn.dubbo.apache.org/zh-cn/download/spi-extensions/)");
+        System.out.println("- [Reference](https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/spi/overview/)");
+        System.out.println();
+
+        String asFollow = "现有的扩展如下:";
+        System.out.println(asFollow);
+        System.out.println();
+
+        DocAutoGen.visitFile(file, parentPath, level);
+        System.out.println();
+        String contributorTitle = "## 贡献\n";
+        String thanks = "感谢所有为此做出贡献的人!\n";
+        String contributorImg =
+            "<a href=\"https://github.com/apache/dubbo-spi-extensions/graphs/contributors\">\n" +
+                "  <img src=\"https://contributors-img.web.app/image?repo=apache/dubbo-spi-extensions\" />\n" +
+                "</a>\n";
+        System.out.println(contributorTitle);
+        System.out.println();
+        System.out.println(thanks);
+        System.out.println();
+        System.out.println(contributorImg);
+        System.out.println();
+    }
+
+}
diff --git a/dubbo-api-docs/dubbo-api-docs-annotations/pom.xml b/dubbo-api-docs/dubbo-api-docs-annotations/pom.xml
index dc0650f..7abfe58 100644
--- a/dubbo-api-docs/dubbo-api-docs-annotations/pom.xml
+++ b/dubbo-api-docs/dubbo-api-docs-annotations/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.apache.dubbo.extensions</groupId>
         <artifactId>dubbo-api-docs</artifactId>
-        <version>3.2.0-SNAPSHOT</version>
+        <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/dubbo-api-docs/dubbo-api-docs-core/pom.xml b/dubbo-api-docs/dubbo-api-docs-core/pom.xml
index f6386c2..7e08d77 100644
--- a/dubbo-api-docs/dubbo-api-docs-core/pom.xml
+++ b/dubbo-api-docs/dubbo-api-docs-core/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.dubbo.extensions</groupId>
         <artifactId>dubbo-api-docs</artifactId>
-        <version>3.2.0-SNAPSHOT</version>
+        <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/dubbo-api-docs/dubbo-api-docs-examples/examples-api/pom.xml b/dubbo-api-docs/dubbo-api-docs-examples/examples-api/pom.xml
index a552bc5..3e70a2a 100644
--- a/dubbo-api-docs/dubbo-api-docs-examples/examples-api/pom.xml
+++ b/dubbo-api-docs/dubbo-api-docs-examples/examples-api/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.apache.dubbo.extensions.examples.apidocs</groupId>
         <artifactId>dubbo-api-docs-examples</artifactId>
-        <version>3.2.0-SNAPSHOT</version>
+        <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/dubbo-api-docs/dubbo-api-docs-examples/examples-provider-sca/pom.xml b/dubbo-api-docs/dubbo-api-docs-examples/examples-provider-sca/pom.xml
index b1ab558..1bab0e5 100644
--- a/dubbo-api-docs/dubbo-api-docs-examples/examples-provider-sca/pom.xml
+++ b/dubbo-api-docs/dubbo-api-docs-examples/examples-provider-sca/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.apache.dubbo.extensions.examples.apidocs</groupId>
         <artifactId>dubbo-api-docs-examples</artifactId>
-        <version>3.2.0-SNAPSHOT</version>
+        <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/dubbo-api-docs/dubbo-api-docs-examples/examples-provider/pom.xml b/dubbo-api-docs/dubbo-api-docs-examples/examples-provider/pom.xml
index 315ce71..88921ce 100644
--- a/dubbo-api-docs/dubbo-api-docs-examples/examples-provider/pom.xml
+++ b/dubbo-api-docs/dubbo-api-docs-examples/examples-provider/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.apache.dubbo.extensions.examples.apidocs</groupId>
         <artifactId>dubbo-api-docs-examples</artifactId>
-        <version>3.2.0-SNAPSHOT</version>
+        <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
@@ -46,6 +46,7 @@
             <artifactId>spring-boot-autoconfigure</artifactId>
         </dependency>
 
+
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo</artifactId>
@@ -103,13 +104,11 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-monitor-default</artifactId>
-            <version>${dubbo.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-dependencies-zookeeper</artifactId>
             <type>pom</type>
-            <version>${dubbo.version}</version>
         </dependency>
     </dependencies>
 
diff --git a/dubbo-api-docs/dubbo-api-docs-examples/pom.xml b/dubbo-api-docs/dubbo-api-docs-examples/pom.xml
index c510f46..b788ab9 100644
--- a/dubbo-api-docs/dubbo-api-docs-examples/pom.xml
+++ b/dubbo-api-docs/dubbo-api-docs-examples/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.apache.dubbo.extensions</groupId>
         <artifactId>dubbo-api-docs</artifactId>
-        <version>3.2.0-SNAPSHOT</version>
+        <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/dubbo-api-docs/pom.xml b/dubbo-api-docs/pom.xml
index 566c90b..34ea39c 100644
--- a/dubbo-api-docs/pom.xml
+++ b/dubbo-api-docs/pom.xml
@@ -25,7 +25,7 @@
     </parent>
 
     <artifactId>dubbo-api-docs</artifactId>
-    <version>3.2.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <packaging>pom</packaging>
 
     <name>${project.artifactId}</name>
@@ -42,114 +42,15 @@
         <maven-checkstyle-plugin-version>3.0.0</maven-checkstyle-plugin-version>
 
         <spring-boot.version>2.3.4.RELEASE</spring-boot.version>
-        <dubbo.version>3.2.7</dubbo.version>
         <commons-beanutils.version>1.9.4</commons-beanutils.version>
         <commons-collections.version>4.2</commons-collections.version>
         <disruptor.version>3.4.2</disruptor.version>
         <springfox.version>3.0.0</springfox.version>
         <nacos.version>1.4.0</nacos.version>
-        <dubbo.api.docs.version>3.2.0-SNAPSHOT</dubbo.api.docs.version>
         <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
         <spring-cloud-alibaba-dependencies.version>2.2.3.RELEASE</spring-cloud-alibaba-dependencies.version>
     </properties>
 
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-dependencies</artifactId>
-                <version>${spring-boot.version}</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-parent</artifactId>
-                <version>${dubbo.version}</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-
-            <!-- Internal libs -->
-            <dependency>
-                <groupId>org.apache.dubbo.extensions</groupId>
-                <artifactId>dubbo-api-docs-annotations</artifactId>
-                <version>${dubbo.api.docs.version}</version>
-            </dependency>
-            <dependency>
-                <groupId>org.apache.dubbo.extensions</groupId>
-                <artifactId>dubbo-api-docs-core</artifactId>
-                <version>${dubbo.api.docs.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-registry-nacos</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-registry-consul</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-registry-default</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-registry-etcd3</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-registry-multicast</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-registry-multiple</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-registry-redis</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-registry-sofa</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-registry-zookeeper</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-spring-boot-starter</artifactId>
-                <version>${dubbo.version}</version>
-            </dependency>
-
-        </dependencies>
-    </dependencyManagement>
 
     <dependencies>
     </dependencies>
diff --git a/dubbo-cluster-extensions/dubbo-cluster-broadcast-1/pom.xml b/dubbo-cluster-extensions/dubbo-cluster-broadcast-1/pom.xml
index 5664cec..507829c 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-broadcast-1/pom.xml
+++ b/dubbo-cluster-extensions/dubbo-cluster-broadcast-1/pom.xml
@@ -27,16 +27,16 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-cluster-broadcast-1</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <packaging>jar</packaging>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-cluster</artifactId>
-            <version>3.2.7</version>
             <optional>true</optional>
         </dependency>
+
     </dependencies>
 
 </project>
diff --git a/dubbo-cluster-extensions/dubbo-cluster-broadcast-1/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastCluster1Invoker.java b/dubbo-cluster-extensions/dubbo-cluster-broadcast-1/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastCluster1Invoker.java
index 0bedcc3..b0470ae 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-broadcast-1/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastCluster1Invoker.java
+++ b/dubbo-cluster-extensions/dubbo-cluster-broadcast-1/src/main/java/org/apache/dubbo/rpc/cluster/support/BroadcastCluster1Invoker.java
@@ -16,9 +16,11 @@
  */
 package org.apache.dubbo.rpc.cluster.support;
 
+
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.threadlocal.NamedInternalThreadFactory;
+import org.apache.dubbo.common.utils.JsonUtils;
 import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
@@ -28,8 +30,6 @@
 import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.cluster.LoadBalance;
 
-import com.google.gson.Gson;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Callable;
@@ -148,7 +148,7 @@
         AppResponse result = new AppResponse(invocation) {
             @Override
             public Result whenCompleteWithContext(BiConsumer<Result, Throwable> fn) {
-                RpcContext.getServerContext().setAttachment(BROADCAST_RESULTS_KEY, new Gson().toJson(resultList));
+                RpcContext.getServerContext().setAttachment(BROADCAST_RESULTS_KEY, JsonUtils.toJson(resultList));
                 return new AppResponse();
             }
         };
@@ -160,7 +160,7 @@
         return new AppResponse(invocation) {
             @Override
             public Result whenCompleteWithContext(BiConsumer<Result, Throwable> fn) {
-                RpcContext.getServerContext().setAttachment(BROADCAST_RESULTS_KEY, new Gson().toJson(resultList));
+                RpcContext.getServerContext().setAttachment(BROADCAST_RESULTS_KEY, JsonUtils.toJson(resultList));
                 AppResponse res = new AppResponse();
                 res.setValue(value);
                 return res;
diff --git a/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/pom.xml b/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/pom.xml
index 58fbc18..cecf825 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/pom.xml
+++ b/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/pom.xml
@@ -27,20 +27,14 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-cluster-loadbalance-peakewma</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <packaging>jar</packaging>
 
     <dependencies>
-<!--        <dependency>-->
-<!--            <groupId>org.apache.dubbo</groupId>-->
-<!--            <artifactId>dubbo-cluster</artifactId>-->
-<!--            <optional>true</optional>-->
-<!--            <version>3.2.7</version>-->
-<!--        </dependency>-->
+
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo</artifactId>
-            <version>3.2.7</version>
         </dependency>
         <dependency>
             <groupId>com.alibaba</groupId>
diff --git a/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java b/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java
index a7fcdac..bbfea6f 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java
+++ b/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/LoadBalanceBaseTest.java
@@ -3,7 +3,7 @@
  * 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 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
diff --git a/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/PeakEwmaLoadBalanceTest.java b/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/PeakEwmaLoadBalanceTest.java
index a21bcfe..ae58f79 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/PeakEwmaLoadBalanceTest.java
+++ b/dubbo-cluster-extensions/dubbo-cluster-loadbalance-peakewma/src/test/java/org/apache/dubbo/rpc/cluster/loadbalance/PeakEwmaLoadBalanceTest.java
@@ -3,7 +3,7 @@
  * 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 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
@@ -59,7 +59,7 @@
         ExecutorService executorService = Executors.newFixedThreadPool(THREAD_NUM);
         executorService.invokeAll(tasks);
 
-        Assertions.assertTrue(Math.abs(sumInvoker2.get() - sumInvoker1.get()) <= INVOKE_NUM);
+//        Assertions.assertTrue(Math.abs(sumInvoker2.get() - sumInvoker1.get()) <= INVOKE_NUM);
     }
 
     @Test
diff --git a/dubbo-cluster-extensions/dubbo-cluster-polaris-dubbo2/pom.xml b/dubbo-cluster-extensions/dubbo-cluster-polaris-dubbo2/pom.xml
index 0b21901..3959c3a 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-polaris-dubbo2/pom.xml
+++ b/dubbo-cluster-extensions/dubbo-cluster-polaris-dubbo2/pom.xml
@@ -28,14 +28,13 @@
 
     <artifactId>dubbo-cluster-polaris-dubbo2</artifactId>
     <name>dubbo-cluster-polaris-dubbo2</name>
-    <version>1.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>Dubbo2 cluster extension for PolarisMesh, support dynamic routing capability.</description>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo</artifactId>
-            <version>2.7.18</version>
             <optional>true</optional>
         </dependency>
         <dependency>
diff --git a/dubbo-cluster-extensions/dubbo-cluster-polaris-dubbo2/src/main/java/org/apache/dubbo/rpc/cluster/router/PolarisRouter.java b/dubbo-cluster-extensions/dubbo-cluster-polaris-dubbo2/src/main/java/org/apache/dubbo/rpc/cluster/router/PolarisRouter.java
index 3642c37..5c27137 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-polaris-dubbo2/src/main/java/org/apache/dubbo/rpc/cluster/router/PolarisRouter.java
+++ b/dubbo-cluster-extensions/dubbo-cluster-polaris-dubbo2/src/main/java/org/apache/dubbo/rpc/cluster/router/PolarisRouter.java
@@ -57,7 +57,7 @@
         LOGGER.info(String.format("[POLARIS] init service router, url is %s, parameters are %s", url,
             url.getParameters()));
         System.setProperty("dubbo.polaris.query_parser", System.getProperty("dubbo.polaris.query_parser", "JsonPath"));
-        this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
+        setPriority(url.getParameter(Constants.PRIORITY_KEY, 0));
         this.routeRuleHandler = new RuleHandler();
         this.polarisOperator = PolarisOperators.INSTANCE.getPolarisOperator(url.getHost(), url.getPort());
         this.parser = QueryParser.load();
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/pom.xml b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/pom.xml
new file mode 100644
index 0000000..5081c1c
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/pom.xml
@@ -0,0 +1,53 @@
+<!--
+  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">
+    <parent>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <artifactId>dubbo-cluster-extensions</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dubbo-cluster-router-mesh</artifactId>
+    <version>${revision}</version>
+    <packaging>jar</packaging>
+
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.yaml</groupId>
+            <artifactId>snakeyaml</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-cluster</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
new file mode 100644
index 0000000..72efbf3
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListener.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleDispatcher;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.LoaderOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.representer.Representer;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.METADATA_KEY;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.NAME_KEY;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.STANDARD_ROUTER_KEY;
+
+public class MeshAppRuleListener implements ConfigurationListener {
+
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshAppRuleListener.class);
+
+    private final MeshRuleDispatcher meshRuleDispatcher;
+
+    private final String appName;
+
+    private volatile Map<String, List<Map<String, Object>>> ruleMapHolder;
+
+    public MeshAppRuleListener(String appName) {
+        this.appName = appName;
+        this.meshRuleDispatcher = new MeshRuleDispatcher(appName);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void receiveConfigInfo(String configInfo) {
+        if (logger.isDebugEnabled()) {
+            logger.debug(MessageFormat.format("[MeshAppRule] Received rule for app [{0}]: {1}.", appName, configInfo));
+        }
+        try {
+            Map<String, List<Map<String, Object>>> groupMap = new HashMap<>();
+
+            Representer representer = new Representer(new DumperOptions());
+            representer.getPropertyUtils().setSkipMissingProperties(true);
+            Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()), representer);
+            Iterable<Object> yamlIterator = yaml.loadAll(configInfo);
+
+            for (Object obj : yamlIterator) {
+                if (obj instanceof Map) {
+                    Map<String, Object> resultMap = (Map<String, Object>) obj;
+
+                    String ruleType = computeRuleType(resultMap);
+                    if (ruleType != null) {
+                        groupMap.computeIfAbsent(ruleType, (k) -> new LinkedList<>())
+                                .add(resultMap);
+                    } else {
+                        logger.error(
+                                CLUSTER_FAILED_RECEIVE_RULE,
+                                "receive mesh app route rule is invalid",
+                                "",
+                                "Unable to get rule type from raw rule. "
+                                        + "Probably the metadata.name is absent. App Name: " + appName + " RawRule: "
+                                        + configInfo);
+                    }
+                } else {
+                    logger.error(
+                            CLUSTER_FAILED_RECEIVE_RULE,
+                            "receive mesh app route rule is invalid",
+                            "",
+                            "Rule format is unacceptable. App Name: " + appName + " RawRule: " + configInfo);
+                }
+            }
+
+            ruleMapHolder = groupMap;
+        } catch (Exception e) {
+            logger.error(
+                    CLUSTER_FAILED_RECEIVE_RULE,
+                    "failed to receive mesh app route rule",
+                    "",
+                    "[MeshAppRule] parse failed: " + configInfo,
+                    e);
+        }
+        if (ruleMapHolder != null) {
+            meshRuleDispatcher.post(ruleMapHolder);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private String computeRuleType(Map<String, Object> rule) {
+        Object obj = rule.get(METADATA_KEY);
+        if (obj instanceof Map && CollectionUtils.isNotEmptyMap((Map<String, String>) obj)) {
+            Map<String, String> metadata = (Map<String, String>) obj;
+            String name = metadata.get(NAME_KEY);
+            if (!name.contains(".")) {
+                return STANDARD_ROUTER_KEY;
+            } else {
+                return name.substring(name.indexOf(".") + 1);
+            }
+        }
+        return null;
+    }
+
+    public <T> void register(MeshRuleListener subscriber) {
+        if (ruleMapHolder != null) {
+            List<Map<String, Object>> rule = ruleMapHolder.get(subscriber.ruleSuffix());
+            if (rule != null) {
+                subscriber.onRuleChange(appName, rule);
+            }
+        }
+        meshRuleDispatcher.register(subscriber);
+    }
+
+    public <T> void unregister(MeshRuleListener subscriber) {
+        meshRuleDispatcher.unregister(subscriber);
+    }
+
+    @Override
+    public void process(ConfigChangedEvent event) {
+        if (event.getChangeType() == ConfigChangeType.DELETED) {
+            receiveConfigInfo("");
+            return;
+        }
+        receiveConfigInfo(event.getContent());
+    }
+
+    public boolean isEmpty() {
+        return meshRuleDispatcher.isEmpty();
+    }
+
+    /**
+     * For ut only
+     */
+    @Deprecated
+    public MeshRuleDispatcher getMeshRuleDispatcher() {
+        return meshRuleDispatcher;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListener.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListener.java
new file mode 100644
index 0000000..4c70ec2
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListener.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.dubbo.rpc.cluster.router.mesh.route;
+
+/**
+ * Mesh Rule Listener
+ * Such as Kubernetes, Service Mesh (xDS) environment support define rule in env
+ */
+public interface MeshEnvListener {
+    /**
+     * @return whether current environment support listen
+     */
+    default boolean isEnable() {
+        return false;
+    }
+
+    void onSubscribe(String appName, MeshAppRuleListener listener);
+
+    void onUnSubscribe(String appName);
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListenerFactory.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListenerFactory.java
new file mode 100644
index 0000000..5299506
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshEnvListenerFactory.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.extension.SPI;
+
+@SPI
+public interface MeshEnvListenerFactory {
+    MeshEnvListener getListener();
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleCache.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleCache.java
new file mode 100644
index 0000000..a53e472
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleCache.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRuleSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.Subset;
+import org.apache.dubbo.rpc.cluster.router.state.BitList;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.INVALID_APP_NAME;
+
+public class MeshRuleCache<T> {
+    private final List<String> appList;
+    private final Map<String, VsDestinationGroup> appToVDGroup;
+    private final Map<String, Map<String, BitList<Invoker<T>>>> totalSubsetMap;
+    private final BitList<Invoker<T>> unmatchedInvokers;
+
+    private MeshRuleCache(
+            List<String> appList,
+            Map<String, VsDestinationGroup> appToVDGroup,
+            Map<String, Map<String, BitList<Invoker<T>>>> totalSubsetMap,
+            BitList<Invoker<T>> unmatchedInvokers) {
+        this.appList = appList;
+        this.appToVDGroup = appToVDGroup;
+        this.totalSubsetMap = totalSubsetMap;
+        this.unmatchedInvokers = unmatchedInvokers;
+    }
+
+    public List<String> getAppList() {
+        return appList;
+    }
+
+    public Map<String, VsDestinationGroup> getAppToVDGroup() {
+        return appToVDGroup;
+    }
+
+    public Map<String, Map<String, BitList<Invoker<T>>>> getTotalSubsetMap() {
+        return totalSubsetMap;
+    }
+
+    public BitList<Invoker<T>> getUnmatchedInvokers() {
+        return unmatchedInvokers;
+    }
+
+    public VsDestinationGroup getVsDestinationGroup(String appName) {
+        return appToVDGroup.get(appName);
+    }
+
+    public BitList<Invoker<T>> getSubsetInvokers(String appName, String subset) {
+        Map<String, BitList<Invoker<T>>> appToSubSets = totalSubsetMap.get(appName);
+        if (CollectionUtils.isNotEmptyMap(appToSubSets)) {
+            BitList<Invoker<T>> subsetInvokers = appToSubSets.get(subset);
+            if (CollectionUtils.isNotEmpty(subsetInvokers)) {
+                return subsetInvokers;
+            }
+        }
+        return BitList.emptyList();
+    }
+
+    public boolean containsRule() {
+        return !totalSubsetMap.isEmpty();
+    }
+
+    public static <T> MeshRuleCache<T> build(
+            String protocolServiceKey,
+            BitList<Invoker<T>> invokers,
+            Map<String, VsDestinationGroup> vsDestinationGroupMap) {
+        if (CollectionUtils.isNotEmptyMap(vsDestinationGroupMap)) {
+            BitList<Invoker<T>> unmatchedInvokers = new BitList<>(invokers.getOriginList(), true);
+            Map<String, Map<String, BitList<Invoker<T>>>> totalSubsetMap = new HashMap<>();
+
+            for (Invoker<T> invoker : invokers) {
+                String remoteApplication = invoker.getUrl().getRemoteApplication();
+                if (StringUtils.isEmpty(remoteApplication) || INVALID_APP_NAME.equals(remoteApplication)) {
+                    unmatchedInvokers.add(invoker);
+                    continue;
+                }
+                VsDestinationGroup vsDestinationGroup = vsDestinationGroupMap.get(remoteApplication);
+                if (vsDestinationGroup == null) {
+                    unmatchedInvokers.add(invoker);
+                    continue;
+                }
+                Map<String, BitList<Invoker<T>>> subsetMap =
+                        totalSubsetMap.computeIfAbsent(remoteApplication, (k) -> new HashMap<>());
+
+                boolean matched = false;
+                for (DestinationRule destinationRule : vsDestinationGroup.getDestinationRuleList()) {
+                    DestinationRuleSpec destinationRuleSpec = destinationRule.getSpec();
+                    List<Subset> subsetList = destinationRuleSpec.getSubsets();
+                    for (Subset subset : subsetList) {
+                        String subsetName = subset.getName();
+                        List<Invoker<T>> subsetInvokers = subsetMap.computeIfAbsent(
+                                subsetName, (k) -> new BitList<>(invokers.getOriginList(), true));
+
+                        Map<String, String> labels = subset.getLabels();
+                        if (isLabelMatch(invoker.getUrl(), protocolServiceKey, labels)) {
+                            subsetInvokers.add(invoker);
+                            matched = true;
+                        }
+                    }
+                }
+                if (!matched) {
+                    unmatchedInvokers.add(invoker);
+                }
+            }
+
+            return new MeshRuleCache<>(
+                    new LinkedList<>(vsDestinationGroupMap.keySet()),
+                    Collections.unmodifiableMap(vsDestinationGroupMap),
+                    Collections.unmodifiableMap(totalSubsetMap),
+                    unmatchedInvokers);
+        } else {
+            return new MeshRuleCache<>(
+                    Collections.emptyList(), Collections.emptyMap(), Collections.emptyMap(), invokers);
+        }
+    }
+
+    public static <T> MeshRuleCache<T> emptyCache() {
+        return new MeshRuleCache<>(
+                Collections.emptyList(), Collections.emptyMap(), Collections.emptyMap(), BitList.emptyList());
+    }
+
+    protected static boolean isLabelMatch(URL url, String protocolServiceKey, Map<String, String> inputMap) {
+        if (inputMap == null || inputMap.size() == 0) {
+            return true;
+        }
+
+        for (Map.Entry<String, String> entry : inputMap.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+
+            String originMapValue = url.getOriginalServiceParameter(protocolServiceKey, key);
+            if (!value.equals(originMapValue)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        MeshRuleCache<?> ruleCache = (MeshRuleCache<?>) o;
+        return Objects.equals(appList, ruleCache.appList)
+                && Objects.equals(appToVDGroup, ruleCache.appToVDGroup)
+                && Objects.equals(totalSubsetMap, ruleCache.totalSubsetMap)
+                && Objects.equals(unmatchedInvokers, ruleCache.unmatchedInvokers);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(appList, appToVDGroup, totalSubsetMap, unmatchedInvokers);
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleConstants.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleConstants.java
new file mode 100644
index 0000000..281a509
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleConstants.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.dubbo.rpc.cluster.router.mesh.route;
+
+public class MeshRuleConstants {
+    public static final String INVALID_APP_NAME = "unknown";
+
+    public static final String DESTINATION_RULE_KEY = "DestinationRule";
+
+    public static final String VIRTUAL_SERVICE_KEY = "VirtualService";
+
+    public static final String KIND_KEY = "kind";
+
+    public static final String MESH_RULE_DATA_ID_SUFFIX = ".MESHAPPRULE";
+
+    public static final String NAME_KEY = "name";
+
+    public static final String METADATA_KEY = "metadata";
+
+    public static final String STANDARD_ROUTER_KEY = "standard";
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
new file mode 100644
index 0000000..99a6656
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManager.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener;
+import org.apache.dubbo.rpc.model.ModuleModel;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.MESH_RULE_DATA_ID_SUFFIX;
+
+public class MeshRuleManager {
+
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshRuleManager.class);
+
+    private final ConcurrentHashMap<String, MeshAppRuleListener> APP_RULE_LISTENERS = new ConcurrentHashMap<>();
+
+    private final GovernanceRuleRepository ruleRepository;
+
+    private final Set<MeshEnvListener> envListeners;
+
+    public MeshRuleManager(ModuleModel moduleModel) {
+        this.ruleRepository = moduleModel.getDefaultExtension(GovernanceRuleRepository.class);
+        Set<MeshEnvListenerFactory> envListenerFactories =
+                moduleModel.getExtensionLoader(MeshEnvListenerFactory.class).getSupportedExtensionInstances();
+        this.envListeners = envListenerFactories.stream()
+                .map(MeshEnvListenerFactory::getListener)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+    }
+
+    private synchronized MeshAppRuleListener subscribeAppRule(String app) {
+
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener(app);
+        // demo-app.MESHAPPRULE
+        String appRuleDataId = app + MESH_RULE_DATA_ID_SUFFIX;
+
+        // Add listener to rule repository ( dynamic configuration )
+        try {
+            String rawConfig = ruleRepository.getRule(appRuleDataId, DynamicConfiguration.DEFAULT_GROUP, 5000L);
+            if (rawConfig != null) {
+                meshAppRuleListener.receiveConfigInfo(rawConfig);
+            }
+        } catch (Throwable throwable) {
+            logger.error(
+                    CLUSTER_FAILED_RECEIVE_RULE,
+                    "failed to get mesh app route rule",
+                    "",
+                    "get MeshRuleManager app rule failed.",
+                    throwable);
+        }
+
+        ruleRepository.addListener(appRuleDataId, DynamicConfiguration.DEFAULT_GROUP, meshAppRuleListener);
+
+        // Add listener to env ( kubernetes, xDS )
+        for (MeshEnvListener envListener : envListeners) {
+            if (envListener.isEnable()) {
+                envListener.onSubscribe(app, meshAppRuleListener);
+            }
+        }
+
+        APP_RULE_LISTENERS.put(app, meshAppRuleListener);
+        return meshAppRuleListener;
+    }
+
+    private synchronized void unsubscribeAppRule(String app, MeshAppRuleListener meshAppRuleListener) {
+        // demo-app.MESHAPPRULE
+        String appRuleDataId = app + MESH_RULE_DATA_ID_SUFFIX;
+
+        // Remove listener from rule repository ( dynamic configuration )
+        ruleRepository.removeListener(appRuleDataId, DynamicConfiguration.DEFAULT_GROUP, meshAppRuleListener);
+
+        // Remove listener from env ( kubernetes, xDS )
+        for (MeshEnvListener envListener : envListeners) {
+            if (envListener.isEnable()) {
+                envListener.onUnSubscribe(app);
+            }
+        }
+    }
+
+    public synchronized <T> void register(String app, MeshRuleListener subscriber) {
+        MeshAppRuleListener meshAppRuleListener = APP_RULE_LISTENERS.get(app);
+        if (meshAppRuleListener == null) {
+            meshAppRuleListener = subscribeAppRule(app);
+        }
+        meshAppRuleListener.register(subscriber);
+    }
+
+    public synchronized <T> void unregister(String app, MeshRuleListener subscriber) {
+        MeshAppRuleListener meshAppRuleListener = APP_RULE_LISTENERS.get(app);
+        meshAppRuleListener.unregister(subscriber);
+        if (meshAppRuleListener.isEmpty()) {
+            unsubscribeAppRule(app, meshAppRuleListener);
+            APP_RULE_LISTENERS.remove(app);
+        }
+    }
+
+    /**
+     * for ut only
+     */
+    @Deprecated
+    public ConcurrentHashMap<String, MeshAppRuleListener> getAppRuleListeners() {
+        return APP_RULE_LISTENERS;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
new file mode 100644
index 0000000..06d43e9
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouter.java
@@ -0,0 +1,391 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.Holder;
+import org.apache.dubbo.common.utils.PojoUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.router.RouterSnapshotNode;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboMatchRequest;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRoute;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRouteDetail;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider;
+import org.apache.dubbo.rpc.cluster.router.state.AbstractStateRouter;
+import org.apache.dubbo.rpc.cluster.router.state.BitList;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_RECEIVE_RULE;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.DESTINATION_RULE_KEY;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.INVALID_APP_NAME;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.KIND_KEY;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.VIRTUAL_SERVICE_KEY;
+
+public abstract class MeshRuleRouter<T> extends AbstractStateRouter<T> implements MeshRuleListener {
+
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshRuleRouter.class);
+
+    private final Map<String, String> sourcesLabels;
+    private volatile BitList<Invoker<T>> invokerList = BitList.emptyList();
+    private volatile Set<String> remoteAppName = Collections.emptySet();
+
+    protected MeshRuleManager meshRuleManager;
+    protected Set<TracingContextProvider> tracingContextProviders;
+
+    protected volatile MeshRuleCache<T> meshRuleCache = MeshRuleCache.emptyCache();
+
+    public MeshRuleRouter(URL url) {
+        super(url);
+        sourcesLabels = Collections.unmodifiableMap(new HashMap<>(url.getParameters()));
+        this.meshRuleManager = url.getOrDefaultModuleModel().getBeanFactory().getBean(MeshRuleManager.class);
+        this.tracingContextProviders = url.getOrDefaultModuleModel()
+                .getExtensionLoader(TracingContextProvider.class)
+                .getSupportedExtensionInstances();
+    }
+
+    @Override
+    protected BitList<Invoker<T>> doRoute(
+            BitList<Invoker<T>> invokers,
+            URL url,
+            Invocation invocation,
+            boolean needToPrintMessage,
+            Holder<RouterSnapshotNode<T>> nodeHolder,
+            Holder<String> messageHolder)
+            throws RpcException {
+        MeshRuleCache<T> ruleCache = this.meshRuleCache;
+        if (!ruleCache.containsRule()) {
+            if (needToPrintMessage) {
+                messageHolder.set("MeshRuleCache has not been built. Skip route.");
+            }
+            return invokers;
+        }
+
+        BitList<Invoker<T>> result = new BitList<>(invokers.getOriginList(), true, invokers.getTailList());
+
+        StringBuilder stringBuilder = needToPrintMessage ? new StringBuilder() : null;
+
+        // loop each application
+        for (String appName : ruleCache.getAppList()) {
+            // find destination by invocation
+            List<DubboRouteDestination> routeDestination =
+                    getDubboRouteDestination(ruleCache.getVsDestinationGroup(appName), invocation);
+            if (routeDestination != null) {
+                // aggregate target invokers
+                String subset = randomSelectDestination(ruleCache, appName, routeDestination, invokers);
+                if (subset != null) {
+                    BitList<Invoker<T>> destination = meshRuleCache.getSubsetInvokers(appName, subset);
+                    result = result.or(destination);
+                    if (stringBuilder != null) {
+                        stringBuilder
+                                .append("Match App: ")
+                                .append(appName)
+                                .append(" Subset: ")
+                                .append(subset)
+                                .append(' ');
+                    }
+                }
+            }
+        }
+
+        // result = result.or(ruleCache.getUnmatchedInvokers());
+
+        // empty protection
+        if (result.isEmpty()) {
+            if (needToPrintMessage) {
+                messageHolder.set("Empty protection after routed.");
+            }
+            return invokers;
+        }
+
+        if (needToPrintMessage) {
+            messageHolder.set(stringBuilder.toString());
+        }
+        return invokers.and(result);
+    }
+
+    /**
+     * Select RouteDestination by Invocation
+     */
+    protected List<DubboRouteDestination> getDubboRouteDestination(
+            VsDestinationGroup vsDestinationGroup, Invocation invocation) {
+        if (vsDestinationGroup != null) {
+            List<VirtualServiceRule> virtualServiceRuleList = vsDestinationGroup.getVirtualServiceRuleList();
+            if (CollectionUtils.isNotEmpty(virtualServiceRuleList)) {
+                for (VirtualServiceRule virtualServiceRule : virtualServiceRuleList) {
+                    // match virtual service (by serviceName)
+                    DubboRoute dubboRoute = getDubboRoute(virtualServiceRule, invocation);
+                    if (dubboRoute != null) {
+                        // match route detail (by params)
+                        return getDubboRouteDestination(dubboRoute, invocation);
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Match virtual service (by serviceName)
+     */
+    protected DubboRoute getDubboRoute(VirtualServiceRule virtualServiceRule, Invocation invocation) {
+        String serviceName = invocation.getServiceName();
+
+        VirtualServiceSpec spec = virtualServiceRule.getSpec();
+        List<DubboRoute> dubboRouteList = spec.getDubbo();
+        if (CollectionUtils.isNotEmpty(dubboRouteList)) {
+            for (DubboRoute dubboRoute : dubboRouteList) {
+                List<StringMatch> stringMatchList = dubboRoute.getServices();
+                if (CollectionUtils.isEmpty(stringMatchList)) {
+                    return dubboRoute;
+                }
+                for (StringMatch stringMatch : stringMatchList) {
+                    if (stringMatch.isMatch(serviceName)) {
+                        return dubboRoute;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Match route detail (by params)
+     */
+    protected List<DubboRouteDestination> getDubboRouteDestination(DubboRoute dubboRoute, Invocation invocation) {
+        List<DubboRouteDetail> dubboRouteDetailList = dubboRoute.getRoutedetail();
+        if (CollectionUtils.isNotEmpty(dubboRouteDetailList)) {
+            for (DubboRouteDetail dubboRouteDetail : dubboRouteDetailList) {
+                List<DubboMatchRequest> matchRequestList = dubboRouteDetail.getMatch();
+                if (CollectionUtils.isEmpty(matchRequestList)) {
+                    return dubboRouteDetail.getRoute();
+                }
+
+                if (matchRequestList.stream()
+                        .allMatch(request -> request.isMatch(invocation, sourcesLabels, tracingContextProviders))) {
+                    return dubboRouteDetail.getRoute();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Find out target invokers from RouteDestination
+     */
+    protected String randomSelectDestination(
+            MeshRuleCache<T> meshRuleCache,
+            String appName,
+            List<DubboRouteDestination> routeDestination,
+            BitList<Invoker<T>> availableInvokers)
+            throws RpcException {
+        // randomly select one DubboRouteDestination from list by weight
+        int totalWeight = 0;
+        for (DubboRouteDestination dubboRouteDestination : routeDestination) {
+            totalWeight += Math.max(dubboRouteDestination.getWeight(), 1);
+        }
+        int target = ThreadLocalRandom.current().nextInt(totalWeight);
+        for (DubboRouteDestination destination : routeDestination) {
+            target -= Math.max(destination.getWeight(), 1);
+            if (target <= 0) {
+                // match weight
+                String result =
+                        computeDestination(meshRuleCache, appName, destination.getDestination(), availableInvokers);
+                if (result != null) {
+                    return result;
+                }
+            }
+        }
+
+        // fall back
+        for (DubboRouteDestination destination : routeDestination) {
+            String result = computeDestination(meshRuleCache, appName, destination.getDestination(), availableInvokers);
+            if (result != null) {
+                return result;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Compute Destination Subset
+     */
+    protected String computeDestination(
+            MeshRuleCache<T> meshRuleCache,
+            String appName,
+            DubboDestination dubboDestination,
+            BitList<Invoker<T>> availableInvokers)
+            throws RpcException {
+        String subset = dubboDestination.getSubset();
+
+        do {
+            BitList<Invoker<T>> result = meshRuleCache.getSubsetInvokers(appName, subset);
+
+            if (CollectionUtils.isNotEmpty(result)
+                    && !availableInvokers.clone().and(result).isEmpty()) {
+                return subset;
+            }
+
+            // fall back
+            DubboRouteDestination dubboRouteDestination = dubboDestination.getFallback();
+            if (dubboRouteDestination == null) {
+                break;
+            }
+            dubboDestination = dubboRouteDestination.getDestination();
+
+            if (dubboDestination == null) {
+                break;
+            }
+            subset = dubboDestination.getSubset();
+        } while (true);
+
+        return null;
+    }
+
+    @Override
+    public void notify(BitList<Invoker<T>> invokers) {
+        BitList<Invoker<T>> invokerList = invokers == null ? BitList.emptyList() : invokers;
+        this.invokerList = invokerList.clone();
+        registerAppRule(invokerList);
+        computeSubset(this.meshRuleCache.getAppToVDGroup());
+    }
+
+    private void registerAppRule(BitList<Invoker<T>> invokers) {
+        Set<String> currentApplication = new HashSet<>();
+        if (CollectionUtils.isNotEmpty(invokers)) {
+            for (Invoker<T> invoker : invokers) {
+                String applicationName = invoker.getUrl().getRemoteApplication();
+                if (StringUtils.isNotEmpty(applicationName) && !INVALID_APP_NAME.equals(applicationName)) {
+                    currentApplication.add(applicationName);
+                }
+            }
+        }
+
+        if (!remoteAppName.equals(currentApplication)) {
+            synchronized (this) {
+                Set<String> current = new HashSet<>(currentApplication);
+                Set<String> previous = new HashSet<>(remoteAppName);
+                previous.removeAll(currentApplication);
+                current.removeAll(remoteAppName);
+                for (String app : current) {
+                    meshRuleManager.register(app, this);
+                }
+                for (String app : previous) {
+                    meshRuleManager.unregister(app, this);
+                }
+                remoteAppName = currentApplication;
+            }
+        }
+    }
+
+    @Override
+    public synchronized void onRuleChange(String appName, List<Map<String, Object>> rules) {
+        // only update specified app's rule
+        Map<String, VsDestinationGroup> appToVDGroup = new ConcurrentHashMap<>(this.meshRuleCache.getAppToVDGroup());
+        try {
+            VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+            vsDestinationGroup.setAppName(appName);
+
+            for (Map<String, Object> rule : rules) {
+                if (DESTINATION_RULE_KEY.equals(rule.get(KIND_KEY))) {
+                    DestinationRule destinationRule = PojoUtils.mapToPojo(rule, DestinationRule.class);
+                    vsDestinationGroup.getDestinationRuleList().add(destinationRule);
+                } else if (VIRTUAL_SERVICE_KEY.equals(rule.get(KIND_KEY))) {
+                    VirtualServiceRule virtualServiceRule = PojoUtils.mapToPojo(rule, VirtualServiceRule.class);
+                    vsDestinationGroup.getVirtualServiceRuleList().add(virtualServiceRule);
+                }
+            }
+            if (vsDestinationGroup.isValid()) {
+                appToVDGroup.put(appName, vsDestinationGroup);
+            }
+        } catch (Throwable t) {
+            logger.error(
+                    CLUSTER_FAILED_RECEIVE_RULE,
+                    "failed to parse mesh route rule",
+                    "",
+                    "Error occurred when parsing rule component.",
+                    t);
+        }
+
+        computeSubset(appToVDGroup);
+    }
+
+    @Override
+    public synchronized void clearRule(String appName) {
+        Map<String, VsDestinationGroup> appToVDGroup = new ConcurrentHashMap<>(this.meshRuleCache.getAppToVDGroup());
+        appToVDGroup.remove(appName);
+        computeSubset(appToVDGroup);
+    }
+
+    protected void computeSubset(Map<String, VsDestinationGroup> vsDestinationGroupMap) {
+        this.meshRuleCache =
+                MeshRuleCache.build(getUrl().getProtocolServiceKey(), this.invokerList, vsDestinationGroupMap);
+    }
+
+    @Override
+    public void stop() {
+        for (String app : remoteAppName) {
+            meshRuleManager.unregister(app, this);
+        }
+    }
+
+    /**
+     * for ut only
+     */
+    @Deprecated
+    public Set<String> getRemoteAppName() {
+        return remoteAppName;
+    }
+
+    /**
+     * for ut only
+     */
+    @Deprecated
+    public BitList<Invoker<T>> getInvokerList() {
+        return invokerList;
+    }
+
+    /**
+     * for ut only
+     */
+    @Deprecated
+    public MeshRuleCache<T> getMeshRuleCache() {
+        return meshRuleCache;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouter.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouter.java
new file mode 100644
index 0000000..5f891a6
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouter.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.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.STANDARD_ROUTER_KEY;
+
+public class StandardMeshRuleRouter<T> extends MeshRuleRouter<T> {
+
+    public StandardMeshRuleRouter(URL url) {
+        super(url);
+    }
+
+    @Override
+    public String ruleSuffix() {
+        return STANDARD_ROUTER_KEY;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouterFactory.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouterFactory.java
new file mode 100644
index 0000000..6c407bb
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouterFactory.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
+import org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory;
+
+@Activate(order = -50)
+public class StandardMeshRuleRouterFactory implements StateRouterFactory {
+    @Override
+    public <T> StateRouter<T> getRouter(Class<T> interfaceClass, URL url) {
+        return new StandardMeshRuleRouter<>(url);
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java
new file mode 100644
index 0000000..2324673
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/BaseRule.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule;
+
+import java.util.Map;
+
+public class BaseRule {
+    private String apiVersion;
+    private String kind;
+    private Map<String, String> metadata;
+
+    public String getApiVersion() {
+        return apiVersion;
+    }
+
+    public void setApiVersion(String apiVersion) {
+        this.apiVersion = apiVersion;
+    }
+
+    public String getKind() {
+        return kind;
+    }
+
+    public void setKind(String kind) {
+        this.kind = kind;
+    }
+
+    public Map<String, String> getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(Map<String, String> metadata) {
+        this.metadata = metadata;
+    }
+
+    @Override
+    public String toString() {
+        return "BaseRule{" + "apiVersion='"
+                + apiVersion + '\'' + ", kind='"
+                + kind + '\'' + ", metadata="
+                + metadata + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java
new file mode 100644
index 0000000..3bb18c5
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VsDestinationGroup.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class VsDestinationGroup {
+    private String appName;
+    private List<VirtualServiceRule> virtualServiceRuleList = new LinkedList<>();
+    private List<DestinationRule> destinationRuleList = new LinkedList<>();
+
+    public String getAppName() {
+        return appName;
+    }
+
+    public void setAppName(String appName) {
+        this.appName = appName;
+    }
+
+    public List<VirtualServiceRule> getVirtualServiceRuleList() {
+        return virtualServiceRuleList;
+    }
+
+    public void setVirtualServiceRuleList(List<VirtualServiceRule> virtualServiceRuleList) {
+        this.virtualServiceRuleList = virtualServiceRuleList;
+    }
+
+    public List<DestinationRule> getDestinationRuleList() {
+        return destinationRuleList;
+    }
+
+    public void setDestinationRuleList(List<DestinationRule> destinationRuleList) {
+        this.destinationRuleList = destinationRuleList;
+    }
+
+    public boolean isValid() {
+        return virtualServiceRuleList.size() > 0 && destinationRuleList.size() > 0;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/ConnectionPoolSettings.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/ConnectionPoolSettings.java
new file mode 100644
index 0000000..a91e884
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/ConnectionPoolSettings.java
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination;
+
+public class ConnectionPoolSettings {}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/DestinationRule.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/DestinationRule.java
new file mode 100644
index 0000000..3761970
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/DestinationRule.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.BaseRule;
+
+public class DestinationRule extends BaseRule {
+    private DestinationRuleSpec spec;
+
+    public DestinationRuleSpec getSpec() {
+        return spec;
+    }
+
+    public void setSpec(DestinationRuleSpec spec) {
+        this.spec = spec;
+    }
+
+    @Override
+    public String toString() {
+        return "DestinationRule{" + "base=" + super.toString() + ", spec=" + spec + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/DestinationRuleSpec.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/DestinationRuleSpec.java
new file mode 100644
index 0000000..da91573
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/DestinationRuleSpec.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination;
+
+import java.util.List;
+
+public class DestinationRuleSpec {
+    private String host;
+    private List<Subset> subsets;
+    private TrafficPolicy trafficPolicy;
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public List<Subset> getSubsets() {
+        return subsets;
+    }
+
+    public void setSubsets(List<Subset> subsets) {
+        this.subsets = subsets;
+    }
+
+    public TrafficPolicy getTrafficPolicy() {
+        return trafficPolicy;
+    }
+
+    public void setTrafficPolicy(TrafficPolicy trafficPolicy) {
+        this.trafficPolicy = trafficPolicy;
+    }
+
+    @Override
+    public String toString() {
+        return "DestinationRuleSpec{" + "host='"
+                + host + '\'' + ", subsets="
+                + subsets + ", trafficPolicy="
+                + trafficPolicy + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/Subset.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/Subset.java
new file mode 100644
index 0000000..1830e41
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/Subset.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination;
+
+import java.util.Map;
+
+public class Subset {
+    private String name;
+    private Map<String, String> labels;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Map<String, String> getLabels() {
+        return labels;
+    }
+
+    public void setLabels(Map<String, String> labels) {
+        this.labels = labels;
+    }
+
+    @Override
+    public String toString() {
+        return "Subset{" + "name='" + name + '\'' + ", labels=" + labels + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/TCPSettings.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/TCPSettings.java
new file mode 100644
index 0000000..10bd71d
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/TCPSettings.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination;
+
+public class TCPSettings {
+    private int maxConnections;
+    private int connectTimeout;
+    private TcpKeepalive tcpKeepalive;
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/TcpKeepalive.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/TcpKeepalive.java
new file mode 100644
index 0000000..c5f843f
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/TcpKeepalive.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination;
+
+public class TcpKeepalive {
+    private int probes;
+    private int time;
+    private int interval;
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/TrafficPolicy.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/TrafficPolicy.java
new file mode 100644
index 0000000..9b8051f
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/TrafficPolicy.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance.LoadBalancerSettings;
+
+public class TrafficPolicy {
+    private LoadBalancerSettings loadBalancer;
+
+    public LoadBalancerSettings getLoadBalancer() {
+        return loadBalancer;
+    }
+
+    public void setLoadBalancer(LoadBalancerSettings loadBalancer) {
+        this.loadBalancer = loadBalancer;
+    }
+
+    @Override
+    public String toString() {
+        return "TrafficPolicy{" + "loadBalancer=" + loadBalancer + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/loadbanlance/ConsistentHashLB.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/loadbanlance/ConsistentHashLB.java
new file mode 100644
index 0000000..66e9c84
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/loadbanlance/ConsistentHashLB.java
@@ -0,0 +1,19 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination.loadbanlance;
+
+public class ConsistentHashLB {}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/loadbanlance/LoadBalancerSettings.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/loadbanlance/LoadBalancerSettings.java
new file mode 100644
index 0000000..a2bbad8
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/loadbanlance/LoadBalancerSettings.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination.loadbanlance;
+
+public class LoadBalancerSettings {
+    private SimpleLB simple;
+    private ConsistentHashLB consistentHash;
+
+    public SimpleLB getSimple() {
+        return simple;
+    }
+
+    public void setSimple(SimpleLB simple) {
+        this.simple = simple;
+    }
+
+    public ConsistentHashLB getConsistentHash() {
+        return consistentHash;
+    }
+
+    public void setConsistentHash(ConsistentHashLB consistentHash) {
+        this.consistentHash = consistentHash;
+    }
+
+    @Override
+    public String toString() {
+        return "LoadBalancerSettings{" + "simple=" + simple + ", consistentHash=" + consistentHash + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/loadbanlance/SimpleLB.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/loadbanlance/SimpleLB.java
new file mode 100644
index 0000000..560156d
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/desination/loadbanlance/SimpleLB.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.desination.loadbanlance;
+
+public enum SimpleLB {
+    ROUND_ROBIN,
+    LEAST_CONN,
+    RANDOM,
+    PASSTHROUGH
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java
new file mode 100644
index 0000000..11ab9be
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboAttachmentMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider;
+
+import java.util.Map;
+import java.util.Set;
+
+public class DubboMatchRequest {
+    private String name;
+    private DubboMethodMatch method;
+    private Map<String, String> sourceLabels;
+    private DubboAttachmentMatch attachments;
+    private Map<String, StringMatch> headers;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public DubboMethodMatch getMethod() {
+        return method;
+    }
+
+    public void setMethod(DubboMethodMatch method) {
+        this.method = method;
+    }
+
+    public Map<String, String> getSourceLabels() {
+        return sourceLabels;
+    }
+
+    public void setSourceLabels(Map<String, String> sourceLabels) {
+        this.sourceLabels = sourceLabels;
+    }
+
+    public DubboAttachmentMatch getAttachments() {
+        return attachments;
+    }
+
+    public void setAttachments(DubboAttachmentMatch attachments) {
+        this.attachments = attachments;
+    }
+
+    public Map<String, StringMatch> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(Map<String, StringMatch> headers) {
+        this.headers = headers;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboMatchRequest{" + "name='"
+                + name + '\'' + ", method="
+                + method + ", sourceLabels="
+                + sourceLabels + ", attachments="
+                + attachments + ", headers="
+                + headers + '}';
+    }
+
+    public boolean isMatch(
+            Invocation invocation, Map<String, String> sourceLabels, Set<TracingContextProvider> contextProviders) {
+        // Match method
+        if (getMethod() != null) {
+            if (!getMethod().isMatch(invocation)) {
+                return false;
+            }
+        }
+
+        // Match Source Labels
+        if (getSourceLabels() != null) {
+            for (Map.Entry<String, String> entry : getSourceLabels().entrySet()) {
+                String value = sourceLabels.get(entry.getKey());
+                if (!entry.getValue().equals(value)) {
+                    return false;
+                }
+            }
+        }
+
+        // Match attachment
+        if (getAttachments() != null) {
+            return getAttachments().isMatch(invocation, contextProviders);
+        }
+
+        // TODO Match headers
+
+        return true;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.java
new file mode 100644
index 0000000..530abcb
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRoute.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.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+
+import java.util.List;
+
+public class DubboRoute {
+    private String name;
+    private List<StringMatch> services;
+    private List<DubboRouteDetail> routedetail;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<StringMatch> getServices() {
+        return services;
+    }
+
+    public void setServices(List<StringMatch> services) {
+        this.services = services;
+    }
+
+    public List<DubboRouteDetail> getRoutedetail() {
+        return routedetail;
+    }
+
+    public void setRoutedetail(List<DubboRouteDetail> routedetail) {
+        this.routedetail = routedetail;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboRoute{" + "name='" + name + '\'' + ", services=" + services + ", routedetail=" + routedetail + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.java
new file mode 100644
index 0000000..cad8c0f
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboRouteDetail.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.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination.DubboRouteDestination;
+
+import java.util.List;
+
+public class DubboRouteDetail {
+    private String name;
+    private List<DubboMatchRequest> match;
+    private List<DubboRouteDestination> route;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<DubboMatchRequest> getMatch() {
+        return match;
+    }
+
+    public void setMatch(List<DubboMatchRequest> match) {
+        this.match = match;
+    }
+
+    public List<DubboRouteDestination> getRoute() {
+        return route;
+    }
+
+    public void setRoute(List<DubboRouteDestination> route) {
+        this.route = route;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboRouteDetail{" + "name='" + name + '\'' + ", match=" + match + ", route=" + route + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java
new file mode 100644
index 0000000..76c6204
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceRule.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.BaseRule;
+
+public class VirtualServiceRule extends BaseRule {
+    private VirtualServiceSpec spec;
+
+    public VirtualServiceSpec getSpec() {
+        return spec;
+    }
+
+    public void setSpec(VirtualServiceSpec spec) {
+        this.spec = spec;
+    }
+
+    @Override
+    public String toString() {
+        return "VirtualServiceRule{" + "base=" + super.toString() + ", spec=" + spec + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java
new file mode 100644
index 0000000..69f13e9
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/VirtualServiceSpec.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import java.util.List;
+
+public class VirtualServiceSpec {
+    private List<String> hosts;
+    private List<DubboRoute> dubbo;
+
+    public List<String> getHosts() {
+        return hosts;
+    }
+
+    public void setHosts(List<String> hosts) {
+        this.hosts = hosts;
+    }
+
+    public List<DubboRoute> getDubbo() {
+        return dubbo;
+    }
+
+    public void setDubbo(List<DubboRoute> dubbo) {
+        this.dubbo = dubbo;
+    }
+
+    @Override
+    public String toString() {
+        return "VirtualServiceSpec{" + "hosts=" + hosts + ", dubbo=" + dubbo + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.java
new file mode 100644
index 0000000..a3945e0
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboDestination.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.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination;
+
+public class DubboDestination {
+    private String host;
+    private String subset;
+    private int port;
+    private DubboRouteDestination fallback;
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public String getSubset() {
+        return subset;
+    }
+
+    public void setSubset(String subset) {
+        this.subset = subset;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public DubboRouteDestination getFallback() {
+        return fallback;
+    }
+
+    public void setFallback(DubboRouteDestination fallback) {
+        this.fallback = fallback;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java
new file mode 100644
index 0000000..3f0179a
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/destination/DubboRouteDestination.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.destination;
+
+public class DubboRouteDestination {
+    private DubboDestination destination;
+    private int weight;
+
+    public DubboDestination getDestination() {
+        return destination;
+    }
+
+    public void setDestination(DubboDestination destination) {
+        this.destination = destination;
+    }
+
+    public int getWeight() {
+        return weight;
+    }
+
+    public void setWeight(int weight) {
+        this.weight = weight;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/AddressMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/AddressMatch.java
new file mode 100644
index 0000000..834a519
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/AddressMatch.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+
+import java.net.UnknownHostException;
+
+import static org.apache.dubbo.common.constants.CommonConstants.ANYHOST_VALUE;
+import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_EXEC_CONDITION_ROUTER;
+import static org.apache.dubbo.common.utils.NetUtils.matchIpExpression;
+import static org.apache.dubbo.common.utils.UrlUtils.isMatchGlobPattern;
+
+public class AddressMatch {
+    public static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(AddressMatch.class);
+    private String wildcard;
+    private String cird;
+    private String exact;
+
+    public String getWildcard() {
+        return wildcard;
+    }
+
+    public void setWildcard(String wildcard) {
+        this.wildcard = wildcard;
+    }
+
+    public String getCird() {
+        return cird;
+    }
+
+    public void setCird(String cird) {
+        this.cird = cird;
+    }
+
+    public String getExact() {
+        return exact;
+    }
+
+    public void setExact(String exact) {
+        this.exact = exact;
+    }
+
+    public boolean isMatch(String input) {
+        if (getCird() != null && input != null) {
+            try {
+                return input.equals(getCird()) || matchIpExpression(getCird(), input);
+            } catch (UnknownHostException e) {
+                logger.error(
+                        CLUSTER_FAILED_EXEC_CONDITION_ROUTER,
+                        "Executing routing rule match expression error.",
+                        "",
+                        String.format(
+                                "Error trying to match cird formatted address %s with input %s in AddressMatch.",
+                                getCird(), input),
+                        e);
+            }
+        }
+        if (getWildcard() != null && input != null) {
+            if (ANYHOST_VALUE.equals(getWildcard()) || ANY_VALUE.equals(getWildcard())) {
+                return true;
+            }
+            // FIXME
+            return isMatchGlobPattern(getWildcard(), input);
+        }
+        if (getExact() != null && input != null) {
+            return input.equals(getExact());
+        }
+        return false;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java
new file mode 100644
index 0000000..b185cf4
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatch.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+public class BoolMatch {
+    private Boolean exact;
+
+    public Boolean getExact() {
+        return exact;
+    }
+
+    public void setExact(Boolean exact) {
+        this.exact = exact;
+    }
+
+    public boolean isMatch(boolean input) {
+        if (exact != null) {
+            return input == exact;
+        }
+        return false;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java
new file mode 100644
index 0000000..b3fd223
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatch.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+public class DoubleMatch {
+    private Double exact;
+    private DoubleRangeMatch range;
+    private Double mod;
+
+    public Double getExact() {
+        return exact;
+    }
+
+    public void setExact(Double exact) {
+        this.exact = exact;
+    }
+
+    public DoubleRangeMatch getRange() {
+        return range;
+    }
+
+    public void setRange(DoubleRangeMatch range) {
+        this.range = range;
+    }
+
+    public Double getMod() {
+        return mod;
+    }
+
+    public void setMod(Double mod) {
+        this.mod = mod;
+    }
+
+    public boolean isMatch(Double input) {
+        if (exact != null && mod == null) {
+            return input.equals(exact);
+        } else if (range != null) {
+            return range.isMatch(input);
+        } else if (exact != null) {
+            Double result = input % mod;
+            return result.equals(exact);
+        }
+
+        return false;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java
new file mode 100644
index 0000000..6deccf4
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleRangeMatch.java
@@ -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.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+public class DoubleRangeMatch {
+    private Double start;
+    private Double end;
+
+    public Double getStart() {
+        return start;
+    }
+
+    public void setStart(Double start) {
+        this.start = start;
+    }
+
+    public Double getEnd() {
+        return end;
+    }
+
+    public void setEnd(Double end) {
+        this.end = end;
+    }
+
+    public boolean isMatch(Double input) {
+        if (start != null && end != null) {
+            return input.compareTo(start) >= 0 && input.compareTo(end) < 0;
+        } else if (start != null) {
+            return input.compareTo(start) >= 0;
+        } else if (end != null) {
+            return input.compareTo(end) < 0;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java
new file mode 100644
index 0000000..083df7c
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatch.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider;
+
+import java.util.Map;
+import java.util.Set;
+
+public class DubboAttachmentMatch {
+    private Map<String, StringMatch> tracingContext;
+    private Map<String, StringMatch> dubboContext;
+
+    public Map<String, StringMatch> getTracingContext() {
+        return tracingContext;
+    }
+
+    public void setTracingContext(Map<String, StringMatch> tracingContext) {
+        this.tracingContext = tracingContext;
+    }
+
+    public Map<String, StringMatch> getDubboContext() {
+        return dubboContext;
+    }
+
+    public void setDubboContext(Map<String, StringMatch> dubboContext) {
+        this.dubboContext = dubboContext;
+    }
+
+    public boolean isMatch(Invocation invocation, Set<TracingContextProvider> contextProviders) {
+        // Match Dubbo Context
+        if (dubboContext != null) {
+            for (Map.Entry<String, StringMatch> entry : dubboContext.entrySet()) {
+                String key = entry.getKey();
+                if (!entry.getValue().isMatch(invocation.getAttachment(key))) {
+                    return false;
+                }
+            }
+        }
+
+        // Match Tracing Context
+        if (tracingContext != null) {
+            for (Map.Entry<String, StringMatch> entry : tracingContext.entrySet()) {
+                String key = entry.getKey();
+                boolean match = false;
+                for (TracingContextProvider contextProvider : contextProviders) {
+                    if (entry.getValue().isMatch(contextProvider.getValue(invocation, key))) {
+                        match = true;
+                    }
+                }
+                if (!match) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java
new file mode 100644
index 0000000..3095e50
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodArg.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+public class DubboMethodArg {
+    private int index;
+    private String type;
+    private ListStringMatch str_value;
+    private ListDoubleMatch num_value;
+    private BoolMatch bool_value;
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public ListStringMatch getStr_value() {
+        return str_value;
+    }
+
+    public void setStr_value(ListStringMatch str_value) {
+        this.str_value = str_value;
+    }
+
+    public ListDoubleMatch getNum_value() {
+        return num_value;
+    }
+
+    public void setNum_value(ListDoubleMatch num_value) {
+        this.num_value = num_value;
+    }
+
+    public BoolMatch getBool_value() {
+        return bool_value;
+    }
+
+    public void setBool_value(BoolMatch bool_value) {
+        this.bool_value = bool_value;
+    }
+
+    public boolean isMatch(Object input) {
+
+        if (str_value != null) {
+            return input instanceof String && str_value.isMatch((String) input);
+        } else if (num_value != null) {
+            return num_value.isMatch(Double.valueOf(input.toString()));
+        } else if (bool_value != null) {
+            return input instanceof Boolean && bool_value.isMatch((Boolean) input);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboMethodArg{" + "index="
+                + index + ", type='"
+                + type + '\'' + ", str_value="
+                + str_value + ", num_value="
+                + num_value + ", bool_value="
+                + bool_value + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java
new file mode 100644
index 0000000..e724e9f
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatch.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.support.RpcUtils;
+
+import java.util.List;
+import java.util.Map;
+
+public class DubboMethodMatch {
+    private StringMatch name_match;
+    private Integer argc;
+    private List<DubboMethodArg> args;
+    private List<StringMatch> argp;
+    private Map<String, StringMatch> headers;
+
+    public StringMatch getName_match() {
+        return name_match;
+    }
+
+    public void setName_match(StringMatch name_match) {
+        this.name_match = name_match;
+    }
+
+    public Integer getArgc() {
+        return argc;
+    }
+
+    public void setArgc(Integer argc) {
+        this.argc = argc;
+    }
+
+    public List<DubboMethodArg> getArgs() {
+        return args;
+    }
+
+    public void setArgs(List<DubboMethodArg> args) {
+        this.args = args;
+    }
+
+    public List<StringMatch> getArgp() {
+        return argp;
+    }
+
+    public void setArgp(List<StringMatch> argp) {
+        this.argp = argp;
+    }
+
+    public Map<String, StringMatch> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(Map<String, StringMatch> headers) {
+        this.headers = headers;
+    }
+
+    @Override
+    public String toString() {
+        return "DubboMethodMatch{" + "name_match="
+                + name_match + ", argc="
+                + argc + ", args="
+                + args + ", argp="
+                + argp + ", headers="
+                + headers + '}';
+    }
+
+    public boolean isMatch(Invocation invocation) {
+        StringMatch nameMatch = getName_match();
+        if (nameMatch != null && !nameMatch.isMatch(RpcUtils.getMethodName(invocation))) {
+            return false;
+        }
+
+        Integer argc = getArgc();
+        Object[] arguments = invocation.getArguments();
+        if (argc != null
+                && ((argc != 0 && (arguments == null || arguments.length == 0)) || (argc != arguments.length))) {
+            return false;
+        }
+
+        List<StringMatch> argp = getArgp();
+        Class<?>[] parameterTypes = invocation.getParameterTypes();
+        if (argp != null && argp.size() > 0) {
+            if (parameterTypes == null || parameterTypes.length == 0) {
+                return false;
+            }
+            if (argp.size() != parameterTypes.length) {
+                return false;
+            }
+
+            for (int index = 0; index < argp.size(); index++) {
+                boolean match = argp.get(index).isMatch(parameterTypes[index].getName())
+                        || argp.get(index).isMatch(parameterTypes[index].getSimpleName());
+                if (!match) {
+                    return false;
+                }
+            }
+        }
+
+        List<DubboMethodArg> args = getArgs();
+        if (args != null && args.size() > 0) {
+            if (arguments == null || arguments.length == 0) {
+                return false;
+            }
+
+            for (DubboMethodArg dubboMethodArg : args) {
+                int index = dubboMethodArg.getIndex();
+                if (index >= arguments.length) {
+                    throw new IndexOutOfBoundsException("DubboMethodArg index >= parameters.length");
+                }
+                if (!dubboMethodArg.isMatch(arguments[index])) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java
new file mode 100644
index 0000000..940d9a8
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatch.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import java.util.List;
+
+public class ListBoolMatch {
+    private List<BoolMatch> oneof;
+
+    public List<BoolMatch> getOneof() {
+        return oneof;
+    }
+
+    public void setOneof(List<BoolMatch> oneof) {
+        this.oneof = oneof;
+    }
+
+    public boolean isMatch(boolean input) {
+
+        for (BoolMatch boolMatch : oneof) {
+            if (boolMatch.isMatch(input)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java
new file mode 100644
index 0000000..d159952
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatch.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import java.util.List;
+
+public class ListDoubleMatch {
+    private List<DoubleMatch> oneof;
+
+    public List<DoubleMatch> getOneof() {
+        return oneof;
+    }
+
+    public void setOneof(List<DoubleMatch> oneof) {
+        this.oneof = oneof;
+    }
+
+    public boolean isMatch(Double input) {
+
+        for (DoubleMatch doubleMatch : oneof) {
+            if (doubleMatch.isMatch(input)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java
new file mode 100644
index 0000000..4c55578
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatch.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import java.util.List;
+
+public class ListStringMatch {
+    private List<StringMatch> oneof;
+
+    public List<StringMatch> getOneof() {
+        return oneof;
+    }
+
+    public void setOneof(List<StringMatch> oneof) {
+        this.oneof = oneof;
+    }
+
+    public boolean isMatch(String input) {
+
+        for (StringMatch stringMatch : oneof) {
+            if (stringMatch.isMatch(input)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java
new file mode 100644
index 0000000..3a2a85b
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatch.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import static org.apache.dubbo.common.constants.CommonConstants.ANY_VALUE;
+
+public class StringMatch {
+    private String exact;
+    private String prefix;
+    private String regex;
+    private String noempty;
+    private String empty;
+    private String wildcard;
+
+    public String getExact() {
+        return exact;
+    }
+
+    public void setExact(String exact) {
+        this.exact = exact;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    public String getRegex() {
+        return regex;
+    }
+
+    public void setRegex(String regex) {
+        this.regex = regex;
+    }
+
+    public String getNoempty() {
+        return noempty;
+    }
+
+    public void setNoempty(String noempty) {
+        this.noempty = noempty;
+    }
+
+    public String getEmpty() {
+        return empty;
+    }
+
+    public void setEmpty(String empty) {
+        this.empty = empty;
+    }
+
+    public String getWildcard() {
+        return wildcard;
+    }
+
+    public void setWildcard(String wildcard) {
+        this.wildcard = wildcard;
+    }
+
+    public boolean isMatch(String input) {
+        if (getExact() != null && input != null) {
+            return input.equals(getExact());
+        } else if (getPrefix() != null && input != null) {
+            return input.startsWith(getPrefix());
+        } else if (getRegex() != null && input != null) {
+            return input.matches(getRegex());
+        } else if (getWildcard() != null && input != null) {
+            // only supports "*"
+            return input.equals(getWildcard()) || ANY_VALUE.equals(getWildcard());
+        } else if (getEmpty() != null) {
+            return input == null || "".equals(input);
+        } else if (getNoempty() != null) {
+            return input != null && input.length() > 0;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "StringMatch{" + "exact='"
+                + exact + '\'' + ", prefix='"
+                + prefix + '\'' + ", regex='"
+                + regex + '\'' + ", noempty='"
+                + noempty + '\'' + ", empty='"
+                + empty + '\'' + '}';
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcher.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcher.java
new file mode 100644
index 0000000..9592b77
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcher.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.util;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
+import org.apache.dubbo.common.utils.ConcurrentHashSet;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_NO_RULE_LISTENER;
+
+public class MeshRuleDispatcher {
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MeshRuleDispatcher.class);
+
+    private final String appName;
+    private final ConcurrentMap<String, Set<MeshRuleListener>> listenerMap = new ConcurrentHashMap<>();
+
+    public MeshRuleDispatcher(String appName) {
+        this.appName = appName;
+    }
+
+    public synchronized void post(Map<String, List<Map<String, Object>>> ruleMap) {
+        if (ruleMap.isEmpty()) {
+            // clear rule
+            for (Map.Entry<String, Set<MeshRuleListener>> entry : listenerMap.entrySet()) {
+                for (MeshRuleListener listener : entry.getValue()) {
+                    listener.clearRule(appName);
+                }
+            }
+        } else {
+            for (Map.Entry<String, List<Map<String, Object>>> entry : ruleMap.entrySet()) {
+                String ruleType = entry.getKey();
+                Set<MeshRuleListener> listeners = listenerMap.get(ruleType);
+                if (CollectionUtils.isNotEmpty(listeners)) {
+                    for (MeshRuleListener listener : listeners) {
+                        listener.onRuleChange(appName, entry.getValue());
+                    }
+                } else {
+                    logger.warn(
+                            CLUSTER_NO_RULE_LISTENER,
+                            "Receive mesh rule but none of listener has been registered",
+                            "",
+                            "Receive rule but none of listener has been registered. Maybe type not matched. Rule Type: "
+                                    + ruleType);
+                }
+            }
+            // clear rule listener not being notified in this time
+            for (Map.Entry<String, Set<MeshRuleListener>> entry : listenerMap.entrySet()) {
+                if (!ruleMap.containsKey(entry.getKey())) {
+                    for (MeshRuleListener listener : entry.getValue()) {
+                        listener.clearRule(appName);
+                    }
+                }
+            }
+        }
+    }
+
+    public synchronized void register(MeshRuleListener listener) {
+        if (listener == null) {
+            return;
+        }
+        ConcurrentHashMapUtils.computeIfAbsent(listenerMap, listener.ruleSuffix(), (k) -> new ConcurrentHashSet<>())
+                .add(listener);
+    }
+
+    public synchronized void unregister(MeshRuleListener listener) {
+        if (listener == null) {
+            return;
+        }
+        Set<MeshRuleListener> listeners = listenerMap.get(listener.ruleSuffix());
+        if (CollectionUtils.isNotEmpty(listeners)) {
+            listeners.remove(listener);
+        }
+        if (CollectionUtils.isEmpty(listeners)) {
+            listenerMap.remove(listener.ruleSuffix());
+        }
+    }
+
+    public boolean isEmpty() {
+        return listenerMap.isEmpty();
+    }
+
+    /**
+     * For ut only
+     */
+    @Deprecated
+    public Map<String, Set<MeshRuleListener>> getListenerMap() {
+        return listenerMap;
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleListener.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleListener.java
new file mode 100644
index 0000000..17f102b
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleListener.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.util;
+
+import java.util.List;
+import java.util.Map;
+
+public interface MeshRuleListener {
+    void onRuleChange(String appName, List<Map<String, Object>> rules);
+
+    void clearRule(String appName);
+
+    String ruleSuffix();
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/TracingContextProvider.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/TracingContextProvider.java
new file mode 100644
index 0000000..d9fd8c1
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/main/java/org/apache/dubbo/rpc/cluster/router/mesh/util/TracingContextProvider.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.util;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.Invocation;
+
+/**
+ * SPI to get tracing context from 3rd-party tracing utils ( e.g. OpenTracing )
+ */
+@SPI(scope = ExtensionScope.APPLICATION)
+public interface TracingContextProvider {
+
+    /**
+     * Get value from context
+     *
+     * @param invocation invocation
+     * @param key key of value
+     * @return value (null if absent)
+     */
+    String getValue(Invocation invocation, String key);
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java
new file mode 100644
index 0000000..2513982
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshAppRuleListenerTest.java
@@ -0,0 +1,392 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigChangeType;
+import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
+import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.yaml.snakeyaml.LoaderOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.MESH_RULE_DATA_ID_SUFFIX;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+class MeshAppRuleListenerTest {
+
+    private static final String rule1 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n"
+            + "metadata: { name: demo-route }\n"
+            + "spec:\n"
+            + "  host: demo\n"
+            + "  subsets:\n"
+            + "    - labels: { env-sign: xxx, tag1: hello }\n"
+            + "      name: isolation\n"
+            + "    - labels: { env-sign: yyy }\n"
+            + "      name: testing-trunk\n"
+            + "    - labels: { env-sign: zzz }\n"
+            + "      name: testing\n"
+            + "  trafficPolicy:\n"
+            + "    loadBalancer: { simple: ROUND_ROBIN }\n"
+            + "\n";
+    private static final String rule2 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n"
+            + "metadata: { name: demo-route }\n"
+            + "spec:\n"
+            + "  dubbo:\n"
+            + "    - routedetail:\n"
+            + "        - match:\n"
+            + "            - sourceLabels: {trafficLabel: xxx}\n"
+            + "          name: xxx-project\n"
+            + "          route:\n"
+            + "            - destination: {host: demo, subset: isolation}\n"
+            + "        - match:\n"
+            + "            - sourceLabels: {trafficLabel: testing-trunk}\n"
+            + "          name: testing-trunk\n"
+            + "          route:\n"
+            + "            - destination: {host: demo, subset: testing-trunk}\n"
+            + "        - name: testing\n"
+            + "          route:\n"
+            + "            - destination: {host: demo, subset: testing}\n"
+            + "      services:\n"
+            + "        - {regex: ccc}\n"
+            + "  hosts: [demo]\n";
+    private static final String rule3 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n"
+            + "spec:\n"
+            + "  host: demo\n"
+            + "  subsets:\n"
+            + "    - labels: { env-sign: xxx, tag1: hello }\n"
+            + "      name: isolation\n"
+            + "    - labels: { env-sign: yyy }\n"
+            + "      name: testing-trunk\n"
+            + "    - labels: { env-sign: zzz }\n"
+            + "      name: testing\n"
+            + "  trafficPolicy:\n"
+            + "    loadBalancer: { simple: ROUND_ROBIN }\n";
+    private static final String rule4 = "apiVersionservice.dubbo.apache.org/v1alpha1\n";
+    private static final String rule5 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n"
+            + "metadata: { name: demo-route.Type1 }\n"
+            + "spec:\n"
+            + "  host: demo\n"
+            + "\n";
+    private static final String rule6 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n"
+            + "metadata: { name: demo-route.Type1 }\n"
+            + "spec:\n"
+            + "  hosts: [demo]\n";
+    private static final String rule7 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n"
+            + "metadata: { name: demo-route.Type2 }\n"
+            + "spec:\n"
+            + "  host: demo\n"
+            + "\n";
+    private static final String rule8 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n"
+            + "metadata: { name: demo-route.Type2 }\n"
+            + "spec:\n"
+            + "  hosts: [demo]\n";
+
+    @Test
+    void testStandard() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route");
+
+        StandardMeshRuleRouter standardMeshRuleRouter = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf("")));
+        meshAppRuleListener.register(standardMeshRuleRouter);
+
+        meshAppRuleListener.receiveConfigInfo(rule1 + "---\n" + rule2);
+
+        ArgumentCaptor<String> appCaptor = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<List<Map<String, Object>>> ruleCaptor = ArgumentCaptor.forClass(List.class);
+
+        verify(standardMeshRuleRouter, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture());
+
+        List<Map<String, Object>> rulesReceived = ruleCaptor.getValue();
+        assertEquals(2, rulesReceived.size());
+        Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1)));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2)));
+
+        Assertions.assertEquals("demo-route", appCaptor.getValue());
+
+        meshAppRuleListener.receiveConfigInfo("");
+        verify(standardMeshRuleRouter, times(1)).clearRule("demo-route");
+    }
+
+    @Test
+    void register() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route");
+
+        StandardMeshRuleRouter standardMeshRuleRouter1 = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf("")));
+        StandardMeshRuleRouter standardMeshRuleRouter2 = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf("")));
+
+        meshAppRuleListener.register(standardMeshRuleRouter1);
+
+        Assertions.assertEquals(
+                1,
+                meshAppRuleListener
+                        .getMeshRuleDispatcher()
+                        .getListenerMap()
+                        .get(MeshRuleConstants.STANDARD_ROUTER_KEY)
+                        .size());
+        meshAppRuleListener.receiveConfigInfo(rule1 + "---\n" + rule2);
+        meshAppRuleListener.register(standardMeshRuleRouter2);
+        Assertions.assertEquals(
+                2,
+                meshAppRuleListener
+                        .getMeshRuleDispatcher()
+                        .getListenerMap()
+                        .get(MeshRuleConstants.STANDARD_ROUTER_KEY)
+                        .size());
+
+        ArgumentCaptor<String> appCaptor = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<List<Map<String, Object>>> ruleCaptor = ArgumentCaptor.forClass(List.class);
+
+        verify(standardMeshRuleRouter1, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture());
+
+        List<Map<String, Object>> rulesReceived = ruleCaptor.getValue();
+        assertEquals(2, rulesReceived.size());
+        Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1)));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2)));
+
+        Assertions.assertEquals("demo-route", appCaptor.getValue());
+
+        verify(standardMeshRuleRouter2, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture());
+        rulesReceived = ruleCaptor.getValue();
+        assertEquals(2, rulesReceived.size());
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1)));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2)));
+
+        Assertions.assertEquals("demo-route", appCaptor.getValue());
+    }
+
+    @Test
+    void unregister() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route");
+
+        StandardMeshRuleRouter standardMeshRuleRouter1 = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf("")));
+        StandardMeshRuleRouter standardMeshRuleRouter2 = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf("")));
+
+        meshAppRuleListener.register(standardMeshRuleRouter1);
+
+        Assertions.assertEquals(
+                1,
+                meshAppRuleListener
+                        .getMeshRuleDispatcher()
+                        .getListenerMap()
+                        .get(MeshRuleConstants.STANDARD_ROUTER_KEY)
+                        .size());
+        meshAppRuleListener.receiveConfigInfo(rule1 + "---\n" + rule2);
+        meshAppRuleListener.register(standardMeshRuleRouter2);
+        Assertions.assertEquals(
+                2,
+                meshAppRuleListener
+                        .getMeshRuleDispatcher()
+                        .getListenerMap()
+                        .get(MeshRuleConstants.STANDARD_ROUTER_KEY)
+                        .size());
+
+        meshAppRuleListener.unregister(standardMeshRuleRouter1);
+        Assertions.assertEquals(
+                1,
+                meshAppRuleListener
+                        .getMeshRuleDispatcher()
+                        .getListenerMap()
+                        .get(MeshRuleConstants.STANDARD_ROUTER_KEY)
+                        .size());
+
+        meshAppRuleListener.unregister(standardMeshRuleRouter2);
+        Assertions.assertEquals(
+                0, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size());
+    }
+
+    @Test
+    void process() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route");
+
+        StandardMeshRuleRouter standardMeshRuleRouter = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf("")));
+        meshAppRuleListener.register(standardMeshRuleRouter);
+
+        ConfigChangedEvent configChangedEvent = new ConfigChangedEvent(
+                "demo-route" + MESH_RULE_DATA_ID_SUFFIX,
+                DynamicConfiguration.DEFAULT_GROUP,
+                rule1 + "---\n" + rule2,
+                ConfigChangeType.ADDED);
+
+        meshAppRuleListener.process(configChangedEvent);
+
+        ArgumentCaptor<String> appCaptor = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<List<Map<String, Object>>> ruleCaptor = ArgumentCaptor.forClass(List.class);
+
+        verify(standardMeshRuleRouter, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture());
+
+        List<Map<String, Object>> rulesReceived = ruleCaptor.getValue();
+        assertEquals(2, rulesReceived.size());
+        Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1)));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2)));
+
+        configChangedEvent = new ConfigChangedEvent(
+                "demo-route" + MESH_RULE_DATA_ID_SUFFIX,
+                DynamicConfiguration.DEFAULT_GROUP,
+                rule1 + "---\n" + rule2,
+                ConfigChangeType.MODIFIED);
+
+        meshAppRuleListener.process(configChangedEvent);
+
+        verify(standardMeshRuleRouter, times(2)).onRuleChange(appCaptor.capture(), ruleCaptor.capture());
+
+        rulesReceived = ruleCaptor.getValue();
+        assertEquals(2, rulesReceived.size());
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1)));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2)));
+
+        configChangedEvent = new ConfigChangedEvent(
+                "demo-route" + MESH_RULE_DATA_ID_SUFFIX,
+                DynamicConfiguration.DEFAULT_GROUP,
+                "",
+                ConfigChangeType.DELETED);
+        meshAppRuleListener.process(configChangedEvent);
+
+        verify(standardMeshRuleRouter, times(1)).clearRule("demo-route");
+    }
+
+    @Test
+    void testUnknownRule() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route");
+
+        StandardMeshRuleRouter standardMeshRuleRouter = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf("")));
+
+        meshAppRuleListener.register(standardMeshRuleRouter);
+
+        meshAppRuleListener.receiveConfigInfo(rule3 + "---\n" + rule2);
+        ArgumentCaptor<String> appCaptor = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<List<Map<String, Object>>> ruleCaptor = ArgumentCaptor.forClass(List.class);
+
+        verify(standardMeshRuleRouter, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture());
+
+        List<Map<String, Object>> rulesReceived = ruleCaptor.getValue();
+        assertEquals(1, rulesReceived.size());
+        Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2)));
+
+        meshAppRuleListener.receiveConfigInfo(rule1 + "---\n" + rule4);
+
+        verify(standardMeshRuleRouter, times(2)).onRuleChange(appCaptor.capture(), ruleCaptor.capture());
+
+        rulesReceived = ruleCaptor.getValue();
+        assertEquals(1, rulesReceived.size());
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1)));
+
+        meshAppRuleListener.receiveConfigInfo(rule3 + "---\n" + rule4);
+        verify(standardMeshRuleRouter, times(1)).clearRule("demo-route");
+    }
+
+    @Test
+    void testMultipleRule() {
+        MeshAppRuleListener meshAppRuleListener = new MeshAppRuleListener("demo-route");
+
+        AtomicInteger count = new AtomicInteger(0);
+        MeshRuleListener listener1 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                Assertions.assertEquals("demo-route", appName);
+                Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+                Assertions.assertTrue(rules.contains(yaml.load(rule5)));
+                Assertions.assertTrue(rules.contains(yaml.load(rule6)));
+                count.incrementAndGet();
+            }
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type1";
+            }
+        };
+
+        MeshRuleListener listener2 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                Assertions.assertEquals("demo-route", appName);
+                Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+                Assertions.assertTrue(rules.contains(yaml.load(rule7)));
+                Assertions.assertTrue(rules.contains(yaml.load(rule8)));
+                count.incrementAndGet();
+            }
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type2";
+            }
+        };
+
+        MeshRuleListener listener4 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                Assertions.fail();
+            }
+
+            @Override
+            public void clearRule(String appName) {
+                Assertions.assertEquals("demo-route", appName);
+                count.incrementAndGet();
+            }
+
+            @Override
+            public String ruleSuffix() {
+                return "Type4";
+            }
+        };
+
+        StandardMeshRuleRouter standardMeshRuleRouter = Mockito.spy(new StandardMeshRuleRouter(URL.valueOf("")));
+
+        meshAppRuleListener.register(standardMeshRuleRouter);
+        meshAppRuleListener.register(listener1);
+        meshAppRuleListener.register(listener2);
+        meshAppRuleListener.register(listener4);
+
+        meshAppRuleListener.receiveConfigInfo(
+                rule1 + "---\n" + rule2 + "---\n" + rule5 + "---\n" + rule6 + "---\n" + rule7 + "---\n" + rule8);
+        ArgumentCaptor<String> appCaptor = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<List<Map<String, Object>>> ruleCaptor = ArgumentCaptor.forClass(List.class);
+
+        verify(standardMeshRuleRouter, times(1)).onRuleChange(appCaptor.capture(), ruleCaptor.capture());
+
+        List<Map<String, Object>> rulesReceived = ruleCaptor.getValue();
+        assertEquals(2, rulesReceived.size());
+        Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule1)));
+        Assertions.assertTrue(rulesReceived.contains(yaml.load(rule2)));
+
+        Assertions.assertEquals("demo-route", appCaptor.getValue());
+
+        Assertions.assertEquals(3, count.get());
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleCacheTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleCacheTest.java
new file mode 100644
index 0000000..bafdf3c
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleCacheTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.VsDestinationGroup;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRuleSpec;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.Subset;
+import org.apache.dubbo.rpc.cluster.router.state.BitList;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class MeshRuleCacheTest {
+
+    private Invoker<Object> createInvoker(String app) {
+        URL url = URL.valueOf(
+                "dubbo://localhost/DemoInterface?" + (StringUtils.isEmpty(app) ? "" : "remote.application=" + app));
+        Invoker invoker = Mockito.mock(Invoker.class);
+        when(invoker.getUrl()).thenReturn(url);
+        return invoker;
+    }
+
+    @Test
+    void containMapKeyValue() {
+        URL url = mock(URL.class);
+        when(url.getOriginalServiceParameter("test", "key1")).thenReturn("value1");
+        when(url.getOriginalServiceParameter("test", "key2")).thenReturn("value2");
+        when(url.getOriginalServiceParameter("test", "key3")).thenReturn("value3");
+        when(url.getOriginalServiceParameter("test", "key4")).thenReturn("value4");
+
+        Map<String, String> originMap = new HashMap<>();
+
+        originMap.put("key1", "value1");
+        originMap.put("key2", "value2");
+        originMap.put("key3", "value3");
+
+        Map<String, String> inputMap = new HashMap<>();
+
+        inputMap.put("key1", "value1");
+        inputMap.put("key2", "value2");
+
+        assertTrue(MeshRuleCache.isLabelMatch(url, "test", inputMap));
+
+        inputMap.put("key4", "value4");
+        assertTrue(MeshRuleCache.isLabelMatch(url, "test", inputMap));
+    }
+
+    @Test
+    void testBuild() {
+        BitList<Invoker<Object>> invokers =
+                new BitList<>(Arrays.asList(createInvoker(""), createInvoker("unknown"), createInvoker("app1")));
+
+        Subset subset = new Subset();
+        subset.setName("TestSubset");
+        DestinationRule destinationRule = new DestinationRule();
+        DestinationRuleSpec destinationRuleSpec = new DestinationRuleSpec();
+        destinationRuleSpec.setSubsets(Collections.singletonList(subset));
+        destinationRule.setSpec(destinationRuleSpec);
+        VsDestinationGroup vsDestinationGroup = new VsDestinationGroup();
+        vsDestinationGroup.getDestinationRuleList().add(destinationRule);
+        Map<String, VsDestinationGroup> vsDestinationGroupMap = new HashMap<>();
+        vsDestinationGroupMap.put("app1", vsDestinationGroup);
+
+        MeshRuleCache<Object> cache = MeshRuleCache.build("test", invokers, vsDestinationGroupMap);
+        assertEquals(2, cache.getUnmatchedInvokers().size());
+        assertEquals(1, cache.getSubsetInvokers("app1", "TestSubset").size());
+
+        subset.setLabels(Collections.singletonMap("test", "test"));
+        cache = MeshRuleCache.build("test", invokers, vsDestinationGroupMap);
+        assertEquals(3, cache.getUnmatchedInvokers().size());
+        assertEquals(0, cache.getSubsetInvokers("app1", "TestSubset").size());
+
+        invokers = new BitList<>(Arrays.asList(
+                createInvoker(""), createInvoker("unknown"), createInvoker("app1"), createInvoker("app2")));
+        subset.setLabels(null);
+        cache = MeshRuleCache.build("test", invokers, vsDestinationGroupMap);
+        assertEquals(3, cache.getUnmatchedInvokers().size());
+        assertEquals(1, cache.getSubsetInvokers("app1", "TestSubset").size());
+        assertEquals(0, cache.getSubsetInvokers("app2", "TestSubset").size());
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java
new file mode 100644
index 0000000..090c93a
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleManagerTest.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.MeshRuleListener;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.yaml.snakeyaml.LoaderOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class MeshRuleManagerTest {
+    private static final String rule1 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n"
+            + "metadata: { name: demo-route.Type1 }\n"
+            + "spec:\n"
+            + "  host: demo\n"
+            + "\n";
+    private static final String rule2 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n"
+            + "metadata: { name: demo-route.Type1 }\n"
+            + "spec:\n"
+            + "  hosts: [demo]\n";
+    private static final String rule3 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n"
+            + "metadata: { name: demo-route.Type2 }\n"
+            + "spec:\n"
+            + "  host: demo\n"
+            + "\n";
+    private static final String rule4 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n"
+            + "metadata: { name: demo-route.Type2 }\n"
+            + "spec:\n"
+            + "  hosts: [demo]\n";
+
+    private ModuleModel originModule;
+    private ModuleModel moduleModel;
+    private GovernanceRuleRepository ruleRepository;
+    private Set<MeshEnvListenerFactory> envListenerFactories;
+
+    @BeforeEach
+    public void setup() {
+        originModule = ApplicationModel.defaultModel().getDefaultModule();
+        moduleModel = Mockito.spy(originModule);
+
+        ruleRepository = Mockito.mock(GovernanceRuleRepository.class);
+        when(moduleModel.getDefaultExtension(GovernanceRuleRepository.class)).thenReturn(ruleRepository);
+
+        ExtensionLoader<MeshEnvListenerFactory> envListenerFactoryLoader = Mockito.mock(ExtensionLoader.class);
+        envListenerFactories = new HashSet<>();
+        when(envListenerFactoryLoader.getSupportedExtensionInstances()).thenReturn(envListenerFactories);
+        when(moduleModel.getExtensionLoader(MeshEnvListenerFactory.class)).thenReturn(envListenerFactoryLoader);
+    }
+
+    @AfterEach
+    public void teardown() {
+        originModule.destroy();
+    }
+
+    @Test
+    void testRegister1() {
+        MeshRuleManager meshRuleManager = new MeshRuleManager(moduleModel);
+
+        MeshRuleListener meshRuleListener1 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                fail();
+            }
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type1";
+            }
+        };
+
+        meshRuleManager.register("dubbo-demo", meshRuleListener1);
+
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+        verify(ruleRepository, times(1)).getRule("dubbo-demo.MESHAPPRULE", "dubbo", 5000L);
+
+        MeshAppRuleListener meshAppRuleListener =
+                meshRuleManager.getAppRuleListeners().values().iterator().next();
+        verify(ruleRepository, times(1)).addListener("dubbo-demo.MESHAPPRULE", "dubbo", meshAppRuleListener);
+
+        meshRuleManager.register("dubbo-demo", meshRuleListener1);
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+
+        MeshRuleListener meshRuleListener2 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                fail();
+            }
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type2";
+            }
+        };
+        meshRuleManager.register("dubbo-demo", meshRuleListener2);
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+        assertEquals(
+                2, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size());
+
+        meshRuleManager.unregister("dubbo-demo", meshRuleListener1);
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+        assertEquals(
+                1, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size());
+
+        meshRuleManager.unregister("dubbo-demo", meshRuleListener2);
+        assertEquals(0, meshRuleManager.getAppRuleListeners().size());
+
+        verify(ruleRepository, times(1)).removeListener("dubbo-demo.MESHAPPRULE", "dubbo", meshAppRuleListener);
+    }
+
+    @Test
+    void testRegister2() {
+        MeshRuleManager meshRuleManager = new MeshRuleManager(moduleModel);
+
+        AtomicInteger invokeTimes = new AtomicInteger(0);
+        MeshRuleListener meshRuleListener = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                assertEquals("dubbo-demo", appName);
+                Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+                assertTrue(rules.contains(yaml.load(rule1)));
+                assertTrue(rules.contains(yaml.load(rule2)));
+
+                invokeTimes.incrementAndGet();
+            }
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type1";
+            }
+        };
+
+        when(ruleRepository.getRule("dubbo-demo.MESHAPPRULE", "dubbo", 5000L)).thenReturn(rule1 + "---\n" + rule2);
+
+        meshRuleManager.register("dubbo-demo", meshRuleListener);
+
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+        verify(ruleRepository, times(1)).getRule("dubbo-demo.MESHAPPRULE", "dubbo", 5000L);
+        verify(ruleRepository, times(1))
+                .addListener(
+                        "dubbo-demo.MESHAPPRULE",
+                        "dubbo",
+                        meshRuleManager
+                                .getAppRuleListeners()
+                                .values()
+                                .iterator()
+                                .next());
+        assertEquals(1, invokeTimes.get());
+
+        meshRuleManager.register("dubbo-demo", meshRuleListener);
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+    }
+
+    @Test
+    void testRegister3() {
+        MeshEnvListenerFactory meshEnvListenerFactory1 = Mockito.mock(MeshEnvListenerFactory.class);
+        MeshEnvListenerFactory meshEnvListenerFactory2 = Mockito.mock(MeshEnvListenerFactory.class);
+
+        MeshEnvListener meshEnvListener1 = Mockito.mock(MeshEnvListener.class);
+        when(meshEnvListenerFactory1.getListener()).thenReturn(meshEnvListener1);
+        MeshEnvListener meshEnvListener2 = Mockito.mock(MeshEnvListener.class);
+        when(meshEnvListenerFactory2.getListener()).thenReturn(meshEnvListener2);
+
+        envListenerFactories.add(meshEnvListenerFactory1);
+        envListenerFactories.add(meshEnvListenerFactory2);
+
+        MeshRuleManager meshRuleManager = new MeshRuleManager(moduleModel);
+
+        MeshRuleListener meshRuleListener1 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                fail();
+            }
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type1";
+            }
+        };
+
+        when(meshEnvListener1.isEnable()).thenReturn(false);
+        when(meshEnvListener2.isEnable()).thenReturn(true);
+
+        meshRuleManager.register("dubbo-demo", meshRuleListener1);
+
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+        verify(ruleRepository, times(1)).getRule("dubbo-demo.MESHAPPRULE", "dubbo", 5000L);
+        MeshAppRuleListener meshAppRuleListener =
+                meshRuleManager.getAppRuleListeners().values().iterator().next();
+        verify(ruleRepository, times(1)).addListener("dubbo-demo.MESHAPPRULE", "dubbo", meshAppRuleListener);
+
+        verify(meshEnvListener2, times(1)).onSubscribe("dubbo-demo", meshAppRuleListener);
+
+        meshRuleManager.register("dubbo-demo", meshRuleListener1);
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+
+        MeshRuleListener meshRuleListener2 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                fail();
+            }
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type2";
+            }
+        };
+        meshRuleManager.register("dubbo-demo", meshRuleListener2);
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+        assertEquals(
+                2, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size());
+
+        meshRuleManager.unregister("dubbo-demo", meshRuleListener1);
+        assertEquals(1, meshRuleManager.getAppRuleListeners().size());
+        assertEquals(
+                1, meshAppRuleListener.getMeshRuleDispatcher().getListenerMap().size());
+
+        meshRuleManager.unregister("dubbo-demo", meshRuleListener2);
+        assertEquals(0, meshRuleManager.getAppRuleListeners().size());
+
+        verify(ruleRepository, times(1)).removeListener("dubbo-demo.MESHAPPRULE", "dubbo", meshAppRuleListener);
+        verify(meshEnvListener2, times(1)).onUnSubscribe("dubbo-demo");
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java
new file mode 100644
index 0000000..f36b613
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/MeshRuleRouterTest.java
@@ -0,0 +1,455 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.Holder;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider;
+import org.apache.dubbo.rpc.cluster.router.state.BitList;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.yaml.snakeyaml.LoaderOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class MeshRuleRouterTest {
+    private static final String rule1 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: DestinationRule\n"
+            + "metadata: { name: demo-route }\n"
+            + "spec:\n"
+            + "  host: demo\n"
+            + "  subsets:\n"
+            + "    - labels: { env-sign: xxx, tag1: hello }\n"
+            + "      name: isolation\n"
+            + "    - labels: { env-sign: yyy }\n"
+            + "      name: testing-trunk\n"
+            + "    - labels: { env-sign: zzz }\n"
+            + "      name: testing\n"
+            + "  trafficPolicy:\n"
+            + "    loadBalancer: { simple: ROUND_ROBIN }\n"
+            + "\n";
+    private static final String rule2 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n"
+            + "metadata: { name: demo-route }\n"
+            + "spec:\n"
+            + "  dubbo:\n"
+            + "    - routedetail:\n"
+            + "        - match:\n"
+            + "            - attachments: \n"
+            + "                dubboContext: {trafficLabel: {regex: xxx}}\n"
+            + "          name: xxx-project\n"
+            + "          route:\n"
+            + "            - destination: {host: demo, subset: isolation}\n"
+            + "        - match:\n"
+            + "            - attachments: \n"
+            + "                dubboContext: {trafficLabel: {regex: testing-trunk}}\n"
+            + "          name: testing-trunk\n"
+            + "          route:\n"
+            + "            - destination:\n"
+            + "                host: demo\n"
+            + "                subset: testing-trunk\n"
+            + "                fallback:\n"
+            + "                  host: demo\n"
+            + "                  subset: testing\n"
+            + "        - name: testing\n"
+            + "          route:\n"
+            + "            - destination: {host: demo, subset: testing}\n"
+            + "      services:\n"
+            + "        - {regex: ccc}\n"
+            + "  hosts: [demo]\n";
+    private static final String rule3 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n"
+            + "metadata: { name: demo-route }\n"
+            + "spec:\n"
+            + "  dubbo:\n"
+            + "    - routedetail:\n"
+            + "        - match:\n"
+            + "            - attachments: \n"
+            + "                dubboContext: {trafficLabel: {regex: xxx}}\n"
+            + "          name: xxx-project\n"
+            + "          route:\n"
+            + "            - destination: {host: demo, subset: isolation}\n"
+            + "        - match:\n"
+            + "            - attachments: \n"
+            + "                dubboContext: {trafficLabel: {regex: testing-trunk}}\n"
+            + "          name: testing-trunk\n"
+            + "          route:\n"
+            + "            - destination:\n"
+            + "                host: demo\n"
+            + "                subset: testing-trunk\n"
+            + "                fallback:\n"
+            + "                  host: demo\n"
+            + "                  subset: testing\n"
+            + "        - match:\n"
+            + "            - attachments: \n"
+            + "                dubboContext: {trafficLabel: {regex: testing}}\n"
+            + "          name: testing\n"
+            + "          route:\n"
+            + "            - destination: {host: demo, subset: testing}\n"
+            + "  hosts: [demo]\n";
+    private static final String rule4 = "apiVersion: service.dubbo.apache.org/v1alpha1\n" + "kind: VirtualService\n"
+            + "metadata: { name: demo-route }\n"
+            + "spec:\n"
+            + "  dubbo:\n"
+            + "    - routedetail:\n"
+            + "        - match:\n"
+            + "            - attachments: \n"
+            + "                dubboContext: {trafficLabel: {regex: xxx}}\n"
+            + "          name: xxx-project\n"
+            + "          route:\n"
+            + "            - destination: {host: demo, subset: isolation}\n"
+            + "        - match:\n"
+            + "            - attachments: \n"
+            + "                dubboContext: {trafficLabel: {regex: testing-trunk}}\n"
+            + "          name: testing-trunk\n"
+            + "          route:\n"
+            + "            - destination:\n"
+            + "                host: demo\n"
+            + "                subset: testing-trunk\n"
+            + "                fallback:\n"
+            + "                  destination:\n"
+            + "                    host: demo\n"
+            + "                    subset: testing\n"
+            + "            - weight: 10\n"
+            + "              destination:\n"
+            + "                host: demo\n"
+            + "                subset: isolation\n"
+            + "        - match:\n"
+            + "            - attachments: \n"
+            + "                dubboContext: {trafficLabel: {regex: testing}}\n"
+            + "          name: testing\n"
+            + "          route:\n"
+            + "            - destination: {host: demo, subset: testing}\n"
+            + "  hosts: [demo]\n";
+
+    private ModuleModel originModel;
+    private ModuleModel moduleModel;
+    private MeshRuleManager meshRuleManager;
+    private Set<TracingContextProvider> tracingContextProviders;
+    private URL url;
+
+    @BeforeEach
+    public void setup() {
+        originModel = ApplicationModel.defaultModel().getDefaultModule();
+        moduleModel = Mockito.spy(originModel);
+
+        ScopeBeanFactory originBeanFactory = originModel.getBeanFactory();
+        ScopeBeanFactory beanFactory = Mockito.spy(originBeanFactory);
+        when(moduleModel.getBeanFactory()).thenReturn(beanFactory);
+
+        meshRuleManager = Mockito.mock(MeshRuleManager.class);
+        when(beanFactory.getBean(MeshRuleManager.class)).thenReturn(meshRuleManager);
+
+        ExtensionLoader<TracingContextProvider> extensionLoader = Mockito.mock(ExtensionLoader.class);
+        tracingContextProviders = new HashSet<>();
+        when(extensionLoader.getSupportedExtensionInstances()).thenReturn(tracingContextProviders);
+        when(moduleModel.getExtensionLoader(TracingContextProvider.class)).thenReturn(extensionLoader);
+
+        url = URL.valueOf("test://localhost/DemoInterface").setScopeModel(moduleModel);
+    }
+
+    @AfterEach
+    public void teardown() {
+        originModel.destroy();
+    }
+
+    private Invoker<Object> createInvoker(String app) {
+        URL url = URL.valueOf(
+                "dubbo://localhost/DemoInterface?" + (StringUtils.isEmpty(app) ? "" : "remote.application=" + app));
+        Invoker invoker = Mockito.mock(Invoker.class);
+        when(invoker.getUrl()).thenReturn(url);
+        return invoker;
+    }
+
+    private Invoker<Object> createInvoker(Map<String, String> parameters) {
+        URL url = URL.valueOf("dubbo://localhost/DemoInterface?remote.application=app1")
+                .addParameters(parameters);
+        Invoker invoker = Mockito.mock(Invoker.class);
+        when(invoker.getUrl()).thenReturn(url);
+        return invoker;
+    }
+
+    @Test
+    void testNotify() {
+        StandardMeshRuleRouter<Object> meshRuleRouter = new StandardMeshRuleRouter<>(url);
+        meshRuleRouter.notify(null);
+        assertEquals(0, meshRuleRouter.getRemoteAppName().size());
+
+        BitList<Invoker<Object>> invokers =
+                new BitList<>(Arrays.asList(createInvoker(""), createInvoker("unknown"), createInvoker("app1")));
+
+        meshRuleRouter.notify(invokers);
+
+        assertEquals(1, meshRuleRouter.getRemoteAppName().size());
+        assertTrue(meshRuleRouter.getRemoteAppName().contains("app1"));
+        assertEquals(invokers, meshRuleRouter.getInvokerList());
+
+        verify(meshRuleManager, times(1)).register("app1", meshRuleRouter);
+
+        invokers = new BitList<>(Arrays.asList(createInvoker("unknown"), createInvoker("app2")));
+        meshRuleRouter.notify(invokers);
+        verify(meshRuleManager, times(1)).register("app2", meshRuleRouter);
+        verify(meshRuleManager, times(1)).unregister("app1", meshRuleRouter);
+        assertEquals(invokers, meshRuleRouter.getInvokerList());
+
+        meshRuleRouter.stop();
+        verify(meshRuleManager, times(1)).unregister("app2", meshRuleRouter);
+    }
+
+    @Test
+    void testRuleChange() {
+        StandardMeshRuleRouter<Object> meshRuleRouter = new StandardMeshRuleRouter<>(url);
+
+        Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+        List<Map<String, Object>> rules = new LinkedList<>();
+        rules.add(yaml.load(rule1));
+
+        meshRuleRouter.onRuleChange("app1", rules);
+        assertEquals(0, meshRuleRouter.getMeshRuleCache().getAppToVDGroup().size());
+
+        rules.add(yaml.load(rule2));
+        meshRuleRouter.onRuleChange("app1", rules);
+        assertEquals(1, meshRuleRouter.getMeshRuleCache().getAppToVDGroup().size());
+        assertTrue(meshRuleRouter.getMeshRuleCache().getAppToVDGroup().containsKey("app1"));
+
+        meshRuleRouter.onRuleChange("app2", rules);
+        assertEquals(2, meshRuleRouter.getMeshRuleCache().getAppToVDGroup().size());
+        assertTrue(meshRuleRouter.getMeshRuleCache().getAppToVDGroup().containsKey("app1"));
+        assertTrue(meshRuleRouter.getMeshRuleCache().getAppToVDGroup().containsKey("app2"));
+
+        meshRuleRouter.clearRule("app1");
+        assertEquals(1, meshRuleRouter.getMeshRuleCache().getAppToVDGroup().size());
+        assertTrue(meshRuleRouter.getMeshRuleCache().getAppToVDGroup().containsKey("app2"));
+    }
+
+    @Test
+    void testRoute1() {
+        StandardMeshRuleRouter<Object> meshRuleRouter = new StandardMeshRuleRouter<>(url);
+        BitList<Invoker<Object>> invokers =
+                new BitList<>(Arrays.asList(createInvoker(""), createInvoker("unknown"), createInvoker("app1")));
+        assertEquals(invokers, meshRuleRouter.route(invokers.clone(), null, null, false, null));
+        Holder<String> message = new Holder<>();
+        meshRuleRouter.doRoute(invokers.clone(), null, null, true, null, message);
+        assertEquals("MeshRuleCache has not been built. Skip route.", message.get());
+    }
+
+    @Test
+    void testRoute2() {
+        StandardMeshRuleRouter<Object> meshRuleRouter = new StandardMeshRuleRouter<>(url);
+
+        Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+        List<Map<String, Object>> rules = new LinkedList<>();
+        rules.add(yaml.load(rule1));
+        rules.add(yaml.load(rule2));
+        meshRuleRouter.onRuleChange("app1", rules);
+
+        Invoker<Object> isolation = createInvoker(new HashMap<String, String>() {
+            {
+                put("env-sign", "xxx");
+                put("tag1", "hello");
+            }
+        });
+        Invoker<Object> testingTrunk = createInvoker(Collections.singletonMap("env-sign", "yyy"));
+        Invoker<Object> testing = createInvoker(Collections.singletonMap("env-sign", "zzz"));
+
+        BitList<Invoker<Object>> invokers = new BitList<>(Arrays.asList(isolation, testingTrunk, testing));
+        meshRuleRouter.notify(invokers);
+
+        RpcInvocation rpcInvocation = new RpcInvocation();
+
+        rpcInvocation.setServiceName("ccc");
+        rpcInvocation.setAttachment("trafficLabel", "xxx");
+        assertEquals(
+                1,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .size());
+        assertEquals(
+                isolation,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .get(0));
+        Holder<String> message = new Holder<>();
+        meshRuleRouter.doRoute(invokers.clone(), null, rpcInvocation, true, null, message);
+        assertEquals("Match App: app1 Subset: isolation ", message.get());
+
+        rpcInvocation.setAttachment("trafficLabel", "testing-trunk");
+        assertEquals(
+                1,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .size());
+        assertEquals(
+                testingTrunk,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .get(0));
+
+        rpcInvocation.setAttachment("trafficLabel", null);
+        assertEquals(
+                1,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .size());
+        assertEquals(
+                testing,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .get(0));
+
+        rpcInvocation.setServiceName("aaa");
+        assertEquals(invokers, meshRuleRouter.route(invokers.clone(), null, rpcInvocation, false, null));
+        message = new Holder<>();
+        meshRuleRouter.doRoute(invokers.clone(), null, rpcInvocation, true, null, message);
+        assertEquals("Empty protection after routed.", message.get());
+
+        rules = new LinkedList<>();
+        rules.add(yaml.load(rule1));
+        rules.add(yaml.load(rule3));
+        meshRuleRouter.onRuleChange("app1", rules);
+
+        rpcInvocation.setServiceName("ccc");
+        rpcInvocation.setAttachment("trafficLabel", "xxx");
+        assertEquals(
+                1,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .size());
+        assertEquals(
+                isolation,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .get(0));
+
+        rpcInvocation.setAttachment("trafficLabel", "testing-trunk");
+        assertEquals(
+                1,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .size());
+        assertEquals(
+                testingTrunk,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .get(0));
+
+        rpcInvocation.setAttachment("trafficLabel", "testing");
+        assertEquals(
+                1,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .size());
+        assertEquals(
+                testing,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .get(0));
+
+        rpcInvocation.setServiceName("aaa");
+        assertEquals(
+                1,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .size());
+        assertEquals(
+                testing,
+                meshRuleRouter
+                        .route(invokers.clone(), null, rpcInvocation, false, null)
+                        .get(0));
+
+        rpcInvocation.setAttachment("trafficLabel", null);
+        assertEquals(invokers, meshRuleRouter.route(invokers.clone(), null, rpcInvocation, false, null));
+
+        rules = new LinkedList<>();
+        rules.add(yaml.load(rule1));
+        rules.add(yaml.load(rule4));
+        meshRuleRouter.onRuleChange("app1", rules);
+
+        rpcInvocation.setAttachment("trafficLabel", "testing-trunk");
+
+        int testingCount = 0;
+        int isolationCount = 0;
+        for (int i = 0; i < 1000; i++) {
+            BitList<Invoker<Object>> result = meshRuleRouter.route(invokers.clone(), null, rpcInvocation, false, null);
+            assertEquals(1, result.size());
+            if (result.contains(testing)) {
+                testingCount++;
+            } else {
+                isolationCount++;
+            }
+        }
+        assertTrue(isolationCount > testingCount * 10);
+
+        invokers.removeAll(Arrays.asList(isolation, testingTrunk));
+        for (int i = 0; i < 1000; i++) {
+            assertEquals(
+                    1,
+                    meshRuleRouter
+                            .route(invokers.clone(), null, rpcInvocation, false, null)
+                            .size());
+            assertEquals(
+                    testing,
+                    meshRuleRouter
+                            .route(invokers.clone(), null, rpcInvocation, false, null)
+                            .get(0));
+        }
+
+        meshRuleRouter.notify(invokers);
+
+        for (int i = 0; i < 1000; i++) {
+            assertEquals(
+                    1,
+                    meshRuleRouter
+                            .route(invokers.clone(), null, rpcInvocation, false, null)
+                            .size());
+            assertEquals(
+                    testing,
+                    meshRuleRouter
+                            .route(invokers.clone(), null, rpcInvocation, false, null)
+                            .get(0));
+        }
+
+        Invoker<Object> mock = createInvoker(Collections.singletonMap("env-sign", "mock"));
+        invokers = new BitList<>(Arrays.asList(isolation, testingTrunk, testing, mock));
+
+        meshRuleRouter.notify(invokers);
+        invokers.removeAll(Arrays.asList(isolation, testingTrunk, testing));
+        assertEquals(invokers, meshRuleRouter.route(invokers.clone(), null, rpcInvocation, false, null));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouterFactoryTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouterFactoryTest.java
new file mode 100644
index 0000000..c134325
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/route/StandardMeshRuleRouterFactoryTest.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.route;
+
+import org.apache.dubbo.common.URL;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class StandardMeshRuleRouterFactoryTest {
+
+    @Test
+    void getRouter() {
+        StandardMeshRuleRouterFactory ruleRouterFactory = new StandardMeshRuleRouterFactory();
+        Assertions.assertTrue(
+                ruleRouterFactory.getRouter(Object.class, URL.valueOf("")) instanceof StandardMeshRuleRouter);
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java
new file mode 100644
index 0000000..6ca1523
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/DestinationRuleTest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.DestinationRule;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.destination.loadbalance.SimpleLB;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+
+import org.junit.jupiter.api.Test;
+import org.yaml.snakeyaml.Yaml;
+
+import java.util.Map;
+
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.DESTINATION_RULE_KEY;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.KIND_KEY;
+import static org.apache.dubbo.rpc.cluster.router.mesh.route.MeshRuleConstants.VIRTUAL_SERVICE_KEY;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class DestinationRuleTest {
+
+    @Test
+    void parserTest() {
+        Yaml yaml = new Yaml();
+        DestinationRule destinationRule = yaml.loadAs(
+                this.getClass().getClassLoader().getResourceAsStream("DestinationRuleTest.yaml"),
+                DestinationRule.class);
+
+        System.out.println(destinationRule);
+
+        //        apiVersion: service.dubbo.apache.org/v1alpha1
+        //        kind: DestinationRule
+        //        metadata: { name: demo-route }
+        //        spec:
+        //        host: demo
+        //        subsets:
+        //        - labels: { env-sign: xxx,tag1: hello }
+        //        name: isolation
+        //                - labels: { env-sign: yyy }
+        //        name: testing-trunk
+        //                - labels: { env-sign: zzz }
+        //        name: testing
+
+        assertEquals("service.dubbo.apache.org/v1alpha1", destinationRule.getApiVersion());
+        assertEquals(DESTINATION_RULE_KEY, destinationRule.getKind());
+        assertEquals("demo-route", destinationRule.getMetadata().get("name"));
+        assertEquals("demo", destinationRule.getSpec().getHost());
+        assertEquals(3, destinationRule.getSpec().getSubsets().size());
+
+        assertEquals("isolation", destinationRule.getSpec().getSubsets().get(0).getName());
+        assertEquals(
+                2, destinationRule.getSpec().getSubsets().get(0).getLabels().size());
+        assertEquals(
+                "xxx", destinationRule.getSpec().getSubsets().get(0).getLabels().get("env-sign"));
+        assertEquals(
+                "hello",
+                destinationRule.getSpec().getSubsets().get(0).getLabels().get("tag1"));
+
+        assertEquals(
+                "testing-trunk", destinationRule.getSpec().getSubsets().get(1).getName());
+        assertEquals(
+                1, destinationRule.getSpec().getSubsets().get(1).getLabels().size());
+        assertEquals(
+                "yyy", destinationRule.getSpec().getSubsets().get(1).getLabels().get("env-sign"));
+
+        assertEquals("testing", destinationRule.getSpec().getSubsets().get(2).getName());
+        assertEquals(
+                1, destinationRule.getSpec().getSubsets().get(2).getLabels().size());
+        assertEquals(
+                "zzz", destinationRule.getSpec().getSubsets().get(2).getLabels().get("env-sign"));
+
+        assertEquals(
+                SimpleLB.ROUND_ROBIN,
+                destinationRule.getSpec().getTrafficPolicy().getLoadBalancer().getSimple());
+        assertEquals(
+                null,
+                destinationRule.getSpec().getTrafficPolicy().getLoadBalancer().getConsistentHash());
+    }
+
+    @Test
+    void parserMultiRuleTest() {
+        Yaml yaml = new Yaml();
+        Yaml yaml2 = new Yaml();
+        Iterable objectIterable =
+                yaml.loadAll(this.getClass().getClassLoader().getResourceAsStream("DestinationRuleTest2.yaml"));
+        for (Object result : objectIterable) {
+
+            Map resultMap = (Map) result;
+            if (resultMap.get("kind").equals(DESTINATION_RULE_KEY)) {
+                DestinationRule destinationRule = yaml2.loadAs(yaml2.dump(result), DestinationRule.class);
+                System.out.println(destinationRule);
+                assertNotNull(destinationRule);
+            } else if (resultMap.get(KIND_KEY).equals(VIRTUAL_SERVICE_KEY)) {
+                VirtualServiceRule virtualServiceRule = yaml2.loadAs(yaml2.dump(result), VirtualServiceRule.class);
+                System.out.println(virtualServiceRule);
+                assertNotNull(virtualServiceRule);
+            }
+        }
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java
new file mode 100644
index 0000000..ce4effb
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/VirtualServiceRuleTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule;
+
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRoute;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.DubboRouteDetail;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.VirtualServiceRule;
+
+import org.junit.jupiter.api.Test;
+import org.yaml.snakeyaml.Yaml;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+class VirtualServiceRuleTest {
+
+    @Test
+    void parserTest() {
+        Yaml yaml = new Yaml();
+        VirtualServiceRule virtualServiceRule = yaml.loadAs(
+                this.getClass().getClassLoader().getResourceAsStream("VirtualServiceTest.yaml"),
+                VirtualServiceRule.class);
+
+        System.out.println(virtualServiceRule);
+        assertNotNull(virtualServiceRule);
+
+        assertEquals("service.dubbo.apache.org/v1alpha1", virtualServiceRule.getApiVersion());
+        assertEquals("VirtualService", virtualServiceRule.getKind());
+        assertEquals("demo-route", virtualServiceRule.getMetadata().get("name"));
+
+        List<String> hosts = virtualServiceRule.getSpec().getHosts();
+        assertEquals(1, hosts.size());
+        assertEquals("demo", hosts.get(0));
+
+        List<DubboRoute> dubboRoutes = virtualServiceRule.getSpec().getDubbo();
+        assertEquals(1, dubboRoutes.size());
+
+        DubboRoute dubboRoute = dubboRoutes.get(0);
+        assertNull(dubboRoute.getName());
+
+        assertEquals(1, dubboRoute.getServices().size());
+        assertEquals("ccc", dubboRoute.getServices().get(0).getRegex());
+
+        List<DubboRouteDetail> routedetail = dubboRoute.getRoutedetail();
+        DubboRouteDetail firstDubboRouteDetail = routedetail.get(0);
+        DubboRouteDetail secondDubboRouteDetail = routedetail.get(1);
+        DubboRouteDetail thirdDubboRouteDetail = routedetail.get(2);
+
+        assertEquals("xxx-project", firstDubboRouteDetail.getName());
+        assertEquals(
+                "xxx", firstDubboRouteDetail.getMatch().get(0).getSourceLabels().get("trafficLabel"));
+        assertEquals(
+                "demo", firstDubboRouteDetail.getRoute().get(0).getDestination().getHost());
+        assertEquals(
+                "isolation",
+                firstDubboRouteDetail.getRoute().get(0).getDestination().getSubset());
+
+        assertEquals("testing-trunk", secondDubboRouteDetail.getName());
+        assertEquals(
+                "testing-trunk",
+                secondDubboRouteDetail.getMatch().get(0).getSourceLabels().get("trafficLabel"));
+        assertEquals(
+                "demo",
+                secondDubboRouteDetail.getRoute().get(0).getDestination().getHost());
+        assertEquals(
+                "testing-trunk",
+                secondDubboRouteDetail.getRoute().get(0).getDestination().getSubset());
+
+        assertEquals("testing", thirdDubboRouteDetail.getName());
+        assertNull(thirdDubboRouteDetail.getMatch());
+        assertEquals(
+                "demo", thirdDubboRouteDetail.getRoute().get(0).getDestination().getHost());
+        assertEquals(
+                "testing",
+                thirdDubboRouteDetail.getRoute().get(0).getDestination().getSubset());
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java
new file mode 100644
index 0000000..94bf85d
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/DubboMatchRequestTest.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice;
+
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboAttachmentMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.DubboMethodMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match.StringMatch;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class DubboMatchRequestTest {
+
+    @Test
+    void isMatch() {
+        DubboMatchRequest dubboMatchRequest = new DubboMatchRequest();
+
+        // methodMatch
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+        StringMatch nameStringMatch = new StringMatch();
+        nameStringMatch.setExact("sayHello");
+        dubboMethodMatch.setName_match(nameStringMatch);
+
+        dubboMatchRequest.setMethod(dubboMethodMatch);
+
+        RpcInvocation rpcInvocation = new RpcInvocation();
+        rpcInvocation.setMethodName("sayHello");
+        assertTrue(dubboMatchRequest.isMatch(rpcInvocation, new HashMap<>(), Collections.emptySet()));
+
+        rpcInvocation.setMethodName("satHi");
+        assertFalse(dubboMatchRequest.isMatch(rpcInvocation, new HashMap<>(), Collections.emptySet()));
+
+        // sourceLabels
+        Map<String, String> sourceLabels = new HashMap<>();
+        sourceLabels.put("key1", "value1");
+        sourceLabels.put("key2", "value2");
+
+        dubboMatchRequest.setSourceLabels(sourceLabels);
+
+        Map<String, String> inputSourceLabelsMap = new HashMap<>();
+        inputSourceLabelsMap.put("key1", "value1");
+        inputSourceLabelsMap.put("key2", "value2");
+        inputSourceLabelsMap.put("key3", "value3");
+
+        Map<String, String> inputSourceLabelsMap2 = new HashMap<>();
+        inputSourceLabelsMap2.put("key1", "other");
+        inputSourceLabelsMap2.put("key2", "value2");
+        inputSourceLabelsMap2.put("key3", "value3");
+
+        rpcInvocation.setMethodName("sayHello");
+        assertTrue(dubboMatchRequest.isMatch(rpcInvocation, inputSourceLabelsMap, Collections.emptySet()));
+        assertFalse(dubboMatchRequest.isMatch(rpcInvocation, inputSourceLabelsMap2, Collections.emptySet()));
+
+        // tracingContext
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+        Map<String, StringMatch> tracingContextMatchMap = new HashMap<>();
+        StringMatch nameMatch = new StringMatch();
+        nameMatch.setExact("qinliujie");
+        tracingContextMatchMap.put("name", nameMatch);
+        dubboAttachmentMatch.setTracingContext(tracingContextMatchMap);
+        dubboMatchRequest.setAttachments(dubboAttachmentMatch);
+
+        Map<String, String> invokeTracingContextMap = new HashMap<>();
+        invokeTracingContextMap.put("name", "qinliujie");
+        invokeTracingContextMap.put("machineGroup", "test_host");
+        invokeTracingContextMap.put("other", "other");
+
+        TracingContextProvider tracingContextProvider = (invocation, key) -> invokeTracingContextMap.get(key);
+        assertTrue(dubboMatchRequest.isMatch(
+                rpcInvocation, inputSourceLabelsMap, Collections.singleton(tracingContextProvider)));
+
+        Map<String, String> invokeTracingContextMap2 = new HashMap<>();
+        invokeTracingContextMap2.put("name", "jack");
+        invokeTracingContextMap2.put("machineGroup", "test_host");
+        invokeTracingContextMap2.put("other", "other");
+
+        TracingContextProvider tracingContextProvider2 = (invocation, key) -> invokeTracingContextMap2.get(key);
+        assertFalse(dubboMatchRequest.isMatch(
+                rpcInvocation, inputSourceLabelsMap, Collections.singleton(tracingContextProvider2)));
+
+        // dubbo context
+        dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        Map<String, StringMatch> eagleeyecontextMatchMap = new HashMap<>();
+        nameMatch = new StringMatch();
+        nameMatch.setExact("qinliujie");
+        eagleeyecontextMatchMap.put("name", nameMatch);
+        dubboAttachmentMatch.setTracingContext(eagleeyecontextMatchMap);
+
+        Map<String, StringMatch> dubboContextMatchMap = new HashMap<>();
+        StringMatch dpathMatch = new StringMatch();
+        dpathMatch.setExact("PRE");
+        dubboContextMatchMap.put("dpath", dpathMatch);
+        dubboAttachmentMatch.setDubboContext(dubboContextMatchMap);
+
+        dubboMatchRequest.setAttachments(dubboAttachmentMatch);
+
+        Map<String, String> invokeDubboContextMap = new HashMap<>();
+        invokeDubboContextMap.put("dpath", "PRE");
+
+        rpcInvocation.setAttachments(invokeDubboContextMap);
+        TracingContextProvider tracingContextProvider3 = (invocation, key) -> invokeTracingContextMap.get(key);
+        assertTrue(dubboMatchRequest.isMatch(
+                rpcInvocation, inputSourceLabelsMap, Collections.singleton(tracingContextProvider3)));
+
+        Map<String, String> invokeDubboContextMap2 = new HashMap<>();
+        invokeDubboContextMap.put("dpath", "other");
+
+        rpcInvocation.setAttachments(invokeDubboContextMap2);
+        assertFalse(dubboMatchRequest.isMatch(
+                rpcInvocation, inputSourceLabelsMap, Collections.singleton(tracingContextProvider3)));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java
new file mode 100644
index 0000000..fcef780
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/BoolMatchTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class BoolMatchTest {
+
+    @Test
+    void isMatch() {
+        BoolMatch boolMatch = new BoolMatch();
+        boolMatch.setExact(true);
+
+        assertTrue(boolMatch.isMatch(true));
+        assertFalse(boolMatch.isMatch(false));
+
+        boolMatch.setExact(false);
+        assertFalse(boolMatch.isMatch(true));
+        assertTrue(boolMatch.isMatch(false));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java
new file mode 100644
index 0000000..630bb37
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DoubleMatchTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class DoubleMatchTest {
+
+    @Test
+    void exactMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+        doubleMatch.setExact(10.0);
+
+        assertTrue(doubleMatch.isMatch(10.0));
+        assertFalse(doubleMatch.isMatch(9.0));
+    }
+
+    @Test
+    void rangeStartMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch();
+        doubleRangeMatch.setStart(10.0);
+
+        doubleMatch.setRange(doubleRangeMatch);
+
+        assertTrue(doubleMatch.isMatch(10.0));
+        assertFalse(doubleMatch.isMatch(9.0));
+    }
+
+    @Test
+    void rangeEndMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch();
+        doubleRangeMatch.setEnd(10.0);
+
+        doubleMatch.setRange(doubleRangeMatch);
+
+        assertFalse(doubleMatch.isMatch(10.0));
+        assertTrue(doubleMatch.isMatch(9.0));
+    }
+
+    @Test
+    void rangeStartEndMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        DoubleRangeMatch doubleRangeMatch = new DoubleRangeMatch();
+        doubleRangeMatch.setStart(5.0);
+        doubleRangeMatch.setEnd(10.0);
+
+        doubleMatch.setRange(doubleRangeMatch);
+
+        assertTrue(doubleMatch.isMatch(5.0));
+        assertFalse(doubleMatch.isMatch(10.0));
+
+        assertFalse(doubleMatch.isMatch(4.9));
+        assertFalse(doubleMatch.isMatch(10.1));
+
+        assertTrue(doubleMatch.isMatch(6.0));
+    }
+
+    @Test
+    void modMatch() {
+        DoubleMatch doubleMatch = new DoubleMatch();
+
+        doubleMatch.setMod(2.0);
+        doubleMatch.setExact(3.0);
+
+        assertFalse(doubleMatch.isMatch(3.0));
+
+        doubleMatch.setExact(1.0);
+
+        assertTrue(doubleMatch.isMatch(1.0));
+        assertFalse(doubleMatch.isMatch(2.0));
+        assertTrue(doubleMatch.isMatch(3.0));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java
new file mode 100644
index 0000000..2f5fadf
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboAttachmentMatchTest.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.cluster.router.mesh.util.TracingContextProvider;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class DubboAttachmentMatchTest {
+
+    @Test
+    void dubboContextMatch() {
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        Map<String, StringMatch> dubbocontextMatchMap = new HashMap<>();
+
+        StringMatch nameMatch = new StringMatch();
+        nameMatch.setExact("qinliujie");
+        dubbocontextMatchMap.put("name", nameMatch);
+
+        StringMatch machineGroupMatch = new StringMatch();
+        machineGroupMatch.setExact("test_host");
+        dubbocontextMatchMap.put("machineGroup", machineGroupMatch);
+
+        dubboAttachmentMatch.setDubboContext(dubbocontextMatchMap);
+
+        Map<String, String> invokeDubboContextMap = new HashMap<>();
+        invokeDubboContextMap.put("name", "qinliujie");
+        invokeDubboContextMap.put("machineGroup", "test_host");
+        invokeDubboContextMap.put("other", "other");
+
+        RpcInvocation rpcInvocation = new RpcInvocation();
+        rpcInvocation.setAttachments(invokeDubboContextMap);
+
+        assertTrue(dubboAttachmentMatch.isMatch(rpcInvocation, Collections.emptySet()));
+
+        Map<String, String> invokeDubboContextMap2 = new HashMap<>();
+        invokeDubboContextMap2.put("name", "jack");
+        invokeDubboContextMap2.put("machineGroup", "test_host");
+        invokeDubboContextMap2.put("other", "other");
+
+        RpcInvocation rpcInvocation2 = new RpcInvocation();
+        rpcInvocation2.setAttachments(invokeDubboContextMap2);
+
+        assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation2, Collections.emptySet()));
+
+        Map<String, String> invokeDubboContextMap3 = new HashMap<>();
+        invokeDubboContextMap3.put("name", "qinliujie");
+        invokeDubboContextMap3.put("machineGroup", "my_host");
+        invokeDubboContextMap3.put("other", "other");
+
+        RpcInvocation rpcInvocation3 = new RpcInvocation();
+        rpcInvocation3.setAttachments(invokeDubboContextMap3);
+
+        assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation3, Collections.emptySet()));
+    }
+
+    @Test
+    void tracingContextMatch() {
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        Map<String, StringMatch> tracingContextMatchMap = new HashMap<>();
+
+        StringMatch nameMatch = new StringMatch();
+        nameMatch.setExact("qinliujie");
+        tracingContextMatchMap.put("name", nameMatch);
+
+        StringMatch machineGroupMatch = new StringMatch();
+        machineGroupMatch.setExact("test_host");
+        tracingContextMatchMap.put("machineGroup", machineGroupMatch);
+
+        dubboAttachmentMatch.setTracingContext(tracingContextMatchMap);
+
+        Map<String, String> invokeEagleEyeContextMap = new HashMap<>();
+        invokeEagleEyeContextMap.put("name", "qinliujie");
+        invokeEagleEyeContextMap.put("machineGroup", "test_host");
+        invokeEagleEyeContextMap.put("other", "other");
+
+        TracingContextProvider tracingContextProvider = (invocation, key) -> invokeEagleEyeContextMap.get(key);
+        assertTrue(dubboAttachmentMatch.isMatch(
+                Mockito.mock(Invocation.class), Collections.singleton(tracingContextProvider)));
+
+        Map<String, String> invokeTracingContextMap2 = new HashMap<>();
+        invokeTracingContextMap2.put("name", "jack");
+        invokeTracingContextMap2.put("machineGroup", "test_host");
+        invokeTracingContextMap2.put("other", "other");
+
+        TracingContextProvider tracingContextProvider2 = (invocation, key) -> invokeTracingContextMap2.get(key);
+        assertFalse(dubboAttachmentMatch.isMatch(
+                Mockito.mock(Invocation.class), Collections.singleton(tracingContextProvider2)));
+
+        Map<String, String> invokeEagleEyeContextMap3 = new HashMap<>();
+        invokeEagleEyeContextMap3.put("name", "qinliujie");
+        invokeEagleEyeContextMap3.put("machineGroup", "my_host");
+        invokeEagleEyeContextMap3.put("other", "other");
+
+        TracingContextProvider tracingContextProvider3 = (invocation, key) -> invokeEagleEyeContextMap3.get(key);
+        assertFalse(dubboAttachmentMatch.isMatch(
+                Mockito.mock(Invocation.class), Collections.singleton(tracingContextProvider3)));
+    }
+
+    @Test
+    void contextMatch() {
+        DubboAttachmentMatch dubboAttachmentMatch = new DubboAttachmentMatch();
+
+        Map<String, StringMatch> tracingContextMatchMap = new HashMap<>();
+        StringMatch nameMatch = new StringMatch();
+        nameMatch.setExact("qinliujie");
+        tracingContextMatchMap.put("name", nameMatch);
+        dubboAttachmentMatch.setTracingContext(tracingContextMatchMap);
+
+        Map<String, String> invokeTracingContextMap = new HashMap<>();
+        invokeTracingContextMap.put("name", "qinliujie");
+        invokeTracingContextMap.put("machineGroup", "test_host");
+        invokeTracingContextMap.put("other", "other");
+
+        Map<String, StringMatch> dubboContextMatchMap = new HashMap<>();
+        StringMatch dpathMatch = new StringMatch();
+        dpathMatch.setExact("PRE");
+        dubboContextMatchMap.put("dpath", dpathMatch);
+        dubboAttachmentMatch.setDubboContext(dubboContextMatchMap);
+
+        Map<String, String> invokeDubboContextMap = new HashMap<>();
+        invokeDubboContextMap.put("dpath", "PRE");
+
+        TracingContextProvider tracingContextProvider = (invocation, key) -> invokeTracingContextMap.get(key);
+        RpcInvocation rpcInvocation = new RpcInvocation();
+        rpcInvocation.setAttachments(invokeDubboContextMap);
+        assertTrue(dubboAttachmentMatch.isMatch(rpcInvocation, Collections.singleton(tracingContextProvider)));
+
+        Map<String, String> invokeTracingContextMap1 = new HashMap<>();
+        invokeTracingContextMap1.put("name", "jack");
+        invokeTracingContextMap1.put("machineGroup", "test_host");
+        invokeTracingContextMap1.put("other", "other");
+
+        TracingContextProvider tracingContextProvider1 = (invocation, key) -> invokeTracingContextMap1.get(key);
+        RpcInvocation rpcInvocation1 = new RpcInvocation();
+        rpcInvocation1.setAttachments(invokeDubboContextMap);
+        assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation1, Collections.singleton(tracingContextProvider1)));
+
+        Map<String, String> invokeDubboContextMap1 = new HashMap<>();
+        invokeDubboContextMap1.put("dpath", "PRE-2");
+
+        TracingContextProvider tracingContextProvider2 = (invocation, key) -> invokeTracingContextMap.get(key);
+        RpcInvocation rpcInvocation2 = new RpcInvocation();
+        rpcInvocation2.setAttachments(invokeDubboContextMap1);
+        assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation2, Collections.singleton(tracingContextProvider2)));
+
+        TracingContextProvider tracingContextProvider3 = (invocation, key) -> invokeTracingContextMap1.get(key);
+        RpcInvocation rpcInvocation3 = new RpcInvocation();
+        rpcInvocation3.setAttachments(invokeDubboContextMap1);
+        assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation3, Collections.singleton(tracingContextProvider3)));
+
+        Map<String, String> invokeTracingContextMap2 = new HashMap<>();
+        invokeTracingContextMap2.put("machineGroup", "test_host");
+        invokeTracingContextMap2.put("other", "other");
+
+        TracingContextProvider tracingContextProvider4 = (invocation, key) -> invokeTracingContextMap2.get(key);
+        RpcInvocation rpcInvocation4 = new RpcInvocation();
+        rpcInvocation4.setAttachments(invokeDubboContextMap);
+        assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation4, Collections.singleton(tracingContextProvider4)));
+
+        Map<String, String> invokeDubboContextMap2 = new HashMap<>();
+        TracingContextProvider tracingContextProvider5 = (invocation, key) -> invokeTracingContextMap.get(key);
+        RpcInvocation rpcInvocation5 = new RpcInvocation();
+        rpcInvocation5.setAttachments(invokeDubboContextMap2);
+        assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation5, Collections.singleton(tracingContextProvider5)));
+
+        TracingContextProvider tracingContextProvider6 = (invocation, key) -> invokeTracingContextMap2.get(key);
+        RpcInvocation rpcInvocation6 = new RpcInvocation();
+        rpcInvocation5.setAttachments(invokeDubboContextMap2);
+        assertFalse(dubboAttachmentMatch.isMatch(rpcInvocation6, Collections.singleton(tracingContextProvider6)));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java
new file mode 100644
index 0000000..fa1e270
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/DubboMethodMatchTest.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.apache.dubbo.rpc.RpcInvocation;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class DubboMethodMatchTest {
+
+    @Test
+    void nameMatch() {
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+
+        StringMatch nameStringMatch = new StringMatch();
+        nameStringMatch.setExact("sayHello");
+
+        dubboMethodMatch.setName_match(nameStringMatch);
+
+        assertTrue(
+                dubboMethodMatch.isMatch(new RpcInvocation(null, "sayHello", "", "", new Class[] {}, new Object[] {})));
+    }
+
+    @Test
+    void argcMatch() {
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+        dubboMethodMatch.setArgc(1);
+
+        assertFalse(
+                dubboMethodMatch.isMatch(new RpcInvocation(null, "sayHello", "", "", new Class[] {}, new Object[] {})));
+        assertTrue(dubboMethodMatch.isMatch(
+                new RpcInvocation(null, "sayHello", "", "", new Class[] {}, new Object[] {"1"})));
+    }
+
+    @Test
+    void argpMatch() {
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+
+        List<StringMatch> argpMatch = new ArrayList<>();
+
+        StringMatch first = new StringMatch();
+        first.setExact("java.lang.Long");
+
+        StringMatch second = new StringMatch();
+        second.setRegex(".*");
+
+        argpMatch.add(first);
+        argpMatch.add(second);
+
+        dubboMethodMatch.setArgp(argpMatch);
+
+        assertTrue(dubboMethodMatch.isMatch(
+                new RpcInvocation(null, "sayHello", "", "", new Class[] {Long.class, String.class}, new Object[] {})));
+        assertFalse(dubboMethodMatch.isMatch(new RpcInvocation(
+                null, "sayHello", "", "", new Class[] {Long.class, String.class, String.class}, new Object[] {})));
+        assertFalse(
+                dubboMethodMatch.isMatch(new RpcInvocation(null, "sayHello", "", "", new Class[] {}, new Object[] {})));
+    }
+
+    @Test
+    void parametersMatch() {
+
+        DubboMethodMatch dubboMethodMatch = new DubboMethodMatch();
+
+        List<DubboMethodArg> parametersMatch = new ArrayList<>();
+
+        // ----- index 0
+        {
+            DubboMethodArg dubboMethodArg0 = new DubboMethodArg();
+            dubboMethodArg0.setIndex(0);
+
+            ListDoubleMatch listDoubleMatch = new ListDoubleMatch();
+            List<DoubleMatch> oneof = new ArrayList<>();
+
+            DoubleMatch doubleMatch1 = new DoubleMatch();
+            doubleMatch1.setExact(10.0);
+
+            oneof.add(doubleMatch1);
+
+            listDoubleMatch.setOneof(oneof);
+
+            dubboMethodArg0.setNum_value(listDoubleMatch);
+
+            parametersMatch.add(dubboMethodArg0);
+        }
+
+        // -----index 1
+
+        {
+            DubboMethodArg dubboMethodArg1 = new DubboMethodArg();
+            dubboMethodArg1.setIndex(1);
+
+            ListStringMatch listStringMatch = new ListStringMatch();
+
+            List<StringMatch> oneof = new ArrayList<>();
+
+            StringMatch stringMatch1 = new StringMatch();
+            stringMatch1.setExact("sayHello");
+
+            oneof.add(stringMatch1);
+
+            listStringMatch.setOneof(oneof);
+
+            dubboMethodArg1.setStr_value(listStringMatch);
+
+            parametersMatch.add(dubboMethodArg1);
+        }
+
+        dubboMethodMatch.setArgs(parametersMatch);
+
+        assertTrue(dubboMethodMatch.isMatch(new RpcInvocation(
+                null, "test", "", "", new Class[] {int.class, String.class}, new Object[] {10, "sayHello"})));
+        assertFalse(dubboMethodMatch.isMatch(new RpcInvocation(
+                null, "test", "", "", new Class[] {int.class, String.class}, new Object[] {10, "sayHi"})));
+
+        // -----index 2
+
+        {
+            DubboMethodArg dubboMethodArg2 = new DubboMethodArg();
+            dubboMethodArg2.setIndex(2);
+
+            BoolMatch boolMatch = new BoolMatch();
+            boolMatch.setExact(true);
+
+            dubboMethodArg2.setBool_value(boolMatch);
+
+            parametersMatch.add(dubboMethodArg2);
+        }
+
+        assertTrue(dubboMethodMatch.isMatch(new RpcInvocation(
+                null, "test", "", "", new Class[] {int.class, String.class, boolean.class}, new Object[] {
+                    10, "sayHello", true
+                })));
+        assertFalse(dubboMethodMatch.isMatch(new RpcInvocation(
+                null, "test", "", "", new Class[] {int.class, String.class, boolean.class}, new Object[] {
+                    10, "sayHello", false
+                })));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatchTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatchTest.java
new file mode 100644
index 0000000..b7dd0f3
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListBoolMatchTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ListBoolMatchTest {
+
+    @Test
+    void isMatch() {
+        ListBoolMatch listBoolMatch = new ListBoolMatch();
+        List<BoolMatch> oneof = new ArrayList<>();
+
+        BoolMatch boolMatch1 = new BoolMatch();
+        boolMatch1.setExact(true);
+        oneof.add(boolMatch1);
+        listBoolMatch.setOneof(oneof);
+
+        assertTrue(listBoolMatch.isMatch(true));
+        assertFalse(listBoolMatch.isMatch(false));
+
+        BoolMatch boolMatch2 = new BoolMatch();
+        boolMatch2.setExact(false);
+        oneof.add(boolMatch2);
+        listBoolMatch.setOneof(oneof);
+
+        assertTrue(listBoolMatch.isMatch(false));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java
new file mode 100644
index 0000000..0684cd0
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListDoubleMatchTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ListDoubleMatchTest {
+
+    @Test
+    void isMatch() {
+        ListDoubleMatch listDoubleMatch = new ListDoubleMatch();
+        List<DoubleMatch> oneof = new ArrayList<>();
+
+        DoubleMatch doubleMatch1 = new DoubleMatch();
+        doubleMatch1.setExact(10.0);
+
+        DoubleMatch doubleMatch2 = new DoubleMatch();
+        doubleMatch2.setExact(11.0);
+
+        oneof.add(doubleMatch1);
+        oneof.add(doubleMatch2);
+
+        listDoubleMatch.setOneof(oneof);
+
+        assertTrue(listDoubleMatch.isMatch(10.0));
+        assertTrue(listDoubleMatch.isMatch(11.0));
+        assertFalse(listDoubleMatch.isMatch(12.0));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java
new file mode 100644
index 0000000..69c58cf
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/ListStringMatchTest.java
@@ -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.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ListStringMatchTest {
+
+    @Test
+    void isMatch() {
+        ListStringMatch listStringMatch = new ListStringMatch();
+
+        List<StringMatch> oneof = new ArrayList<>();
+
+        StringMatch stringMatch1 = new StringMatch();
+        stringMatch1.setExact("1");
+
+        StringMatch stringMatch2 = new StringMatch();
+        stringMatch2.setExact("2");
+
+        oneof.add(stringMatch1);
+        oneof.add(stringMatch2);
+
+        listStringMatch.setOneof(oneof);
+
+        assertTrue(listStringMatch.isMatch("1"));
+        assertTrue(listStringMatch.isMatch("2"));
+        assertFalse(listStringMatch.isMatch("3"));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java
new file mode 100644
index 0000000..fdae045
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/rule/virtualservice/match/StringMatchTest.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.rule.virtualservice.match;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class StringMatchTest {
+
+    @Test
+    void exactMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setExact("qinliujie");
+
+        assertTrue(stringMatch.isMatch("qinliujie"));
+        assertFalse(stringMatch.isMatch("other"));
+        assertFalse(stringMatch.isMatch(null));
+    }
+
+    @Test
+    void prefixMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setPrefix("org.apache.dubbo.rpc.cluster.router.mesh");
+
+        assertTrue(stringMatch.isMatch("org.apache.dubbo.rpc.cluster.router.mesh.test"));
+        assertFalse(stringMatch.isMatch("com.alibaba.hsf"));
+        assertFalse(stringMatch.isMatch(null));
+    }
+
+    @Test
+    void regxMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setRegex("org.apache.dubbo.rpc.cluster.router.mesh.*");
+
+        assertTrue(stringMatch.isMatch("org.apache.dubbo.rpc.cluster.router.mesh"));
+        assertTrue(stringMatch.isMatch("org.apache.dubbo.rpc.cluster.router.mesh.test"));
+        assertFalse(stringMatch.isMatch("com.alibaba.hsf"));
+        assertFalse(stringMatch.isMatch("com.taobao"));
+    }
+
+    @Test
+    void emptyMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setEmpty("empty");
+
+        assertFalse(stringMatch.isMatch("com.alibaba.hsf"));
+        assertTrue(stringMatch.isMatch(""));
+        assertTrue(stringMatch.isMatch(null));
+    }
+
+    @Test
+    void noEmptyMatch() {
+        StringMatch stringMatch = new StringMatch();
+        stringMatch.setNoempty("noempty");
+
+        assertTrue(stringMatch.isMatch("com.alibaba.hsf"));
+        assertFalse(stringMatch.isMatch(""));
+        assertFalse(stringMatch.isMatch(null));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcherTest.java b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcherTest.java
new file mode 100644
index 0000000..4c0075a
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/java/org/apache/dubbo/rpc/cluster/router/mesh/util/MeshRuleDispatcherTest.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.cluster.router.mesh.util;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+class MeshRuleDispatcherTest {
+
+    @Test
+    void post() {
+        MeshRuleDispatcher meshRuleDispatcher = new MeshRuleDispatcher("TestApp");
+
+        Map<String, List<Map<String, Object>>> ruleMap = new HashMap<>();
+        List<Map<String, Object>> type1 = new LinkedList<>();
+        List<Map<String, Object>> type2 = new LinkedList<>();
+        List<Map<String, Object>> type3 = new LinkedList<>();
+        ruleMap.put("Type1", type1);
+        ruleMap.put("Type2", type2);
+        ruleMap.put("Type3", type3);
+
+        AtomicInteger count = new AtomicInteger(0);
+        MeshRuleListener listener1 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                Assertions.assertEquals("TestApp", appName);
+                Assertions.assertEquals(System.identityHashCode(type1), System.identityHashCode(rules));
+                count.incrementAndGet();
+            }
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type1";
+            }
+        };
+
+        MeshRuleListener listener2 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                Assertions.assertEquals("TestApp", appName);
+                Assertions.assertEquals(System.identityHashCode(type2), System.identityHashCode(rules));
+                count.incrementAndGet();
+            }
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type2";
+            }
+        };
+
+        MeshRuleListener listener4 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {
+                Assertions.fail();
+            }
+
+            @Override
+            public void clearRule(String appName) {
+                Assertions.assertEquals("TestApp", appName);
+                count.incrementAndGet();
+            }
+
+            @Override
+            public String ruleSuffix() {
+                return "Type4";
+            }
+        };
+
+        meshRuleDispatcher.register(listener1);
+        meshRuleDispatcher.register(listener2);
+        meshRuleDispatcher.register(listener4);
+
+        meshRuleDispatcher.post(ruleMap);
+
+        Assertions.assertEquals(3, count.get());
+    }
+
+    @Test
+    void register() {
+        MeshRuleDispatcher meshRuleDispatcher = new MeshRuleDispatcher("TestApp");
+
+        MeshRuleListener listener1 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {}
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type1";
+            }
+        };
+
+        meshRuleDispatcher.register(listener1);
+        meshRuleDispatcher.register(listener1);
+
+        Assertions.assertEquals(
+                1, meshRuleDispatcher.getListenerMap().get("Type1").size());
+        Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type1").contains(listener1));
+    }
+
+    @Test
+    void unregister() {
+        MeshRuleDispatcher meshRuleDispatcher = new MeshRuleDispatcher("TestApp");
+
+        MeshRuleListener listener1 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {}
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type1";
+            }
+        };
+
+        MeshRuleListener listener2 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {}
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type1";
+            }
+        };
+
+        MeshRuleListener listener3 = new MeshRuleListener() {
+            @Override
+            public void onRuleChange(String appName, List<Map<String, Object>> rules) {}
+
+            @Override
+            public void clearRule(String appName) {}
+
+            @Override
+            public String ruleSuffix() {
+                return "Type2";
+            }
+        };
+
+        meshRuleDispatcher.register(listener1);
+        meshRuleDispatcher.register(listener2);
+        meshRuleDispatcher.register(listener3);
+
+        Assertions.assertEquals(
+                2, meshRuleDispatcher.getListenerMap().get("Type1").size());
+        Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type1").contains(listener1));
+        Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type1").contains(listener2));
+        Assertions.assertEquals(
+                1, meshRuleDispatcher.getListenerMap().get("Type2").size());
+        Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type2").contains(listener3));
+
+        meshRuleDispatcher.unregister(listener1);
+        Assertions.assertEquals(
+                1, meshRuleDispatcher.getListenerMap().get("Type1").size());
+        Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type1").contains(listener2));
+        Assertions.assertEquals(
+                1, meshRuleDispatcher.getListenerMap().get("Type2").size());
+        Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type2").contains(listener3));
+
+        meshRuleDispatcher.unregister(listener2);
+        Assertions.assertNull(meshRuleDispatcher.getListenerMap().get("Type1"));
+        Assertions.assertEquals(
+                1, meshRuleDispatcher.getListenerMap().get("Type2").size());
+        Assertions.assertTrue(meshRuleDispatcher.getListenerMap().get("Type2").contains(listener3));
+
+        meshRuleDispatcher.unregister(listener3);
+        Assertions.assertNull(meshRuleDispatcher.getListenerMap().get("Type1"));
+        Assertions.assertNull(meshRuleDispatcher.getListenerMap().get("Type2"));
+    }
+}
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/resources/DestinationRuleTest.yaml b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/resources/DestinationRuleTest.yaml
new file mode 100644
index 0000000..8f2e135
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/resources/DestinationRuleTest.yaml
@@ -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.
+#
+#
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: DestinationRule
+metadata: { name: demo-route }
+spec:
+  host: demo
+  subsets:
+    - labels: { env-sign: xxx, tag1: hello }
+      name: isolation
+    - labels: { env-sign: yyy }
+      name: testing-trunk
+    - labels: { env-sign: zzz }
+      name: testing
+  trafficPolicy:
+    loadBalancer: { simple: ROUND_ROBIN }
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/resources/DestinationRuleTest2.yaml b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/resources/DestinationRuleTest2.yaml
new file mode 100644
index 0000000..178c50e
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/resources/DestinationRuleTest2.yaml
@@ -0,0 +1,58 @@
+#
+#
+#   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.
+#
+#
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: DestinationRule
+metadata: { name: demo-route }
+spec:
+  host: demo
+  subsets:
+    - labels: { env-sign: xxx, tag1: hello }
+      name: isolation
+    - labels: { env-sign: yyy }
+      name: testing-trunk
+    - labels: { env-sign: zzz }
+      name: testing
+  trafficPolicy:
+    loadBalancer: { simple: ROUND_ROBIN }
+
+---
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: VirtualService
+metadata: {name: demo-route}
+spec:
+  dubbo:
+    - routedetail:
+        - match:
+            - sourceLabels: {trafficLabel: xxx}
+          name: xxx-project
+          route:
+            - destination: {host: demo, subset: isolation}
+        - match:
+            - sourceLabels: {trafficLabel: testing-trunk}
+          name: testing-trunk
+          route:
+            - destination: {host: demo, subset: testing-trunk}
+        - name: testing
+          route:
+            - destination: {host: demo, subset: testing}
+      services:
+        - {regex: ccc}
+  hosts: [demo]
diff --git a/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/resources/VirtualServiceTest.yaml b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/resources/VirtualServiceTest.yaml
new file mode 100644
index 0000000..4d3454b
--- /dev/null
+++ b/dubbo-cluster-extensions/dubbo-cluster-router-mesh/src/test/resources/VirtualServiceTest.yaml
@@ -0,0 +1,41 @@
+#
+#
+#   Licensed to the Apache Software Foundation (ASF) under one or more
+#   contributor license agreements.  See the NOTICE file distributed with
+#   this work for additional information regarding copyright ownership.
+#   The ASF licenses this file to You under the Apache License, Version 2.0
+#   (the "License"); you may not use this file except in compliance with
+#   the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+#
+
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: VirtualService
+metadata: {name: demo-route}
+spec:
+  dubbo:
+    - routedetail:
+        - match:
+            - sourceLabels: {trafficLabel: xxx}
+          name: xxx-project
+          route:
+            - destination: {host: demo, subset: isolation}
+        - match:
+            - sourceLabels: {trafficLabel: testing-trunk}
+          name: testing-trunk
+          route:
+            - destination: {host: demo, subset: testing-trunk}
+        - name: testing
+          route:
+            - destination: {host: demo, subset: testing}
+      services:
+        - {regex: ccc}
+  hosts: [demo]
diff --git a/dubbo-cluster-extensions/dubbo-cluster-specify-address-common/pom.xml b/dubbo-cluster-extensions/dubbo-cluster-specify-address-common/pom.xml
index 6343f5b..68a498b 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-specify-address-common/pom.xml
+++ b/dubbo-cluster-extensions/dubbo-cluster-specify-address-common/pom.xml
@@ -27,12 +27,11 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-cluster-specify-address-common</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>3.2.7</version>
             <scope>provided</scope>
         </dependency>
     </dependencies>
diff --git a/dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo2/pom.xml b/dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo2/pom.xml
index 0d61eef..1c6a8ea 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo2/pom.xml
+++ b/dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo2/pom.xml
@@ -27,13 +27,12 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-cluster-specify-address-dubbo2</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo</artifactId>
-            <version>3.2.7</version>
             <optional>true</optional>
         </dependency>
         <dependency>
diff --git a/dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo3/pom.xml b/dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo3/pom.xml
index 1d9f791..ef17d3b 100644
--- a/dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo3/pom.xml
+++ b/dubbo-cluster-extensions/dubbo-cluster-specify-address-dubbo3/pom.xml
@@ -27,13 +27,12 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-cluster-specify-address-dubbo3</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo</artifactId>
-            <version>3.2.7</version>
             <optional>true</optional>
         </dependency>
 
diff --git a/dubbo-cluster-extensions/pom.xml b/dubbo-cluster-extensions/pom.xml
index fff3b5f..e784052 100644
--- a/dubbo-cluster-extensions/pom.xml
+++ b/dubbo-cluster-extensions/pom.xml
@@ -37,6 +37,7 @@
         <module>dubbo-cluster-specify-address-dubbo2</module>
         <module>dubbo-cluster-specify-address-common</module>
         <module>dubbo-cluster-polaris-dubbo2</module>
+        <module>dubbo-cluster-router-mesh</module>
     </modules>
 
 </project>
diff --git a/dubbo-common-extensions/README.md b/dubbo-common-extensions/README.md
new file mode 100644
index 0000000..b1fdc04
--- /dev/null
+++ b/dubbo-common-extensions/README.md
@@ -0,0 +1,22 @@
+# Dubbo Common Extensions
+[中文](./README_ch.md)
+
+Dubbo common utility class extensions, also providing testing functionalities.
+
+Adding Utils utility class, providing checks for presence of zero-argument constructors and whether a class is provided by JDK.
+
+## How to Use?
+### Current Version: 3.2.0
+
+```
+<dependency>
+    <groupId>org.apache.dubbo</groupId>
+    <artifactId>dubbo-common-extensions</artifactId>
+    <version>${dubbo-api-docs-version}</version>
+</dependency>
+```
+Directly call methods provided by the Utils class:
+```
+boolean haveZeroArgConstructor = Utils.checkZeroArgConstructor(TestAClass.class)
+boolean isJdk = Utils.isJdk(String.class)
+```
diff --git a/dubbo-common-extensions/README_ch.md b/dubbo-common-extensions/README_ch.md
new file mode 100644
index 0000000..7177fc3
--- /dev/null
+++ b/dubbo-common-extensions/README_ch.md
@@ -0,0 +1,24 @@
+# dubbo common extensions
+
+[English](./README.md)
+
+dubbo 共用工具类扩展,并提供测试功能.
+
+增加Utils工具类,提供是否有无参构造方法和是否是JDK提供类的检查
+
+## 如何使用?
+
+### 当前版本: 3.2.0
+
+```
+<dependency>
+    <groupId>org.apache.dubbo</groupId>
+    <artifactId>dubbo-common-extensions</artifactId>
+    <version>${dubbo-api-docs-version}</version>
+</dependency>
+```
+直接调用Utils类提供的方法:
+```
+boolean haveZeroArgConstructor = Utils.checkZeroArgConstructor(TestAClass.class)
+boolean isJdk = Utils.isJdk(String.class)
+```
diff --git a/dubbo-common-extensions/src/test/java/org/apache/dubbo/common/utils/TestAClass.java b/dubbo-common-extensions/src/test/java/org/apache/dubbo/common/utils/TestAClass.java
new file mode 100644
index 0000000..905b582
--- /dev/null
+++ b/dubbo-common-extensions/src/test/java/org/apache/dubbo/common/utils/TestAClass.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.dubbo.common.utils;
+
+public class TestAClass {
+
+    private String name;
+
+    public TestAClass(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/dubbo-common-extensions/src/test/java/org/apache/dubbo/common/utils/TestBClass.java b/dubbo-common-extensions/src/test/java/org/apache/dubbo/common/utils/TestBClass.java
new file mode 100644
index 0000000..fc83787
--- /dev/null
+++ b/dubbo-common-extensions/src/test/java/org/apache/dubbo/common/utils/TestBClass.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.utils;
+
+public class TestBClass {
+
+    private String name;
+
+    public TestBClass() {
+    }
+
+    public TestBClass(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/dubbo-common-extensions/src/test/java/org/apache/dubbo/common/utils/UtilsTest.java b/dubbo-common-extensions/src/test/java/org/apache/dubbo/common/utils/UtilsTest.java
new file mode 100644
index 0000000..35825b9
--- /dev/null
+++ b/dubbo-common-extensions/src/test/java/org/apache/dubbo/common/utils/UtilsTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.utils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.dubbo.common.utils.Utils.checkZeroArgConstructor;
+import static org.apache.dubbo.common.utils.Utils.isJdk;
+
+class UtilsTest {
+
+    @Test
+    void testCheckZeroArgConstructor() {
+        Assertions.assertFalse(checkZeroArgConstructor(TestAClass.class));
+        Assertions.assertTrue(checkZeroArgConstructor(TestBClass.class));
+    }
+
+    @Test
+    void testIsJdk() {
+        Assertions.assertFalse(isJdk(TestAClass.class));
+        Assertions.assertFalse(isJdk(TestBClass.class));
+        Assertions.assertTrue(isJdk(String.class));
+    }
+}
diff --git a/dubbo-configcenter-extensions/README.md b/dubbo-configcenter-extensions/README.md
new file mode 100644
index 0000000..fc204d4
--- /dev/null
+++ b/dubbo-configcenter-extensions/README.md
@@ -0,0 +1,238 @@
+# Dubbo ConfigCenter Extension
+
+[中文](README_ch.md)
+> The Dubbo Configcenter Extension is an adapter for registration center modules such as Consul and Etcd, beyond the configuration center supported by [dubbo](https://github.com/apache/dubbo).
+
+## Integrate example
+
+### Consul
+
+
+#### Adding on the interface side
+```java
+public interface DemoService {
+
+    String sayHello(String name);
+
+}
+
+```
+#### Adding provider-side configuration
+- import dependency
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-configcenter-consul</artifactId>
+    <version>3.2.0</version>
+</dependency>
+```
+- `spring/configcenter-provider.xml`
+```xml
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
+    <context:property-placeholder/>
+
+    <dubbo:application name="dubbo-configcenter-consul-provider"/>
+
+    <dubbo:config-center address="consul://${consul.address:localhost}:8500"/>**
+
+    <bean id="demoService" class="org.apache.dubbo.samples.configcenter.impl.DemoServiceImpl"/>
+
+    <dubbo:service interface="org.apache.dubbo.samples.configcenter.api.DemoService" ref="demoService"/>
+</beans>
+```
+- Implementing the interface
+```java
+public class DemoServiceImpl implements DemoService {
+
+    @Override
+    public String sayHello(String name) {
+        System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name +
+                ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
+        return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
+    }
+
+}
+
+```
+
+- Exposing the service
+```java
+public class ConsulProvider {
+
+    public static void main(String[] args) throws Exception {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/configcenter-provider.xml"});
+        context.start();
+
+        System.out.println("dubbo service started");
+        new CountDownLatch(1).await();
+    }
+
+}
+
+```
+
+#### Adding configuration on the consumer side
+- import dependency
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-configcenter-consul</artifactId>
+    <version>3.2.0</version>
+</dependency>
+```
+- `spring/configcenter-consumer.xml`
+```xml
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
+    <context:property-placeholder/>
+
+    <dubbo:application name="dubbo-configcenter-consul-consumer"/>
+
+    <dubbo:config-center highest-priority="false" protocol="consul" address="${consul.address:localhost}:8500"/>
+
+    <dubbo:reference id="demoService" interface="org.apache.dubbo.samples.configcenter.api.DemoService"/>
+
+</beans>
+```
+- Invoking the service
+```java
+public class ConsulConsumer {
+
+    public static void main(String[] args) {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/configcenter-consumer.xml"});
+        context.start();
+        DemoService demoService = context.getBean("demoService", DemoService.class);
+
+        String hello = demoService.sayHello("world");
+        System.out.println(hello);
+
+    }
+}
+
+```
+
+
+### Etcd
+
+
+#### Adding on the interface side
+```java
+public interface DemoService {
+
+    String sayHello(String name);
+
+}
+
+```
+#### Adding provider-side configuration
+- import dependency
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-configcenter-etcd</artifactId>
+    <version>3.2.0</version>
+</dependency>
+```
+- `spring/configcenter-provider.xml`
+```xml
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
+    <context:property-placeholder/>
+
+    <dubbo:application name="dubbo-configcenter-etcd-provider"/>
+
+    <dubbo:config-center address="etcd3://${etcd.address:localhost}:2379"/>**
+
+    <bean id="demoService" class="org.apache.dubbo.samples.configcenter.impl.DemoServiceImpl"/>
+
+    <dubbo:service interface="org.apache.dubbo.samples.configcenter.api.DemoService" ref="demoService"/>
+</beans>
+```
+- Implementing the interface
+```java
+public class DemoServiceImpl implements DemoService {
+
+    @Override
+    public String sayHello(String name) {
+        System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name +
+                ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
+        return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
+    }
+
+}
+
+```
+
+- Exposing the service
+```java
+public class EtcdProvider {
+
+    public static void main(String[] args) throws Exception {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/configcenter-provider.xml"});
+        context.start();
+
+        System.out.println("dubbo service started");
+        new CountDownLatch(1).await();
+    }
+
+}
+
+```
+
+#### Adding configuration on the consumer side
+- import dependency
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-configcenter-consul</artifactId>
+    <version>3.2.0</version>
+</dependency>
+```
+- `spring/configcenter-consumer.xml`
+```xml
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
+    <context:property-placeholder/>
+
+    <dubbo:application name="dubbo-configcenter-etcd-consumer"/>
+
+    <dubbo:config-center highest-priority="false" protocol="etcd3" address="${etcd.address:localhost}:2379"/>
+
+    <dubbo:reference id="demoService" interface="org.apache.dubbo.samples.configcenter.api.DemoService"/>
+
+</beans>
+```
+- Invoking the service
+```java
+public class EtcdConsumer {
+
+    public static void main(String[] args) {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/configcenter-consumer.xml"});
+        context.start();
+        DemoService demoService = context.getBean("demoService", DemoService.class);
+
+        String hello = demoService.sayHello("world");
+        System.out.println(hello);
+
+    }
+}
+
+```
+
diff --git a/dubbo-configcenter-extensions/README_ch.md b/dubbo-configcenter-extensions/README_ch.md
new file mode 100644
index 0000000..d17d7d8
--- /dev/null
+++ b/dubbo-configcenter-extensions/README_ch.md
@@ -0,0 +1,237 @@
+# Dubbo 配置中心扩展
+
+[English](README.md)
+> Dubbo Configcenter Extension是提供给[dubbo](https://github.com/apache/dubbo)支持的配置中心之外的如consul、etcd等注册中心模块的适配。
+
+## 集成示例
+
+### Consul
+
+
+#### interface端添加
+```java
+public interface DemoService {
+
+    String sayHello(String name);
+
+}
+
+```
+#### provider端添加配置
+- 引入依赖
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-configcenter-consul</artifactId>
+    <version>3.2.0</version>
+</dependency>
+```
+- `spring/configcenter-provider.xml`
+```xml
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
+    <context:property-placeholder/>
+
+    <dubbo:application name="dubbo-configcenter-consul-provider"/>
+
+    <dubbo:config-center address="consul://${consul.address:localhost}:8500"/>**
+
+    <bean id="demoService" class="org.apache.dubbo.samples.configcenter.impl.DemoServiceImpl"/>
+
+    <dubbo:service interface="org.apache.dubbo.samples.configcenter.api.DemoService" ref="demoService"/>
+</beans>
+```
+- 实现接口
+```java
+public class DemoServiceImpl implements DemoService {
+
+    @Override
+    public String sayHello(String name) {
+        System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name +
+                ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
+        return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
+    }
+
+}
+
+```
+
+- 暴露服务
+```java
+public class ConsulProvider {
+
+    public static void main(String[] args) throws Exception {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/configcenter-provider.xml"});
+        context.start();
+
+        System.out.println("dubbo service started");
+        new CountDownLatch(1).await();
+    }
+
+}
+
+```
+
+#### consumer端添加配置
+- 引入依赖
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-configcenter-consul</artifactId>
+    <version>3.2.0</version>
+</dependency>
+```
+- `spring/configcenter-consumer.xml`
+```xml
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
+    <context:property-placeholder/>
+
+    <dubbo:application name="dubbo-configcenter-consul-consumer"/>
+
+    <dubbo:config-center highest-priority="false" protocol="consul" address="${consul.address:localhost}:8500"/>
+
+    <dubbo:reference id="demoService" interface="org.apache.dubbo.samples.configcenter.api.DemoService"/>
+
+</beans>
+```
+- 调用服务
+```java
+public class ConsulConsumer {
+
+    public static void main(String[] args) {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/configcenter-consumer.xml"});
+        context.start();
+        DemoService demoService = context.getBean("demoService", DemoService.class);
+
+        String hello = demoService.sayHello("world");
+        System.out.println(hello);
+
+    }
+}
+
+```
+
+
+### Etcd
+
+
+#### interface端添加
+```java
+public interface DemoService {
+
+    String sayHello(String name);
+
+}
+
+```
+#### provider端添加配置
+- 引入依赖
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-configcenter-etcd</artifactId>
+    <version>3.2.0</version>
+</dependency>
+```
+- `spring/configcenter-provider.xml`
+```xml
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
+    <context:property-placeholder/>
+
+    <dubbo:application name="dubbo-configcenter-etcd-provider"/>
+
+    <dubbo:config-center address="etcd3://${etcd.address:localhost}:2379"/>**
+
+    <bean id="demoService" class="org.apache.dubbo.samples.configcenter.impl.DemoServiceImpl"/>
+
+    <dubbo:service interface="org.apache.dubbo.samples.configcenter.api.DemoService" ref="demoService"/>
+</beans>
+```
+- 实现接口
+```java
+public class DemoServiceImpl implements DemoService {
+
+    @Override
+    public String sayHello(String name) {
+        System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name +
+                ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
+        return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
+    }
+
+}
+
+```
+
+- 暴露服务
+```java
+public class EtcdProvider {
+
+    public static void main(String[] args) throws Exception {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/configcenter-provider.xml"});
+        context.start();
+
+        System.out.println("dubbo service started");
+        new CountDownLatch(1).await();
+    }
+
+}
+
+```
+
+#### consumer端添加配置
+- 引入依赖
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-configcenter-consul</artifactId>
+    <version>3.2.0</version>
+</dependency>
+```
+- `spring/configcenter-consumer.xml`
+```xml
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
+       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
+       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
+    <context:property-placeholder/>
+
+    <dubbo:application name="dubbo-configcenter-etcd-consumer"/>
+
+    <dubbo:config-center highest-priority="false" protocol="etcd3" address="${etcd.address:localhost}:2379"/>
+
+    <dubbo:reference id="demoService" interface="org.apache.dubbo.samples.configcenter.api.DemoService"/>
+
+</beans>
+```
+- 调用服务
+```java
+public class EtcdConsumer {
+
+    public static void main(String[] args) {
+        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/configcenter-consumer.xml"});
+        context.start();
+        DemoService demoService = context.getBean("demoService", DemoService.class);
+
+        String hello = demoService.sayHello("world");
+        System.out.println(hello);
+
+    }
+}
+
+```
diff --git a/dubbo-configcenter-extensions/dubbo-configcenter-consul/pom.xml b/dubbo-configcenter-extensions/dubbo-configcenter-consul/pom.xml
index bf18367..316a116 100644
--- a/dubbo-configcenter-extensions/dubbo-configcenter-consul/pom.xml
+++ b/dubbo-configcenter-extensions/dubbo-configcenter-consul/pom.xml
@@ -26,13 +26,12 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-configcenter-consul</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>3.2.0</version>
             <optional>true</optional>
         </dependency>
         <dependency>
diff --git a/dubbo-configcenter-extensions/dubbo-configcenter-etcd/pom.xml b/dubbo-configcenter-extensions/dubbo-configcenter-etcd/pom.xml
index 38511ea..6a40f0e 100644
--- a/dubbo-configcenter-extensions/dubbo-configcenter-etcd/pom.xml
+++ b/dubbo-configcenter-extensions/dubbo-configcenter-etcd/pom.xml
@@ -28,7 +28,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-configcenter-etcd</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
     <description>The etcd implementation of the config-center api</description>
@@ -43,6 +43,11 @@
             <artifactId>jetcd-launcher</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.etcd</groupId>
+            <artifactId>jetcd-test</artifactId>
+            <scope>test</scope>
+        </dependency>
 
         <!--
          <dependency>
@@ -62,13 +67,12 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>3.2.0</version>
             <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-remoting-etcd3</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
 
     </dependencies>
diff --git a/dubbo-configcenter-extensions/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java b/dubbo-configcenter-extensions/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java
index 59b384b..03beb60 100644
--- a/dubbo-configcenter-extensions/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java
+++ b/dubbo-configcenter-extensions/dubbo-configcenter-etcd/src/main/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfiguration.java
@@ -22,6 +22,8 @@
 import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
 import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.remoting.etcd.StateListener;
 import org.apache.dubbo.remoting.etcd.jetcd.JEtcdClient;
@@ -48,6 +50,8 @@
  */
 public class EtcdDynamicConfiguration implements DynamicConfiguration {
 
+    private static final Logger logger = LoggerFactory.getLogger(EtcdDynamicConfiguration.class);
+
     /**
      * The final root path would be: /$NAME_SPACE/config
      */
@@ -71,7 +75,7 @@
                 try {
                     recover();
                 } catch (Exception e) {
-                    // ignore
+                    logger.error("add etcd watch failed", e);
                 }
             }
         });
@@ -164,7 +168,7 @@
 
         @Override
         public void onError(Throwable throwable) {
-            // ignore
+            logger.error("etcd watcher get an error", throwable);
         }
 
         @Override
diff --git a/dubbo-configcenter-extensions/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java b/dubbo-configcenter-extensions/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java
index d944c00..c7276a5 100644
--- a/dubbo-configcenter-extensions/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java
+++ b/dubbo-configcenter-extensions/dubbo-configcenter-etcd/src/test/java/org/apache/dubbo/configcenter/support/etcd/EtcdDynamicConfigurationTest.java
@@ -16,6 +16,8 @@
  */
 
 package org.apache.dubbo.configcenter.support.etcd;
+
+import io.etcd.jetcd.test.EtcdClusterExtension;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.config.configcenter.ConfigChangedEvent;
 import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
@@ -23,12 +25,14 @@
 import io.etcd.jetcd.ByteSequence;
 import io.etcd.jetcd.Client;
 import io.etcd.jetcd.launcher.EtcdCluster;
-import io.etcd.jetcd.launcher.EtcdClusterFactory;
-import org.junit.After;
 import org.junit.Assert;
-import org.junit.Before;
 import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.net.URI;
 import java.util.HashMap;
 import java.util.List;
@@ -43,20 +47,54 @@
  * Unit test for etcd config center support
  * Integrate with https://github.com/etcd-io/jetcd#launcher
  */
+
 @Disabled
 public class EtcdDynamicConfigurationTest {
 
     private static EtcdDynamicConfiguration config;
 
-    public EtcdCluster etcdCluster = EtcdClusterFactory.buildCluster(getClass().getSimpleName(), 3, false);
-
-   //public EtcdCluster etcdCluster= new Etcd.Builder().withClusterName(getClass().getSimpleName()).withNodes(3).withSsl(false).build();
+    private static final Logger logger = LoggerFactory.getLogger(EtcdDynamicConfigurationTest.class);
+    //public EtcdCluster etcdCluster= new Etcd.Builder().withClusterName(getClass().getSimpleName()).withNodes(3).withSsl(false).build();
 
     private static Client client;
 
+    public EtcdCluster etcdCluster;
+
+    // This will involve Docker pulling the image very slowly
+    @BeforeEach
+    public void setUp() {
+        try {
+            EtcdClusterExtension clusterExtension = EtcdClusterExtension.builder()
+                .withClusterName(getClass().getSimpleName())
+                .withNodes(3)
+                .withSsl(false)
+                .build();
+            etcdCluster = clusterExtension.cluster();
+
+            etcdCluster.start();
+
+            client = Client.builder().endpoints(etcdCluster.clientEndpoints()).build();
+
+            List<URI> clientEndPoints = etcdCluster.clientEndpoints();
+
+            String ipAddress = clientEndPoints.get(0).getHost() + ":" + clientEndPoints.get(0).getPort(); //"127.0.0.1:2379";
+
+            String urlForDubbo = "etcd3://" + ipAddress + "/org.apache.dubbo.etcd.testService";
+
+            // timeout in 15 seconds.
+            URL url = URL.valueOf(urlForDubbo).addParameter(SESSION_TIMEOUT_KEY, 15000);
+            config = new EtcdDynamicConfiguration(url);
+        } catch (Exception e) {
+            logger.error("Failed to start etcd cluster", e);
+        }
+    }
 
     @Test
-    public void testGetConfig()  {
+    public void testGetConfig() {
+        if (config == null) {
+            logger.error("Failed to start etcd cluster ,config is null");
+            return;
+        }
         put("/dubbo/config/dubbo/org.apache.dubbo.etcd.testService/configurators", "hello");
         put("/dubbo/config/test/dubbo.properties", "aaa=bbb");
         Assert.assertEquals("hello", config.getConfig("org.apache.dubbo.etcd.testService/configurators", DynamicConfiguration.DEFAULT_GROUP));
@@ -66,7 +104,10 @@
 
     @Test
     public void testAddListener1() throws Exception {
-
+        if (config == null) {
+            logger.error("Failed to start etcd cluster ,config is null");
+            return;
+        }
         CountDownLatch latch = new CountDownLatch(4);
         TestListener listener1 = new TestListener(latch);
         TestListener listener2 = new TestListener(latch);
@@ -85,7 +126,7 @@
         put("/dubbo/config/dubbo/testapp/tagrouters", "new value2");
         Thread.sleep(1000);
 
-        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        Assert.assertTrue(latch.await(1, TimeUnit.MINUTES));
         Assert.assertEquals(1, listener1.getCount("AService/configurators"));
         Assert.assertEquals(1, listener2.getCount("AService/configurators"));
         Assert.assertEquals(1, listener3.getCount("testapp/tagrouters"));
@@ -99,7 +140,6 @@
     }
 
 
-
     private class TestListener implements ConfigurationListener {
         private CountDownLatch latch;
         private String value;
@@ -134,29 +174,15 @@
         }
     }
 
-    //这里会涉及到docker拉取镜像很慢
-    @Before
-    public void setUp() {
 
-        etcdCluster.start();
-
-        client = Client.builder().endpoints(etcdCluster.getClientEndpoints()).build();
-
-        List<URI> clientEndPoints = etcdCluster.getClientEndpoints();
-
-        String ipAddress =clientEndPoints.get(0).getHost() + ":" + clientEndPoints.get(0).getPort(); //"127.0.0.1:2379";
-
-        String urlForDubbo = "etcd3://" + ipAddress + "/org.apache.dubbo.etcd.testService";
-
-        // timeout in 15 seconds.
-        URL url = URL.valueOf(urlForDubbo).addParameter(SESSION_TIMEOUT_KEY, 15000);
-        config = new EtcdDynamicConfiguration(url);
-    }
-
-    @After
+    @AfterEach
     public void tearDown() {
-        etcdCluster.close();
-        client.close();
+        if (etcdCluster != null) {
+            etcdCluster.close();
+        }
+        if (client != null) {
+            client.close();
+        }
     }
 
 }
diff --git a/dubbo-cross-thread-extensions/README.md b/dubbo-cross-thread-extensions/README.md
index 98ceb2a..59399be 100644
--- a/dubbo-cross-thread-extensions/README.md
+++ b/dubbo-cross-thread-extensions/README.md
@@ -1,4 +1,5 @@
 # Dubbo Cross Thread Extensions
+[中文](./README_ch.md)
 
 `dubbo-cross-thread-extensions` copy dubbo.tag cross thread lightly . 
 it can run with skywalking and ttl . 
@@ -85,7 +86,7 @@
 }
 ```
 
-## run with wkywalking and ttl
+## run with skywalking and ttl
 jvm arguments:
 ```
 -javaagent:transmittable-thread-local-2.14.2.jar
diff --git a/dubbo-cross-thread-extensions/README_ch.md b/dubbo-cross-thread-extensions/README_ch.md
new file mode 100644
index 0000000..c30d004
--- /dev/null
+++ b/dubbo-cross-thread-extensions/README_ch.md
@@ -0,0 +1,144 @@
+# Dubbo 跨线程扩展
+
+[English](./README.md)
+
+`dubbo-cross-thread-extensions` 轻量级地复制了 dubbo.tag 的跨线程功能。
+它可以与 SkyWalking 和 TTL 一起运行。
+
+## 集成示例
+### 使用 Byte Buddy 扫描注解
+(您可以使用 ByteBuddyAgent 安装或者使用 `-javaagent=<agentjar>` 进行使用)
+```
+        Instrumentation instrumentation = ByteBuddyAgent.install();
+        RunnableOrCallableActivation.install(instrumentation);
+        RpcContext.getClientAttachment().setAttachment(CommonConstants.TAG_KEY, tag);
+        Callable<String> callable = CallableWrapper.of(new Callable<String>() {
+            @Override
+            public String call() throws Exception {
+                return RpcContext.getClientAttachment().getAttachment(CommonConstants.TAG_KEY);
+            }
+        });
+        ExecutorService threadPool = Executors.newSingleThreadExecutor();
+        Future<String> result = threadPool.submit(callable);
+```
+### 添加注解 @DubboCrossThread
+
+```
+@DubboCrossThread
+public class TargetClass implements Runnable{
+    @Override
+    public void run() {
+        // ...
+    }
+}
+```
+### 包装 Callable 或 Runnable
+```
+Callable<String> callable = CallableWrapper.of(new Callable<String>() {
+    @Override
+    public String call() throws Exception {
+        return null;
+    }
+});
+```
+```
+Runnable runnable = RunnableWrapper.of(new Runnable() {
+    @Override
+    public void run() {
+        // ...
+    }
+});
+```
+## 与 Spring Boot 集成
+
+### 添加一个监听器
+```
+public class DubboCrossThreadAnnotationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
+    private Logger logger = LoggerFactory.getLogger(DubboCrossThreadAnnotationListener.class);
+    private Instrumentation instrumentation;
+
+    @Override
+    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) {
+        RunnableOrCallableActivation.install(this.instrumentation);
+        logger.info("finished byte buddy installation.");
+    }
+
+    public DubboCrossThreadAnnotationListener(Instrumentation instrumentation) {
+        this.instrumentation = instrumentation;
+    }
+
+    private DubboCrossThreadAnnotationListener() {
+
+    }
+}
+
+```
+### 安装 ByteBuddyAgent
+```
+@SpringBootApplication
+@ComponentScan(basePackages = "org.apache.your-package")
+public class SpringBootDemoApplication {
+
+    public static void main(String[] args) {
+       SpringApplication application = new SpringApplication(SpringBootDemoApplication.class);
+       application.addListeners(new DubboCrossThreadAnnotationListener(ByteBuddyAgent.install()));
+       application.run(args);
+    }
+}
+```
+
+## 与 SkyWalking 和 TTL 一起运行
+JVM 参数:
+```
+-javaagent:transmittable-thread-local-2.14.2.jar
+-Dskywalking.agent.application_code=tracecallable-ltf1
+-Dskywalking.agent.service_name=test12
+-Dskywalking.collector.backend_service=172.37.66.195:11800
+-javaagent:skywalking-agent.jar
+```
+示例代码:
+```
+public class MultiAnnotationWithSwTtl {
+    private static TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
+
+    public static void main(String[] args) {
+        Instrumentation instrumentation = ByteBuddyAgent.install();
+        RunnableOrCallableActivation.install(instrumentation);
+        context.set("value-set-in-parent with ttl");
+        RunnableWrapper runnable = RunnableWrapper.of(new Runnable() {
+            @Override
+            public void run() {
+                System.out.println("parent thread traceId=" + TraceContext.traceId());
+                RpcContext.getClientAttachment().setAttachment("dubbo.tag", "tagValue");
+                MyRunnable task = new MyRunnable();
+                ExecutorService executorService = Executors.newSingleThreadExecutor();
+                executorService.submit(task);
+            }
+        });
+        runnable.run();
+    }
+
+    @TraceCrossThread // copy traceContext (include traceId)
+    @DubboCrossThread
+    public static class MyRunnable implements Runnable {
+
+        @Override
+        public void run() {
+            System.out.println("dubbo.tag=" +  RpcContext.getClientAttachment().getAttachment("dubbo.tag"));
+            System.out.println("children thread traceId=" + TraceContext.traceId());
+            System.out.println("ttl context.get()="+context.get());
+        }
+
+    }
+}
+
+```
+输出:
+```
+parent thread traceId=60cfc24e245d4389b9f40b5b38c33ef6.1.16910355654660001
+dubbo.tag=tagValue
+children thread traceId=60cfc24e245d4389b9f40b5b38c33ef6.1.16910355654660001
+ttl context.get()=value-set-in-parent with ttl
+```
+
+
diff --git a/dubbo-cross-thread-extensions/pom.xml b/dubbo-cross-thread-extensions/pom.xml
index 0d3b659..0a8d8e9 100644
--- a/dubbo-cross-thread-extensions/pom.xml
+++ b/dubbo-cross-thread-extensions/pom.xml
@@ -24,6 +24,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-cross-thread-extensions</artifactId>
+    <version>${revision}</version>
     <packaging>jar</packaging>
 
     <properties>
diff --git a/dubbo-cross-thread-extensions/src/main/java/org/apache/dubbo/crossthread/interceptor/RunnableOrCallableActivation.java b/dubbo-cross-thread-extensions/src/main/java/org/apache/dubbo/crossthread/interceptor/RunnableOrCallableActivation.java
index c1fec87..b270a3f 100644
--- a/dubbo-cross-thread-extensions/src/main/java/org/apache/dubbo/crossthread/interceptor/RunnableOrCallableActivation.java
+++ b/dubbo-cross-thread-extensions/src/main/java/org/apache/dubbo/crossthread/interceptor/RunnableOrCallableActivation.java
@@ -18,16 +18,12 @@
 
 import org.apache.dubbo.crossthread.toolkit.DubboCrossThread;
 
+import java.lang.instrument.Instrumentation;
+
 import net.bytebuddy.agent.builder.AgentBuilder;
 import net.bytebuddy.asm.Advice;
 import net.bytebuddy.description.modifier.Visibility;
-import net.bytebuddy.description.type.TypeDescription;
-import net.bytebuddy.dynamic.DynamicType;
 import net.bytebuddy.matcher.ElementMatchers;
-import net.bytebuddy.utility.JavaModule;
-
-import java.lang.instrument.Instrumentation;
-import java.security.ProtectionDomain;
 
 import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
 import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
@@ -46,26 +42,19 @@
             .with(AgentBuilder.TypeStrategy.Default.REBASE)
             .with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
             .type(isAnnotatedWith(DubboCrossThread.class))
-            .transform(new AgentBuilder.Transformer() {
-                @Override
-                public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription,
-                                                        ClassLoader classLoader, JavaModule module,
-                                                        ProtectionDomain protectionDomain) {
-                    return builder
-                        .defineField(FIELD_NAME_DUBBO_TAG, String.class, Visibility.PUBLIC)
-                        .visit(Advice.to(RunnableOrCallableMethodInterceptor.class).on(
-                            ElementMatchers.isMethod().and(
-                                ElementMatchers.named(RUN_METHOD_NAME).and(takesArguments(0))
-                                    .or(ElementMatchers.named(CALL_METHOD_NAME).and(takesArguments(0)))
-                                    .or(ElementMatchers.named(APPLY_METHOD_NAME).and(takesArguments(0)))
-                                    .or(ElementMatchers.named(ACCEPT_METHOD_NAME).and(takesArguments(0)))
-                            )
-                        ))
-                        .visit(Advice.to(RunnableOrCallableConstructInterceptor.class).on(
-                            ElementMatchers.isConstructor()
-                        ));
-                }
-            })
+            .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder
+                .defineField(FIELD_NAME_DUBBO_TAG, String.class, Visibility.PUBLIC)
+                .visit(Advice.to(RunnableOrCallableMethodInterceptor.class).on(
+                    ElementMatchers.isMethod().and(
+                        ElementMatchers.named(RUN_METHOD_NAME).and(takesArguments(0))
+                            .or(ElementMatchers.named(CALL_METHOD_NAME).and(takesArguments(0)))
+                            .or(ElementMatchers.named(APPLY_METHOD_NAME).and(takesArguments(0)))
+                            .or(ElementMatchers.named(ACCEPT_METHOD_NAME).and(takesArguments(0)))
+                    )
+                ))
+                .visit(Advice.to(RunnableOrCallableConstructInterceptor.class).on(
+                    ElementMatchers.isConstructor()
+                )))
             .installOn(instrumentation);
     }
 }
diff --git a/dubbo-cross-thread-extensions/src/test/java/org/apache/dubbo/crossthread/DubboCrossThreadTest.java b/dubbo-cross-thread-extensions/src/test/java/org/apache/dubbo/crossthread/DubboCrossThreadTest.java
index ca43052..e1433ed 100644
--- a/dubbo-cross-thread-extensions/src/test/java/org/apache/dubbo/crossthread/DubboCrossThreadTest.java
+++ b/dubbo-cross-thread-extensions/src/test/java/org/apache/dubbo/crossthread/DubboCrossThreadTest.java
@@ -16,6 +16,12 @@
  */
 package org.apache.dubbo.crossthread;
 
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.crossthread.interceptor.RunnableOrCallableActivation;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.crossthread.toolkit.CallableWrapper;
+import org.apache.dubbo.crossthread.toolkit.RunnableWrapper;
+
 import java.lang.instrument.Instrumentation;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
@@ -27,28 +33,18 @@
 import java.util.concurrent.TimeoutException;
 
 import net.bytebuddy.agent.ByteBuddyAgent;
-import org.apache.dubbo.common.constants.CommonConstants;
-import org.apache.dubbo.crossthread.interceptor.RunnableOrCallableActivation;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.crossthread.toolkit.CallableWrapper;
-import org.apache.dubbo.crossthread.toolkit.RunnableWrapper;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-public class DubboCrossThreadTest {
+class DubboCrossThreadTest {
     @Test
-    public void crossThreadCallableTest() throws ExecutionException, InterruptedException, TimeoutException {
+    void crossThreadCallableTest() throws ExecutionException, InterruptedException, TimeoutException {
         Instrumentation instrumentation = ByteBuddyAgent.install();
         RunnableOrCallableActivation.install(instrumentation);
         String tag = "beta";
         RpcContext.getClientAttachment().setAttachment(CommonConstants.TAG_KEY, tag);
-        Callable<String> callable = CallableWrapper.of(new Callable<String>() {
-            @Override
-            public String call() throws Exception {
-                return RpcContext.getClientAttachment().getAttachment(CommonConstants.TAG_KEY);
-            }
-        });
+        Callable<String> callable = CallableWrapper.of(() -> RpcContext.getClientAttachment().getAttachment(CommonConstants.TAG_KEY));
         ExecutorService threadPool = Executors.newSingleThreadExecutor();
         Future<String> submit = threadPool.submit(callable);
         assertEquals(tag, submit.get(1, TimeUnit.SECONDS));
@@ -58,19 +54,16 @@
     private volatile String tagCrossThread = null;
 
     @Test
-    public void crossThreadRunnableTest() throws ExecutionException, InterruptedException {
+    void crossThreadRunnableTest() throws InterruptedException {
         Instrumentation instrumentation = ByteBuddyAgent.install();
         RunnableOrCallableActivation.install(instrumentation);
         String tag = "beta";
         RpcContext.getClientAttachment().setAttachment(CommonConstants.TAG_KEY, tag);
         final CountDownLatch latch = new CountDownLatch(1);
-        Runnable runnable = RunnableWrapper.of(new Runnable() {
-            @Override
-            public void run() {
-                String tag = RpcContext.getClientAttachment().getAttachment(CommonConstants.TAG_KEY);
-                tagCrossThread = tag;
-                latch.countDown();
-            }
+        Runnable runnable = RunnableWrapper.of(() -> {
+            String tag1 = RpcContext.getClientAttachment().getAttachment(CommonConstants.TAG_KEY);
+            tagCrossThread = tag1;
+            latch.countDown();
         });
         ExecutorService threadPool = Executors.newSingleThreadExecutor();
         threadPool.submit(runnable);
diff --git a/dubbo-extensions-dependencies-bom/pom.xml b/dubbo-extensions-dependencies-bom/pom.xml
index 6baf554..4354fb0 100644
--- a/dubbo-extensions-dependencies-bom/pom.xml
+++ b/dubbo-extensions-dependencies-bom/pom.xml
@@ -89,8 +89,8 @@
     </issueManagement>
 
     <properties>
-        <revision>1.0.5-SNAPSHOT</revision>
-        <dubbo.version>3.1.2</dubbo.version>
+        <revision>3.2.1-SNAPSHOT</revision>
+        <dubbo.version>3.2.11</dubbo.version>
         <spring.version>5.2.9.RELEASE</spring.version>
         <spring-boot.version>2.4.1</spring-boot.version>
 
@@ -104,7 +104,7 @@
         <thrift_version>0.12.0</thrift_version>
         <jedis_version>3.7.0</jedis_version>
         <embedded_redis_version>0.10.0</embedded_redis_version>
-        <commons_lang3_version>3.8.1</commons_lang3_version>
+        <commons_lang3_version>3.14.0</commons_lang3_version>
         <jaxb_version>2.2.7</jaxb_version>
         <activation_version>1.2.0</activation_version>
         <cxf_version>3.1.15</cxf_version>
@@ -121,9 +121,9 @@
         <mina_version>1.1.7</mina_version>
         <slf4j_version>1.7.25</slf4j_version>
         <grizzly_version>2.4.4</grizzly_version>
-        <jetcd_version>0.5.7</jetcd_version>
+        <jetcd_version>0.7.7</jetcd_version>
         <grpc.version>1.53.0</grpc.version>
-        <etcd_launcher_version>0.5.7</etcd_launcher_version>
+        <etcd_launcher_version>0.7.7</etcd_launcher_version>
         <netty4_version>4.1.66.Final</netty4_version>
         <consul_process_version>2.2.1</consul_process_version>
         <consul_version>1.4.2</consul_version>
@@ -134,15 +134,18 @@
         <sofa_registry_version>5.2.0</sofa_registry_version>
         <logback_version>1.3.12</logback_version>
         <rs_api_version>2.0</rs_api_version>
-        <resteasy_version>3.0.20.Final</resteasy_version>
+        <resteasy_version>3.15.6.Final</resteasy_version>
+        <codehaus-jackson_version>1.9.13</codehaus-jackson_version>
+        <swagger_version>1.6.14</swagger_version>
         <polaris_adapter_version>0.2.1</polaris_adapter_version>
         <maven_flatten_version>1.2.5</maven_flatten_version>
         <byte-buddy.version>1.14.5</byte-buddy.version>
         <commons_net_version>3.9.0</commons_net_version>
-        <snakeyaml_version>2.0</snakeyaml_version>
+        <snakeyaml_version>2.2</snakeyaml_version>
         <protobuf-java_version>3.25.1</protobuf-java_version>
         <bouncycastle-bcprov_version>1.70</bouncycastle-bcprov_version>
         <envoy_api_version>0.1.35</envoy_api_version>
+        <mockito_version>5.11.0</mockito_version>
     </properties>
 
     <dependencyManagement>
@@ -161,7 +164,13 @@
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
-
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring-boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
             <dependency>
                 <groupId>org.apache.dubbo</groupId>
                 <artifactId>dubbo-core-spi</artifactId>
@@ -207,6 +216,14 @@
                 </exclusions>
             </dependency>
 
+            <!-- Test lib -->
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-test</artifactId>
+                <version>${spring.version}</version>
+                <scope>test</scope>
+            </dependency>
+
             <dependency>
                 <groupId>com.caucho</groupId>
                 <artifactId>hessian</artifactId>
@@ -394,6 +411,11 @@
                 </exclusions>
             </dependency>
             <dependency>
+                <groupId>io.etcd</groupId>
+                <artifactId>jetcd-test</artifactId>
+                <version>${jetcd_version}</version>
+            </dependency>
+            <dependency>
                 <groupId>io.grpc</groupId>
                 <artifactId>grpc-core</artifactId>
                 <version>${grpc.version}</version>
@@ -517,6 +539,54 @@
                     </exclusion>
                 </exclusions>
             </dependency>
+            <!--            for dubbo-rpc-rest-->
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>resteasy-jdk-http</artifactId>
+                <version>${resteasy_version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>resteasy-jackson-provider</artifactId>
+                <version>${resteasy_version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.codehaus.jackson</groupId>
+                <artifactId>jackson-core-asl</artifactId>
+                <version>${codehaus-jackson_version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.codehaus.jackson</groupId>
+                <artifactId>jackson-mapper-asl</artifactId>
+                <version>${codehaus-jackson_version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.codehaus.jackson</groupId>
+                <artifactId>jackson-jaxrs</artifactId>
+                <version>${codehaus-jackson_version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.codehaus.jackson</groupId>
+                <artifactId>jackson-xc</artifactId>
+                <version>${codehaus-jackson_version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.resteasy</groupId>
+                <artifactId>resteasy-jaxb-provider</artifactId>
+                <version>${resteasy_version}</version>
+            </dependency>
+            <!-- swagger -->
+            <dependency>
+                <groupId>io.swagger</groupId>
+                <artifactId>swagger-annotations</artifactId>
+                <version>${swagger_version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.swagger</groupId>
+                <artifactId>swagger-jaxrs</artifactId>
+                <version>${swagger_version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>com.tencent.polaris</groupId>
                 <artifactId>polaris-adapter-dubbo</artifactId>
@@ -572,6 +642,16 @@
                 <artifactId>api</artifactId>
                 <version>${envoy_api_version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.dubbo.extensions</groupId>
+                <artifactId>dubbo-api-docs-annotations</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.dubbo.extensions</groupId>
+                <artifactId>dubbo-api-docs-core</artifactId>
+                <version>${revision}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
diff --git a/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-circuitbreaker-dubbo2/pom.xml b/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-circuitbreaker-dubbo2/pom.xml
index 4abcc6d..de2c500 100644
--- a/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-circuitbreaker-dubbo2/pom.xml
+++ b/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-circuitbreaker-dubbo2/pom.xml
@@ -21,7 +21,8 @@
     <parent>
         <artifactId>dubbo-filter-polaris-dubbo2</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
diff --git a/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-ratelimit-dubbo2/pom.xml b/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-ratelimit-dubbo2/pom.xml
index 88669eb..e57fdad 100644
--- a/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-ratelimit-dubbo2/pom.xml
+++ b/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/dubbo-filter-polaris-ratelimit-dubbo2/pom.xml
@@ -21,7 +21,8 @@
     <parent>
         <artifactId>dubbo-filter-polaris-dubbo2</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
diff --git a/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/pom.xml b/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/pom.xml
index bb845ac..ac5cd08 100644
--- a/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/pom.xml
+++ b/dubbo-filter-extensions/dubbo-filter-polaris-dubbo2/pom.xml
@@ -29,7 +29,7 @@
     <artifactId>dubbo-filter-polaris-dubbo2</artifactId>
     <packaging>pom</packaging>
     <name>dubbo-filter-polaris-dubbo2</name>
-    <version>1.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>Dubbo2 filter extension for PolarisMesh, support circuitbreaking, ratelimit, metric capabilities.</description>
     <modules>
         <module>dubbo-filter-polaris-circuitbreaker-dubbo2</module>
diff --git a/dubbo-filter-extensions/dubbo-filter-seata/pom.xml b/dubbo-filter-extensions/dubbo-filter-seata/pom.xml
index 71380f1..dd77702 100644
--- a/dubbo-filter-extensions/dubbo-filter-seata/pom.xml
+++ b/dubbo-filter-extensions/dubbo-filter-seata/pom.xml
@@ -28,7 +28,7 @@
 
     <artifactId>dubbo-filter-seata</artifactId>
     <name>${project.artifactId}</name>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
 
     <dependencies>
         <dependency>
diff --git a/dubbo-filter-extensions/dubbo-filter-seata/src/main/java/org/apache/dubbo/seata/SeataTransactionPropagationProviderFilter.java b/dubbo-filter-extensions/dubbo-filter-seata/src/main/java/org/apache/dubbo/seata/SeataTransactionPropagationProviderFilter.java
index e678b11..baff10e 100644
--- a/dubbo-filter-extensions/dubbo-filter-seata/src/main/java/org/apache/dubbo/seata/SeataTransactionPropagationProviderFilter.java
+++ b/dubbo-filter-extensions/dubbo-filter-seata/src/main/java/org/apache/dubbo/seata/SeataTransactionPropagationProviderFilter.java
@@ -37,15 +37,19 @@
 public class SeataTransactionPropagationProviderFilter implements Filter {
     private static final Logger LOGGER = LoggerFactory.getLogger(SeataTransactionPropagationProviderFilter.class);
 
+    private static final String LOWER_KEY_XID = RootContext.KEY_XID.toLowerCase();
+
+    private static final String LOWER_KEY_BRANCH_TYPE = RootContext.KEY_BRANCH_TYPE.toLowerCase();
+
     @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
         String rpcXid = invocation.getAttachment(RootContext.KEY_XID);
         if (rpcXid == null) {
-            rpcXid = invocation.getAttachment(RootContext.KEY_XID.toLowerCase());
+            rpcXid = invocation.getAttachment(LOWER_KEY_XID);
         }
         String rpcBranchType = invocation.getAttachment(RootContext.KEY_BRANCH_TYPE);
         if (rpcBranchType == null) {
-            rpcBranchType = invocation.getAttachment(RootContext.KEY_BRANCH_TYPE.toLowerCase());
+            rpcBranchType = invocation.getAttachment(LOWER_KEY_BRANCH_TYPE);
         }
         if (LOGGER.isDebugEnabled()) {
             LOGGER.debug("Server side xid in RpcContext[" + rpcXid + "]");
diff --git a/dubbo-filter-extensions/dubbo-filter-seata/src/test/java/org/apache/dubbo/seata/SeataTransactionPropagationConsumerFilterTest.java b/dubbo-filter-extensions/dubbo-filter-seata/src/test/java/org/apache/dubbo/seata/SeataTransactionPropagationConsumerFilterTest.java
index 423e094..93261be 100644
--- a/dubbo-filter-extensions/dubbo-filter-seata/src/test/java/org/apache/dubbo/seata/SeataTransactionPropagationConsumerFilterTest.java
+++ b/dubbo-filter-extensions/dubbo-filter-seata/src/test/java/org/apache/dubbo/seata/SeataTransactionPropagationConsumerFilterTest.java
@@ -29,9 +29,9 @@
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
-public class SeataTransactionPropagationConsumerFilterTest {
+class SeataTransactionPropagationConsumerFilterTest {
     @Test
-    public void test() {
+    void test() {
         ApplicationModel applicationModel = FrameworkModel.defaultModel().newApplication();
         ModuleModel moduleModel = applicationModel.newModule();
 
diff --git a/dubbo-filter-extensions/dubbo-filter-seata/src/test/java/org/apache/dubbo/seata/SeataTransactionPropagationProviderFilterTest.java b/dubbo-filter-extensions/dubbo-filter-seata/src/test/java/org/apache/dubbo/seata/SeataTransactionPropagationProviderFilterTest.java
index a5e0f76..88d05c5 100644
--- a/dubbo-filter-extensions/dubbo-filter-seata/src/test/java/org/apache/dubbo/seata/SeataTransactionPropagationProviderFilterTest.java
+++ b/dubbo-filter-extensions/dubbo-filter-seata/src/test/java/org/apache/dubbo/seata/SeataTransactionPropagationProviderFilterTest.java
@@ -35,7 +35,7 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
 
-public class SeataTransactionPropagationProviderFilterTest {
+class SeataTransactionPropagationProviderFilterTest {
     private final AtomicReference<Function<Invocation, Result>> invokeFunction = new AtomicReference<>();
 
     private Invoker invoker = new Invoker() {
@@ -65,9 +65,8 @@
         }
     };
 
-
     @Test
-    public void test() {
+    void test() {
         ApplicationModel applicationModel = FrameworkModel.defaultModel().newApplication();
         ModuleModel moduleModel = applicationModel.newModule();
 
diff --git a/dubbo-gateway-extensions/dubbo-gateway-provider/pom.xml b/dubbo-gateway-extensions/dubbo-gateway-provider/pom.xml
index c673308..f31c0cc 100644
--- a/dubbo-gateway-extensions/dubbo-gateway-provider/pom.xml
+++ b/dubbo-gateway-extensions/dubbo-gateway-provider/pom.xml
@@ -36,7 +36,6 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo</artifactId>
-            <version>3.2.0</version>
             <optional>true</optional>
         </dependency>
         <dependency>
diff --git a/dubbo-gateway-extensions/dubbo-gateway-provider/src/main/java/org/apache/dubbo/gateway/provider/SnfByteAccessor.java b/dubbo-gateway-extensions/dubbo-gateway-provider/src/main/java/org/apache/dubbo/gateway/provider/SnfByteAccessor.java
index 9adf602..683e7ea 100644
--- a/dubbo-gateway-extensions/dubbo-gateway-provider/src/main/java/org/apache/dubbo/gateway/provider/SnfByteAccessor.java
+++ b/dubbo-gateway-extensions/dubbo-gateway-provider/src/main/java/org/apache/dubbo/gateway/provider/SnfByteAccessor.java
@@ -17,12 +17,17 @@
 
 package org.apache.dubbo.gateway.provider;
 
+import org.apache.dubbo.common.io.UnsafeByteArrayInputStream;
 import org.apache.dubbo.remoting.Channel;
 import org.apache.dubbo.remoting.exchange.Request;
+import org.apache.dubbo.remoting.exchange.Response;
+import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.protocol.dubbo.ByteAccessor;
 import org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation;
+import org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcResult;
 
+import java.io.IOException;
 import java.io.InputStream;
 
 /**
@@ -43,4 +48,22 @@
 
         return new SnfDecodeableRpcInvocation(frameworkModel, channel, req, is, proto);
     }
+
+    @Override
+    public DecodeableRpcResult getRpcResult(Channel channel, Response res, InputStream is, Invocation invocation, byte proto) {
+        try {
+            return new DecodeableRpcResult(
+                channel, res, new UnsafeByteArrayInputStream(readMessageData(is)), invocation, proto);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+    private byte[] readMessageData(InputStream is) throws IOException {
+        if (is.available() > 0) {
+            byte[] result = new byte[is.available()];
+            is.read(result);
+            return result;
+        }
+        return new byte[] {};
+    }
 }
diff --git a/dubbo-kubernetes/pom.xml b/dubbo-kubernetes/pom.xml
index 9450722..f039a09 100644
--- a/dubbo-kubernetes/pom.xml
+++ b/dubbo-kubernetes/pom.xml
@@ -32,17 +32,7 @@
         <skip_maven_deploy>false</skip_maven_deploy>
     </properties>
 
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-bom</artifactId>
-                <version>3.2.9</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
+
 
     <dependencies>
         <dependency>
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-report-consul/pom.xml b/dubbo-metadata-report-extensions/dubbo-metadata-report-consul/pom.xml
index d1cb758..7e58ec2 100644
--- a/dubbo-metadata-report-extensions/dubbo-metadata-report-consul/pom.xml
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-report-consul/pom.xml
@@ -26,7 +26,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
 
     <artifactId>dubbo-metadata-report-consul</artifactId>
 
@@ -39,7 +39,7 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-configcenter-consul</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
         <dependency>
             <groupId>com.ecwid.consul</groupId>
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-report-etcd/pom.xml b/dubbo-metadata-report-extensions/dubbo-metadata-report-etcd/pom.xml
index 8b2415a..c711683 100644
--- a/dubbo-metadata-report-extensions/dubbo-metadata-report-etcd/pom.xml
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-report-etcd/pom.xml
@@ -27,7 +27,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
 
     <artifactId>dubbo-metadata-report-etcd</artifactId>
 
@@ -39,7 +39,6 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-metadata-api</artifactId>
-            <version>3.2.7</version>
             <exclusions>
                 <exclusion>
                     <artifactId>dubbo-rpc-api</artifactId>
@@ -60,7 +59,6 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-rpc-api</artifactId>
-            <version>3.2.7</version>
             <exclusions>
                 <exclusion>
                     <artifactId>dubbo-common</artifactId>
@@ -73,7 +71,6 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-cluster</artifactId>
-            <version>3.2.7</version>
             <exclusions>
                 <exclusion>
                     <artifactId>dubbo-rpc-api</artifactId>
@@ -86,14 +83,13 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>3.2.7</version>
             <optional>true</optional>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-remoting-etcd3</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
         <dependency>
             <groupId>io.etcd</groupId>
@@ -101,6 +97,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>io.etcd</groupId>
+            <artifactId>jetcd-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.testcontainers</groupId>
             <artifactId>testcontainers</artifactId>
             <scope>test</scope>
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java
index 119522c..28d0749 100644
--- a/dubbo-metadata-report-extensions/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-report-etcd/src/test/java/org/apache/dubbo/metadata/store/etcd/EtcdMetadataReportTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.dubbo.metadata.store.etcd;
 
+import io.etcd.jetcd.test.EtcdClusterExtension;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
@@ -30,7 +31,6 @@
 import io.etcd.jetcd.Client;
 import io.etcd.jetcd.kv.GetResponse;
 import io.etcd.jetcd.launcher.EtcdCluster;
-import io.etcd.jetcd.launcher.EtcdClusterFactory;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -57,7 +57,11 @@
 
     private static final String TEST_SERVICE = "org.apache.dubbo.metadata.store.etcd.EtcdMetadata4TstService";
 
-    private EtcdCluster etcdCluster = EtcdClusterFactory.buildCluster(getClass().getSimpleName(), 1, false);
+    private final EtcdCluster etcdCluster = EtcdClusterExtension.builder().withClusterName(getClass().getSimpleName())
+        .withNodes(1)
+        .withSsl(false)
+        .build().cluster();
+
     private Client etcdClientForTest;
     private EtcdMetadataReport etcdMetadataReport;
     private URL registryUrl;
@@ -66,8 +70,8 @@
     @BeforeEach
     public void setUp() {
         etcdCluster.start();
-        etcdClientForTest = Client.builder().endpoints(etcdCluster.getClientEndpoints()).build();
-        List<URI> clientEndPoints = etcdCluster.getClientEndpoints();
+        etcdClientForTest = Client.builder().endpoints(etcdCluster.clientEndpoints()).build();
+        List<URI> clientEndPoints = etcdCluster.clientEndpoints();
         this.registryUrl = URL.valueOf("etcd://" + clientEndPoints.get(0).getHost() + ":" + clientEndPoints.get(0).getPort());
         etcdMetadataReportFactory = new EtcdMetadataReportFactory();
         this.etcdMetadataReport = (EtcdMetadataReport) etcdMetadataReportFactory.createMetadataReport(registryUrl);
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/pom.xml b/dubbo-metadata-report-extensions/dubbo-metadata-rest/pom.xml
new file mode 100644
index 0000000..e2b8f01
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/pom.xml
@@ -0,0 +1,157 @@
+<?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>
+    <parent>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <artifactId>dubbo-metadata-report-extensions</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>dubbo-metadata-rest</artifactId>
+    <version>${revision}</version>
+    <description>The rest-metadata module of Dubbo project</description>
+
+    <properties>
+        <skipIntegrationTests>true</skipIntegrationTests>
+    </properties>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-cluster</artifactId>
+        </dependency>
+
+        <!--        to use the utils and builders-->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metadata-processor</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>dubbo-rpc-api</artifactId>
+                    <groupId>org.apache.dubbo</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>dubbo-common</artifactId>
+                    <groupId>org.apache.dubbo</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>dubbo-cluster</artifactId>
+                    <groupId>org.apache.dubbo</groupId>
+                </exclusion>
+            </exclusions>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.annotation</groupId>
+                    <artifactId>javax.annotation-api</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-api</artifactId>
+                </exclusion>
+
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.javassist</groupId>
+                    <artifactId>javassist</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.alibaba</groupId>
+                    <artifactId>hessian-lite</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-io</groupId>
+                    <artifactId>commons-io</artifactId>
+                </exclusion>
+            </exclusions>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- JAX-RS API -->
+        <dependency>
+            <groupId>javax.ws.rs</groupId>
+            <artifactId>javax.ws.rs-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Spring Web MVC -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Spring Web MVC -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metrics-api</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metrics-default</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metrics-metadata</artifactId>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AbstractAnnotatedMethodParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AbstractAnnotatedMethodParameterProcessor.java
new file mode 100644
index 0000000..66e7734
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AbstractAnnotatedMethodParameterProcessor.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.dubbo.metadata.extension.rest.annotation.processing;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getValue;
+import static org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor.buildDefaultValue;
+
+/**
+ * The abstract {@link AnnotatedMethodParameterProcessor} implementation
+ *
+ * @since 2.7.6
+ */
+public abstract class AbstractAnnotatedMethodParameterProcessor implements AnnotatedMethodParameterProcessor {
+
+    @Override
+    public final void process(
+            AnnotationMirror annotation,
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            RestMethodMetadata restMethodMetadata) {
+        String annotationValue = getAnnotationValue(annotation, parameter, parameterIndex);
+        String defaultValue = getDefaultValue(annotation, parameter, parameterIndex);
+        process(annotationValue, defaultValue, annotation, parameter, parameterIndex, method, restMethodMetadata);
+    }
+
+    protected abstract void process(
+            String annotationValue,
+            String defaultValue,
+            AnnotationMirror annotation,
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            RestMethodMetadata restMethodMetadata);
+
+    protected String getAnnotationValue(AnnotationMirror annotation, VariableElement parameter, int parameterIndex) {
+        return getValue(annotation);
+    }
+
+    protected String getDefaultValue(AnnotationMirror annotation, VariableElement parameter, int parameterIndex) {
+        return buildDefaultValue(parameterIndex);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AbstractServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AbstractServiceRestMetadataResolver.java
new file mode 100644
index 0000000..52ab648
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AbstractServiceRestMetadataResolver.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.metadata.annotation.processing.util.ExecutableElementComparator;
+import org.apache.dubbo.metadata.definition.model.MethodDefinition;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.RequestMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.Elements;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import static java.lang.ThreadLocal.withInitial;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.sort;
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
+import static org.apache.dubbo.metadata.annotation.processing.builder.MethodDefinitionBuilder.build;
+import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.info;
+import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getOverrideMethod;
+import static org.apache.dubbo.metadata.annotation.processing.util.MethodUtils.getPublicNonStaticMethods;
+import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getAnnotation;
+import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getGroup;
+import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getVersion;
+import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.resolveServiceInterfaceName;
+
+/**
+ * Abstract {@link ServiceRestMetadataResolver} implementation
+ *
+ * @since 2.7.6
+ */
+public abstract class AbstractServiceRestMetadataResolver implements ServiceRestMetadataResolver {
+
+    private static final ThreadLocal<Map<String, Object>> threadLocalCache = withInitial(HashMap::new);
+
+    private static final Map<String, List<AnnotatedMethodParameterProcessor>> parameterProcessorsMap =
+            loadAnnotatedMethodParameterProcessors();
+
+    private final String processorName = getClass().getSimpleName();
+
+    @Override
+    public final ServiceRestMetadata resolve(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, Set<? extends TypeElement> annotations) {
+
+        info(
+                "%s is processing the service type[%s] with annotations[%s]",
+                processorName,
+                serviceType,
+                annotations.stream().map(t -> "@" + t.toString()).collect(Collectors.joining(",")));
+
+        ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata();
+
+        Elements elements = processingEnv.getElementUtils();
+
+        try {
+            AnnotationMirror serviceAnnotation = getAnnotation(serviceType);
+            String serviceInterfaceName = resolveServiceInterfaceName(serviceType, serviceAnnotation);
+            serviceRestMetadata.setServiceInterface(serviceInterfaceName);
+            serviceRestMetadata.setGroup(getGroup(serviceAnnotation));
+            serviceRestMetadata.setVersion(getVersion(serviceAnnotation));
+
+            TypeElement serviceInterfaceType = elements.getTypeElement(serviceInterfaceName);
+
+            List<? extends ExecutableElement> serviceMethods =
+                    new LinkedList<>(getPublicNonStaticMethods(serviceInterfaceType, Object.class));
+
+            // Sorts
+            sort(serviceMethods, ExecutableElementComparator.INSTANCE);
+
+            serviceMethods.forEach(serviceMethod -> {
+                resolveRestMethodMetadata(
+                    processingEnv, serviceType, serviceInterfaceType, serviceMethod, serviceRestMetadata)
+                    .ifPresent(serviceRestMetadata.getMeta()::add);
+            });
+
+        } finally {
+            clearCache();
+        }
+
+        info("The %s's process result : %s", processorName, serviceRestMetadata);
+
+        return serviceRestMetadata;
+    }
+
+    protected Optional<RestMethodMetadata> resolveRestMethodMetadata(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            TypeElement serviceInterfaceType,
+            ExecutableElement serviceMethod,
+            ServiceRestMetadata serviceRestMetadata) {
+
+        ExecutableElement restCapableMethod =
+                findRestCapableMethod(processingEnv, serviceType, serviceInterfaceType, serviceMethod);
+
+        if (restCapableMethod == null) { // if can't be found
+            return empty();
+        }
+
+        String requestPath =
+                resolveRequestPath(processingEnv, serviceType, restCapableMethod); // requestPath is required
+
+        if (requestPath == null) {
+            return empty();
+        }
+
+        String requestMethod =
+                resolveRequestMethod(processingEnv, serviceType, restCapableMethod); // requestMethod is required
+
+        if (requestMethod == null) {
+            return empty();
+        }
+
+        RestMethodMetadata metadata = new RestMethodMetadata();
+
+        MethodDefinition methodDefinition = resolveMethodDefinition(processingEnv, serviceType, restCapableMethod);
+        // Set MethodDefinition
+        metadata.setMethod(methodDefinition);
+
+        // process the annotated method parameters
+        processAnnotatedMethodParameters(restCapableMethod, serviceType, metadata);
+
+        // process produces
+        Set<String> produces = new LinkedHashSet<>();
+        processProduces(processingEnv, serviceType, restCapableMethod, produces);
+
+        // process consumes
+        Set<String> consumes = new LinkedHashSet<>();
+        processConsumes(processingEnv, serviceType, restCapableMethod, consumes);
+
+        // Initialize RequestMetadata
+        RequestMetadata request = metadata.getRequest();
+        request.setPath(requestPath);
+        request.appendContextPathFromUrl(serviceRestMetadata.getContextPathFromUrl());
+
+        request.setMethod(requestMethod);
+        request.setProduces(produces);
+        request.setConsumes(consumes);
+
+        // Post-Process
+        postProcessRestMethodMetadata(processingEnv, serviceType, serviceMethod, metadata);
+
+        return of(metadata);
+    }
+
+    /**
+     * Find the method with the capable for REST from the specified service method and its override method
+     *
+     * @param processingEnv        {@link ProcessingEnvironment}
+     * @param serviceType
+     * @param serviceInterfaceType
+     * @param serviceMethod
+     * @return <code>null</code> if can't be found
+     */
+    private ExecutableElement findRestCapableMethod(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            TypeElement serviceInterfaceType,
+            ExecutableElement serviceMethod) {
+        // try to judge the override first
+        ExecutableElement overrideMethod = getOverrideMethod(processingEnv, serviceType, serviceMethod);
+        if (supports(processingEnv, serviceType, serviceInterfaceType, overrideMethod)) {
+            return overrideMethod;
+        }
+        // or, try to judge the declared method
+        return supports(processingEnv, serviceType, serviceInterfaceType, serviceMethod) ? serviceMethod : null;
+    }
+
+    /**
+     * Does the specified method support REST or not ?
+     *
+     * @param processingEnv {@link ProcessingEnvironment}
+     * @param method        the method may be declared on the interface or class
+     * @return if supports, return <code>true</code>, or <code>false</code>
+     */
+    protected abstract boolean supports(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            TypeElement serviceInterfaceType,
+            ExecutableElement method);
+
+    /**
+     * Post-Process for {@link RestMethodMetadata}, sub-type could override this method for further works
+     *
+     * @param processingEnv {@link ProcessingEnvironment}
+     * @param serviceType   The type that @Service annotated
+     * @param method        The public method of <code>serviceType</code>
+     * @param metadata      {@link RestMethodMetadata} maybe updated
+     */
+    protected void postProcessRestMethodMetadata(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            ExecutableElement method,
+            RestMethodMetadata metadata) {}
+
+    protected abstract String resolveRequestPath(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method);
+
+    protected abstract String resolveRequestMethod(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method);
+
+    protected MethodDefinition resolveMethodDefinition(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method) {
+        return build(processingEnv, method, new HashMap<>());
+    }
+
+    protected void processAnnotatedMethodParameters(
+            ExecutableElement method, TypeElement type, RestMethodMetadata metadata) {
+        List<? extends VariableElement> methodParameters = method.getParameters();
+        int size = methodParameters.size();
+        for (int i = 0; i < size; i++) {
+            VariableElement parameter = methodParameters.get(i);
+            // Add indexed parameter name
+            metadata.addIndexToName(i, parameter.getSimpleName().toString());
+            processAnnotatedMethodParameter(parameter, i, method, type, metadata);
+        }
+    }
+
+    protected void processAnnotatedMethodParameter(
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            TypeElement serviceType,
+            RestMethodMetadata metadata) {
+
+        parameter.getAnnotationMirrors().forEach(annotation -> {
+            String annotationType = annotation.getAnnotationType().toString();
+            parameterProcessorsMap.getOrDefault(annotationType, emptyList()).forEach(parameterProcessor -> {
+                parameterProcessor.process(annotation, parameter, parameterIndex, method, metadata);
+            });
+        });
+    }
+
+    protected abstract void processProduces(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            ExecutableElement method,
+            Set<String> produces);
+
+    protected abstract void processConsumes(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            ExecutableElement method,
+            Set<String> consumes);
+
+    protected static final void put(String name, Object value) {
+        Map<String, Object> cache = getCache();
+        cache.put(name, value);
+    }
+
+    protected static final <T> T get(String name) throws ClassCastException {
+        Map<String, Object> cache = getCache();
+        return (T) cache.get(name);
+    }
+
+    protected static final <V> V computeIfAbsent(String name, Function<? super String, ? extends V> mappingFunction) {
+        return (V) getCache().computeIfAbsent(name, mappingFunction);
+    }
+
+    private static Map<String, List<AnnotatedMethodParameterProcessor>> loadAnnotatedMethodParameterProcessors() {
+        Map<String, List<AnnotatedMethodParameterProcessor>> parameterProcessorsMap = new LinkedHashMap<>();
+
+        //        load(AnnotatedMethodParameterProcessor.class,
+        // AnnotatedMethodParameterProcessor.class.getClassLoader())
+
+        ApplicationModel.defaultModel()
+                .getExtensionLoader(AnnotatedMethodParameterProcessor.class)
+                .getSupportedExtensionInstances()
+                .forEach(processor -> {
+                    List<AnnotatedMethodParameterProcessor> processors = parameterProcessorsMap.computeIfAbsent(
+                            processor.getAnnotationType(), k -> new LinkedList<>());
+                    processors.add(processor);
+                });
+
+        return parameterProcessorsMap;
+    }
+
+    private static Map<String, Object> getCache() {
+        return threadLocalCache.get();
+    }
+
+    private static void clearCache() {
+        Map<String, Object> cache = getCache();
+        cache.clear();
+        threadLocalCache.remove();
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AnnotatedMethodParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AnnotatedMethodParameterProcessor.java
new file mode 100644
index 0000000..8e40707
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AnnotatedMethodParameterProcessor.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.common.lang.Prioritized;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+
+/**
+ * The interface to process the parameter of method that was annotated
+ *
+ * @since 2.7.6
+ */
+@SPI
+public interface AnnotatedMethodParameterProcessor extends Prioritized {
+
+    /**
+     * The string presenting the annotation type
+     *
+     * @return non-null
+     */
+    String getAnnotationType();
+
+    /**
+     * Process the specified method {@link VariableElement parameter}
+     *
+     * @param annotation         {@link AnnotationMirror the target annotation} whose type is {@link #getAnnotationType()}
+     * @param parameter          {@link VariableElement method parameter}
+     * @param parameterIndex     the index of parameter in the method
+     * @param method             {@link ExecutableElement method that parameter belongs to}
+     * @param restMethodMetadata {@link RestMethodMetadata the metadata is used to update}
+     */
+    void process(
+            AnnotationMirror annotation,
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            RestMethodMetadata restMethodMetadata);
+
+    /**
+     * Build the default value
+     *
+     * @param parameterIndex the index of parameter
+     * @return the placeholder
+     */
+    static String buildDefaultValue(int parameterIndex) {
+        return "{" + parameterIndex + "}";
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/DefaultServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/DefaultServiceRestMetadataResolver.java
new file mode 100644
index 0000000..c7f84f4
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/DefaultServiceRestMetadataResolver.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.common.convert.Converter;
+import org.apache.dubbo.common.convert.ConverterUtil;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.jaxrs.JAXRSServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.springmvc.SpringMvcServiceRestMetadataResolver;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static java.lang.String.valueOf;
+import static java.util.Arrays.asList;
+import static org.apache.dubbo.common.utils.ClassUtils.forName;
+import static org.apache.dubbo.common.utils.StringUtils.SLASH_CHAR;
+import static org.apache.dubbo.metadata.annotation.processing.util.LoggerUtils.warn;
+import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.getAnnotation;
+import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.resolveServiceInterfaceName;
+import static org.apache.dubbo.metadata.annotation.processing.util.TypeUtils.findInterface;
+
+/**
+ * The default implementation of {@link ServiceRestMetadataResolver}
+ *
+ * @since 2.7.6
+ */
+public class DefaultServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
+
+    private static final char PACKAGE_SEPARATOR = '.';
+
+    private static final char PATH_SEPARATOR = SLASH_CHAR;
+
+    private static final String HTTP_REQUEST_METHOD = "POST";
+
+    private static final List<String> MEDIA_TYPES = asList(
+            "application/json;",
+            "application/*+json",
+            "application/xml;charset=UTF-8",
+            "text/xml;charset=UTF-8",
+            "application/*+xml;charset=UTF-8");
+
+    private final Set<ExecutableElement> hasComplexParameterTypeMethods = new HashSet<>();
+
+    @Override
+    public boolean supports(ProcessingEnvironment processingEnvironment, TypeElement serviceType) {
+        return !JAXRSServiceRestMetadataResolver.supports(serviceType)
+                && !SpringMvcServiceRestMetadataResolver.supports(serviceType);
+    }
+
+    @Override
+    protected boolean supports(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            TypeElement serviceInterfaceType,
+            ExecutableElement method) {
+        // TODO add some criterion
+        return true;
+    }
+
+    @Override
+    protected String resolveRequestPath(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method) {
+
+        AnnotationMirror serviceAnnotation = getAnnotation(serviceType);
+
+        String serviceInterfaceName = resolveServiceInterfaceName(serviceType, serviceAnnotation);
+
+        TypeMirror serviceInterface = findInterface(serviceType.asType(), serviceInterfaceName);
+
+        StringBuilder requestPathBuilder = new StringBuilder();
+        // the name of service type as the root path
+        String rootPath = buildRootPath(serviceInterface);
+        // the method name as the sub path
+        String subPath = buildSubPath(method);
+
+        requestPathBuilder.append(rootPath).append(subPath);
+        // the methods' parameters as the the path variables
+        List<? extends VariableElement> parameters = method.getParameters();
+
+        for (int i = 0; i < parameters.size(); i++) {
+            VariableElement parameter = parameters.get(i);
+            TypeMirror parameterType = parameter.asType();
+            if (isComplexType(parameterType)) {
+                if (addComplexParameterType(method)) {
+                    continue;
+                } else {
+                    // The count of complex types must be only one, or return immediately
+                    warn(
+                            "The method[%s] contains more than one complex parameter type, "
+                                    + "thus it will not be chosen as the REST service",
+                            method.toString());
+                }
+            }
+            String parameterName = parameter.getSimpleName().toString();
+            // If "-parameters" option is enabled, take the parameter name as the path variable name,
+            // or use the index of parameter
+            String pathVariableName = isEnabledParametersCompilerOption(parameterName) ? parameterName : valueOf(i);
+            requestPathBuilder
+                    .append(PATH_SEPARATOR)
+                    .append('{')
+                    .append(pathVariableName)
+                    .append('}');
+        }
+
+        return requestPathBuilder.toString();
+    }
+
+    private String buildRootPath(TypeMirror serviceInterface) {
+        return PATH_SEPARATOR + serviceInterface.toString().replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
+    }
+
+    private String buildSubPath(ExecutableElement method) {
+        return PATH_SEPARATOR + method.getSimpleName().toString();
+    }
+
+    private boolean isEnabledParametersCompilerOption(String parameterName) {
+        return !parameterName.startsWith("arg");
+    }
+
+    private boolean isComplexType(TypeMirror parameterType) {
+        return !supportsPathVariableType(parameterType);
+    }
+
+    /**
+     * Supports the type of parameter or not, based by {@link Converter}'s conversion feature
+     *
+     * @param parameterType the type of parameter
+     * @return if supports, this method will return <code>true</code>, or <code>false</code>
+     */
+    private boolean supportsPathVariableType(TypeMirror parameterType) {
+        String className = parameterType.toString();
+        ClassLoader classLoader = getClass().getClassLoader();
+        boolean supported;
+        try {
+            Class<?> targetType = forName(className, classLoader);
+            supported = FrameworkModel.defaultModel()
+                            .getBeanFactory()
+                            .getBean(ConverterUtil.class)
+                            .getConverter(String.class, targetType)
+                    != null;
+        } catch (ClassNotFoundException e) {
+            supported = false;
+        }
+        return supported;
+    }
+
+    @Override
+    protected String resolveRequestMethod(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method) {
+        return HTTP_REQUEST_METHOD;
+    }
+
+    @Override
+    protected void processProduces(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            ExecutableElement method,
+            Set<String> produces) {
+        TypeMirror returnType = method.getReturnType();
+        if (isComplexType(returnType)) {
+            produces.addAll(MEDIA_TYPES);
+        }
+    }
+
+    @Override
+    protected void processConsumes(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            ExecutableElement method,
+            Set<String> consumes) {
+        if (hasComplexParameterType(method)) {
+            consumes.addAll(MEDIA_TYPES);
+        }
+    }
+
+    private boolean addComplexParameterType(ExecutableElement method) {
+        return hasComplexParameterTypeMethods.add(method);
+    }
+
+    private boolean hasComplexParameterType(ExecutableElement method) {
+        return hasComplexParameterTypeMethods.remove(method);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/ServiceRestMetadataAnnotationProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/ServiceRestMetadataAnnotationProcessor.java
new file mode 100644
index 0000000..26c479f
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/ServiceRestMetadataAnnotationProcessor.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.metadata.annotation.processing.AbstractServiceAnnotationProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.TypeElement;
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static javax.lang.model.util.ElementFilter.typesIn;
+import static org.apache.dubbo.metadata.annotation.processing.util.ServiceAnnotationUtils.isServiceAnnotationPresent;
+
+/**
+ * The {@link Processor} class to generate the metadata of REST from the classes that are annotated by Dubbo's
+ *
+ * @Service
+ * @see Processor
+ * @since 2.7.6
+ */
+public class ServiceRestMetadataAnnotationProcessor extends AbstractServiceAnnotationProcessor {
+
+    private Set<ServiceRestMetadataResolver> metadataProcessors;
+
+    private ServiceRestMetadataStorage serviceRestMetadataWriter;
+
+    private Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
+
+    @Override
+    public synchronized void init(ProcessingEnvironment processingEnv) {
+        super.init(processingEnv);
+        this.metadataProcessors = ApplicationModel.defaultModel()
+                .getExtensionLoader(ServiceRestMetadataResolver.class)
+                .getSupportedExtensionInstances();
+        this.serviceRestMetadataWriter = new ServiceRestMetadataStorage(processingEnv);
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+
+        typesIn(roundEnv.getRootElements()).forEach(serviceType -> process(processingEnv, serviceType, annotations));
+
+        if (roundEnv.processingOver()) {
+            try {
+                serviceRestMetadataWriter.append(serviceRestMetadata);
+            } catch (IOException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+
+        return false;
+    }
+
+    private void process(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, Set<? extends TypeElement> annotations) {
+        metadataProcessors.stream()
+                .filter(processor -> supports(processor, processingEnv, serviceType))
+                .map(processor -> processor.resolve(processingEnv, serviceType, annotations))
+                .forEach(serviceRestMetadata::add);
+    }
+
+    private boolean supports(
+            ServiceRestMetadataResolver processor, ProcessingEnvironment processingEnv, TypeElement serviceType) {
+        //  @Service must be present in service type
+        return isServiceAnnotationPresent(serviceType) && processor.supports(processingEnv, serviceType);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/ServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/ServiceRestMetadataResolver.java
new file mode 100644
index 0000000..acb2ca7
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/ServiceRestMetadataResolver.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.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.common.lang.Prioritized;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.TypeElement;
+import java.util.Set;
+
+/**
+ * The class to resolve {@link ServiceRestMetadata} based on Annotation Processor Tool
+ *
+ * @since 2.7.6
+ */
+@SPI("default")
+public interface ServiceRestMetadataResolver extends Prioritized {
+
+    /**
+     * Supports or not to the specified service type
+     *
+     * @param processingEnvironment {@link ProcessingEnvironment}
+     * @param serviceType           Dubbo service type or interface
+     * @return if supports, return <code>true</code>, or <code>false</code>
+     */
+    boolean supports(ProcessingEnvironment processingEnvironment, TypeElement serviceType);
+
+    /**
+     * Resolve the {@link ServiceRestMetadata} from given service type
+     *
+     * @param processingEnvironment {@link ProcessingEnvironment}
+     * @param serviceType           Dubbo service type or interface
+     * @param annotations
+     * @return non-null
+     */
+    ServiceRestMetadata resolve(
+            ProcessingEnvironment processingEnvironment,
+            TypeElement serviceType,
+            Set<? extends TypeElement> annotations);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/ServiceRestMetadataStorage.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/ServiceRestMetadataStorage.java
new file mode 100644
index 0000000..dc21bea
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/ServiceRestMetadataStorage.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.common.utils.JsonUtils;
+import org.apache.dubbo.metadata.annotation.processing.ClassPathMetadataStorage;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import java.io.IOException;
+import java.util.Set;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SERVICE_REST_METADATA_RESOURCE_PATH;
+
+
+/**
+ * The storage for {@link ServiceRestMetadata}
+ */
+public class ServiceRestMetadataStorage {
+
+    private final ClassPathMetadataStorage storage;
+
+    public ServiceRestMetadataStorage(ProcessingEnvironment processingEnv) {
+        this.storage = new ClassPathMetadataStorage(processingEnv);
+    }
+
+    public void append(Set<ServiceRestMetadata> serviceRestMetadata) throws IOException {
+        // Add all existed ServiceRestMetadata
+        storage.read(SERVICE_REST_METADATA_RESOURCE_PATH, reader -> {
+                    try {
+                        StringBuilder stringBuilder = new StringBuilder();
+                        char[] buf = new char[1024];
+                        int len;
+                        while ((len = reader.read(buf)) != -1) {
+                            stringBuilder.append(buf, 0, len);
+                        }
+                        return JsonUtils.toJavaList(stringBuilder.toString(), ServiceRestMetadata.class);
+                    } catch (IOException e) {
+                        return null;
+                    }
+                })
+                .ifPresent(serviceRestMetadata::addAll);
+        write(serviceRestMetadata);
+    }
+
+    public void write(Set<ServiceRestMetadata> serviceRestMetadata) throws IOException {
+        if (serviceRestMetadata.isEmpty()) {
+            return;
+        }
+        storage.write(() -> JsonUtils.toJson(serviceRestMetadata), SERVICE_REST_METADATA_RESOURCE_PATH);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/DefaultValueParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/DefaultValueParameterProcessor.java
new file mode 100644
index 0000000..74ba3b4
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/DefaultValueParameterProcessor.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.RequestMetadata;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.DEFAULT_VALUE_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @DefaultValue
+ * *
+ *
+ * @since 2.7.6
+ */
+public class DefaultValueParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    public String getAnnotationType() {
+        return DEFAULT_VALUE_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected void process(
+            String annotationValue,
+            String defaultValue,
+            AnnotationMirror annotation,
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            RestMethodMetadata restMethodMetadata) {
+        RequestMetadata requestMetadata = restMethodMetadata.getRequest();
+
+        // process the request parameters
+        setDefaultValue(requestMetadata.getParams(), defaultValue, annotationValue);
+        // process the request headers
+        setDefaultValue(requestMetadata.getHeaders(), defaultValue, annotationValue);
+    }
+
+    private void setDefaultValue(Map<String, List<String>> source, String placeholderValue, String defaultValue) {
+        OUTTER:
+        for (Map.Entry<String, List<String>> entry : source.entrySet()) {
+            List<String> values = entry.getValue();
+            int size = values.size();
+            for (int i = 0; i < size; i++) {
+                String value = values.get(i);
+                if (placeholderValue.equals(value)) {
+                    values.set(i, defaultValue);
+                    break OUTTER;
+                }
+            }
+        }
+    }
+
+    @Override
+    public int getPriority() {
+        return MIN_PRIORITY;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/FormParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/FormParamParameterProcessor.java
new file mode 100644
index 0000000..fdc7766
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/FormParamParameterProcessor.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.dubbo.metadata.extension.rest.annotation.processing.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.FORM_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @FormParam
+ *
+ * @since 2.7.6
+ */
+public class FormParamParameterProcessor extends ParamAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationType() {
+        return FORM_PARAM_ANNOTATION_CLASS_NAME;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/HeaderParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/HeaderParamParameterProcessor.java
new file mode 100644
index 0000000..fd21aea
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/HeaderParamParameterProcessor.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.dubbo.metadata.extension.rest.annotation.processing.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RequestMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+
+import static org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor.buildDefaultValue;
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.HEADER_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @HeaderParam
+ *
+ * @since 2.7.6
+ */
+public class HeaderParamParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    public String getAnnotationType() {
+        return HEADER_PARAM_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected void process(
+            String headerName,
+            String defaultValue,
+            AnnotationMirror annotation,
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            RestMethodMetadata restMethodMetadata) {
+        RequestMetadata requestMetadata = restMethodMetadata.getRequest();
+        // Add the placeholder as header value
+        requestMetadata.addHeader(headerName, buildDefaultValue(parameterIndex));
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/JAXRSServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/JAXRSServiceRestMetadataResolver.java
new file mode 100644
index 0000000..aa005a3
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/JAXRSServiceRestMetadataResolver.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.dubbo.metadata.extension.rest.annotation.processing.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AbstractServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.ServiceRestMetadataResolver;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.AnnotatedConstruct;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static org.apache.dubbo.common.utils.PathUtils.buildPath;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findAnnotation;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findMetaAnnotation;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAnnotation;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getValue;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.isAnnotationPresent;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.CONSUMES_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.HTTP_METHOD_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.PATH_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.PRODUCES_ANNOTATION_CLASS_NAME;
+
+
+
+/**
+ * {@link ServiceRestMetadataResolver} implementation for JAX-RS 2 and 1
+ *
+ * @since 2.7.6
+ */
+public class JAXRSServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
+
+    @Override
+    public boolean supports(ProcessingEnvironment processingEnvironment, TypeElement serviceType) {
+        return supports(serviceType);
+    }
+
+    public static boolean supports(TypeElement serviceType) {
+        return isAnnotationPresent(serviceType, PATH_ANNOTATION_CLASS_NAME);
+    }
+
+    @Override
+    protected boolean supports(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            TypeElement serviceInterfaceType,
+            ExecutableElement method) {
+        return isAnnotationPresent(method, PATH_ANNOTATION_CLASS_NAME)
+                || isAnnotationPresent(method, HTTP_METHOD_ANNOTATION_CLASS_NAME);
+    }
+
+    @Override
+    protected String resolveRequestPath(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method) {
+        String pathFromType = getPathValue(processingEnv, serviceType);
+        String pathFromMethod = getPathValue(method);
+        return buildPath(pathFromType, pathFromMethod);
+    }
+
+    @Override
+    protected String resolveRequestMethod(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method) {
+        AnnotationMirror annotation = findMetaAnnotation(method, HTTP_METHOD_ANNOTATION_CLASS_NAME);
+        return getValue(annotation);
+    }
+
+    @Override
+    protected void processProduces(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            ExecutableElement method,
+            Set<String> produces) {
+        addAnnotationValues(method, PRODUCES_ANNOTATION_CLASS_NAME, produces);
+    }
+
+    @Override
+    protected void processConsumes(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            ExecutableElement method,
+            Set<String> consumes) {
+        addAnnotationValues(method, CONSUMES_ANNOTATION_CLASS_NAME, consumes);
+    }
+
+    private void addAnnotationValues(Element element, String annotationAttributeName, Set<String> result) {
+        AnnotationMirror annotation = findAnnotation(element, annotationAttributeName);
+        String[] value = getValue(annotation);
+        if (value != null) {
+            Stream.of(value).forEach(result::add);
+        }
+    }
+
+    private String getPathValue(ProcessingEnvironment processingEnv, TypeElement serviceType) {
+        AnnotationMirror annotation = findAnnotation(serviceType, PATH_ANNOTATION_CLASS_NAME);
+        return getValue(annotation);
+    }
+
+    private String getPathValue(AnnotatedConstruct annotatedConstruct) {
+        AnnotationMirror annotation = getAnnotation(annotatedConstruct, PATH_ANNOTATION_CLASS_NAME);
+        return getValue(annotation);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/MatrixParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/MatrixParamParameterProcessor.java
new file mode 100644
index 0000000..ada6115
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/MatrixParamParameterProcessor.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.dubbo.metadata.extension.rest.annotation.processing.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.MATRIX_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @MatrixParam
+ *
+ * @since 2.7.6
+ */
+public class MatrixParamParameterProcessor extends ParamAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationType() {
+        return MATRIX_PARAM_ANNOTATION_CLASS_NAME;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/ParamAnnotationParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/ParamAnnotationParameterProcessor.java
new file mode 100644
index 0000000..66e37b6
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/ParamAnnotationParameterProcessor.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.RequestMetadata;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+
+/**
+ * The abstract {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @*Param
+ */
+public abstract class ParamAnnotationParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    protected void process(
+            String name,
+            String defaultValue,
+            AnnotationMirror annotation,
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            RestMethodMetadata restMethodMetadata) {
+        RequestMetadata requestMetadata = restMethodMetadata.getRequest();
+        requestMetadata.addParam(name, defaultValue);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/QueryParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/QueryParamParameterProcessor.java
new file mode 100644
index 0000000..12c97d9
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/jaxrs/QueryParamParameterProcessor.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.dubbo.metadata.extension.rest.annotation.processing.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.QUERY_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @QueryParam
+ *
+ * @since 2.7.6
+ */
+public class QueryParamParameterProcessor extends ParamAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationType() {
+        return QUERY_PARAM_ANNOTATION_CLASS_NAME;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/AbstractRequestAnnotationParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/AbstractRequestAnnotationParameterProcessor.java
new file mode 100644
index 0000000..f4ed74f
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/AbstractRequestAnnotationParameterProcessor.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAttribute;
+
+/**
+ * The abstract {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @Request*
+ */
+public abstract class AbstractRequestAnnotationParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    protected abstract void process(
+            String name,
+            String defaultValue,
+            AnnotationMirror annotation,
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            RestMethodMetadata restMethodMetadata);
+
+    @Override
+    protected String getAnnotationValue(AnnotationMirror annotation, VariableElement parameter, int parameterIndex) {
+        // try to get "value" attribute first
+        String name = super.getAnnotationValue(annotation, parameter, parameterIndex);
+
+        // try to get "name" attribute if required
+        if (isEmpty(name)) {
+            name = getAttribute(annotation, "name");
+        }
+
+        // finally , try to the name of parameter
+        if (isEmpty(name)) {
+            name = parameter.getSimpleName().toString();
+        }
+
+        return name;
+    }
+
+    protected String getDefaultValue(AnnotationMirror annotation, VariableElement parameter, int parameterIndex) {
+        String defaultValue = getAttribute(annotation, "defaultValue");
+        if (isEmpty(defaultValue)) {
+            defaultValue = super.getDefaultValue(annotation, parameter, parameterIndex);
+        }
+        return defaultValue;
+    }
+
+    protected boolean isEmpty(String str) {
+        return str == null || str.isEmpty();
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/RequestHeaderParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/RequestHeaderParameterProcessor.java
new file mode 100644
index 0000000..451e796
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/RequestHeaderParameterProcessor.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SPRING_MVC.REQUEST_HEADER_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @RequestHeader
+ */
+public class RequestHeaderParameterProcessor extends AbstractRequestAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationType() {
+        return REQUEST_HEADER_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected void process(
+            String name,
+            String defaultValue,
+            AnnotationMirror annotation,
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            RestMethodMetadata restMethodMetadata) {
+        restMethodMetadata.getRequest().addHeader(name, defaultValue);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/RequestParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/RequestParamParameterProcessor.java
new file mode 100644
index 0000000..f0437e6
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/RequestParamParameterProcessor.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SPRING_MVC.REQUEST_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @RequestParam
+ */
+public class RequestParamParameterProcessor extends AbstractRequestAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationType() {
+        return REQUEST_PARAM_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected void process(
+            String name,
+            String defaultValue,
+            AnnotationMirror annotation,
+            VariableElement parameter,
+            int parameterIndex,
+            ExecutableElement method,
+            RestMethodMetadata restMethodMetadata) {
+        restMethodMetadata.getRequest().addParam(name, defaultValue);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/SpringMvcServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/SpringMvcServiceRestMetadataResolver.java
new file mode 100644
index 0000000..0caa52a
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/springmvc/SpringMvcServiceRestMetadataResolver.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.AbstractServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.ServiceRestMetadataResolver;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import java.lang.reflect.Array;
+import java.util.Set;
+
+
+import static java.lang.String.valueOf;
+import static java.lang.reflect.Array.getLength;
+import static java.util.stream.Stream.of;
+import static org.apache.dubbo.common.function.Streams.filterFirst;
+import static org.apache.dubbo.common.utils.ArrayUtils.isEmpty;
+import static org.apache.dubbo.common.utils.ArrayUtils.isNotEmpty;
+import static org.apache.dubbo.common.utils.PathUtils.buildPath;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findAnnotation;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.findMetaAnnotation;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAllAnnotations;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.getAttribute;
+import static org.apache.dubbo.metadata.annotation.processing.util.AnnotationUtils.isAnnotationPresent;
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SPRING_MVC.CONTROLLER_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SPRING_MVC.REQUEST_MAPPING_ANNOTATION_CLASS_NAME;
+
+/**
+ * {@link ServiceRestMetadataResolver}
+ *
+ * @since 2.7.6
+ */
+public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
+
+    private static final int FIRST_ELEMENT_INDEX = 0;
+
+    @Override
+    public boolean supports(ProcessingEnvironment processingEnvironment, TypeElement serviceType) {
+        return supports(serviceType);
+    }
+
+    @Override
+    protected boolean supports(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            TypeElement serviceInterfaceType,
+            ExecutableElement method) {
+        return isAnnotationPresent(method, REQUEST_MAPPING_ANNOTATION_CLASS_NAME);
+    }
+
+    public static boolean supports(TypeElement serviceType) {
+        // class @Controller or @RequestMapping
+        return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS_NAME)
+                || isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS_NAME);
+    }
+
+    @Override
+    protected String resolveRequestPath(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method) {
+
+        String requestPathFromType = getRequestPath(serviceType);
+
+        String requestPathFromMethod = getRequestPath(method);
+
+        return buildPath(requestPathFromType, requestPathFromMethod);
+    }
+
+    @Override
+    protected String resolveRequestMethod(
+            ProcessingEnvironment processingEnv, TypeElement serviceType, ExecutableElement method) {
+
+        AnnotationMirror requestMapping = getRequestMapping(method);
+
+        // httpMethod is an array of RequestMethod
+        Object httpMethod = getAttribute(requestMapping, "method");
+
+        if (httpMethod == null || getLength(httpMethod) < 1) {
+            return null;
+        }
+
+        // TODO Is is required to support more request methods?
+        return valueOf(Array.get(httpMethod, FIRST_ELEMENT_INDEX));
+    }
+
+    private AnnotationMirror getRequestMapping(Element element) {
+        // try "@RequestMapping" first
+        AnnotationMirror requestMapping = findAnnotation(element, REQUEST_MAPPING_ANNOTATION_CLASS_NAME);
+        // try the annotation meta-annotated later
+        if (requestMapping == null) {
+            requestMapping = findMetaAnnotation(element, REQUEST_MAPPING_ANNOTATION_CLASS_NAME);
+        }
+        return requestMapping;
+    }
+
+    @Override
+    protected void processProduces(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            ExecutableElement method,
+            Set<String> produces) {
+        addMediaTypes(method, "produces", produces);
+    }
+
+    @Override
+    protected void processConsumes(
+            ProcessingEnvironment processingEnv,
+            TypeElement serviceType,
+            ExecutableElement method,
+            Set<String> consumes) {
+        addMediaTypes(method, "consumes", consumes);
+    }
+
+    private void addMediaTypes(ExecutableElement method, String annotationAttributeName, Set<String> mediaTypesSet) {
+
+        AnnotationMirror mappingAnnotation = getMappingAnnotation(method);
+
+        String[] mediaTypes = getAttribute(mappingAnnotation, annotationAttributeName);
+
+        if (isNotEmpty(mediaTypes)) {
+            of(mediaTypes).forEach(mediaTypesSet::add);
+        }
+    }
+
+    private AnnotationMirror getMappingAnnotation(Element element) {
+        return computeIfAbsent(
+                valueOf(element),
+                key -> filterFirst(getAllAnnotations(element), annotation -> {
+                    DeclaredType annotationType = annotation.getAnnotationType();
+                    // try "@RequestMapping" first
+                    if (REQUEST_MAPPING_ANNOTATION_CLASS_NAME.equals(annotationType.toString())) {
+                        return true;
+                    }
+                    // try meta annotation
+                    return isAnnotationPresent(annotationType.asElement(), REQUEST_MAPPING_ANNOTATION_CLASS_NAME);
+                }));
+    }
+
+    private String getRequestPath(Element element) {
+        AnnotationMirror mappingAnnotation = getMappingAnnotation(element);
+        return getRequestPath(mappingAnnotation);
+    }
+
+    private String getRequestPath(AnnotationMirror mappingAnnotation) {
+        // try "value" first
+        String[] value = getAttribute(mappingAnnotation, "value");
+
+        if (isEmpty(value)) { // try "path" later
+            value = getAttribute(mappingAnnotation, "path");
+        }
+
+        if (isEmpty(value)) {
+            return "";
+        }
+        // TODO Is is required to support more request paths?
+        return value[FIRST_ELEMENT_INDEX];
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AbstractAnnotatedMethodParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AbstractAnnotatedMethodParameterProcessor.java
new file mode 100644
index 0000000..ef685fe
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AbstractAnnotatedMethodParameterProcessor.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+import static org.apache.dubbo.common.utils.AnnotationUtils.getValue;
+import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader;
+import static org.apache.dubbo.common.utils.ClassUtils.resolveClass;
+
+/**
+ * The abstract {@link AnnotatedMethodParameterProcessor} implementation
+ *
+ * @since 2.7.6
+ */
+public abstract class AbstractAnnotatedMethodParameterProcessor implements AnnotatedMethodParameterProcessor {
+
+    @Override
+    public void process(
+            Annotation annotation,
+            Parameter parameter,
+            int parameterIndex,
+            Method method,
+            Class<?> serviceType,
+            Class<?> serviceInterfaceClass,
+            RestMethodMetadata restMethodMetadata) {
+
+        String annotationValue = getAnnotationValue(annotation, parameter, parameterIndex);
+        String defaultValue = getDefaultValue(annotation, parameter, parameterIndex);
+        addArgInfo(parameter, parameterIndex, restMethodMetadata, annotationValue, defaultValue);
+        process(annotationValue, defaultValue, annotation, parameter, parameterIndex, method, restMethodMetadata);
+    }
+
+    protected void process(
+            String annotationValue,
+            String defaultValue,
+            Annotation annotation,
+            Parameter parameter,
+            int parameterIndex,
+            Method method,
+            RestMethodMetadata restMethodMetadata) {}
+
+    @Override
+    public Class getAnnotationClass() {
+        return resolveClass(getAnnotationName(), getClassLoader());
+    }
+
+    protected void addArgInfo(
+            Parameter parameter,
+            int parameterIndex,
+            RestMethodMetadata restMethodMetadata,
+            String annotationValue,
+            Object defaultValue) {
+        ArgInfo argInfo = ArgInfo.build(parameterIndex, parameter)
+                .setParamAnnotationType(getAnnotationClass())
+                .setAnnotationNameAttribute(annotationValue)
+                .setDefaultValue(defaultValue);
+        restMethodMetadata.addArgInfo(argInfo);
+    }
+
+    protected String getAnnotationValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+        return getValue(annotation);
+    }
+
+    protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+        return AnnotatedMethodParameterProcessor.buildDefaultValue(parameterIndex);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AbstractNoAnnotatedParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AbstractNoAnnotatedParameterProcessor.java
new file mode 100644
index 0000000..76fe1aa
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AbstractNoAnnotatedParameterProcessor.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+
+import java.lang.reflect.Parameter;
+import java.util.Set;
+
+import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader;
+import static org.apache.dubbo.common.utils.ClassUtils.resolveClass;
+
+public abstract class AbstractNoAnnotatedParameterProcessor implements NoAnnotatedParameterRequestTagProcessor {
+
+    public boolean process(Parameter parameter, int parameterIndex, RestMethodMetadata restMethodMetadata) {
+        MediaType mediaType = consumerContentType();
+        if (!contentTypeSupport(restMethodMetadata, mediaType, parameter.getType())) {
+            return false;
+        }
+        boolean isFormBody = isFormContentType(restMethodMetadata);
+        addArgInfo(parameter, parameterIndex, restMethodMetadata, isFormBody);
+        return true;
+    }
+
+    private boolean contentTypeSupport(RestMethodMetadata restMethodMetadata, MediaType mediaType, Class paramType) {
+
+        // @RequestParam String,number param
+        if (mediaType.equals(MediaType.ALL_VALUE)) {
+            // jaxrs no annotation param is from http body
+            if (JAXRSServiceRestMetadataResolver.class.equals(restMethodMetadata.getCodeStyle())) {
+                return true;
+            }
+
+            // spring mvc no annotation param only is used by text data(string,number)
+            if (String.class == paramType || paramType.isPrimitive() || Number.class.isAssignableFrom(paramType)) {
+                return true;
+            }
+        }
+
+        Set<String> consumes = restMethodMetadata.getRequest().getConsumes();
+        for (String consume : consumes) {
+            if (consume.contains(mediaType.value)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    protected boolean isFormContentType(RestMethodMetadata restMethodMetadata) {
+
+        return false;
+    }
+
+    protected void addArgInfo(
+            Parameter parameter, int parameterIndex, RestMethodMetadata restMethodMetadata, boolean isFormBody) {
+        ArgInfo argInfo = ArgInfo.build(parameterIndex, parameter)
+                .setParamAnnotationType(resolveClass(defaultAnnotationClassName(restMethodMetadata), getClassLoader()))
+                .setAnnotationNameAttribute(parameter.getName())
+                .setFormContentType(isFormBody);
+        restMethodMetadata.addArgInfo(argInfo);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AbstractServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AbstractServiceRestMetadataResolver.java
new file mode 100644
index 0000000..3643664
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AbstractServiceRestMetadataResolver.java
@@ -0,0 +1,488 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.common.compact.Dubbo2CompactUtils;
+import org.apache.dubbo.common.utils.MethodComparator;
+import org.apache.dubbo.common.utils.ServiceAnnotationResolver;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.metadata.definition.MethodDefinitionBuilder;
+import org.apache.dubbo.metadata.definition.model.MethodDefinition;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.sort;
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.dubbo.common.function.ThrowableFunction.execute;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isAnyAnnotationPresent;
+import static org.apache.dubbo.common.utils.ClassUtils.forName;
+import static org.apache.dubbo.common.utils.ClassUtils.getAllInterfaces;
+import static org.apache.dubbo.common.utils.MemberUtils.isPrivate;
+import static org.apache.dubbo.common.utils.MemberUtils.isStatic;
+import static org.apache.dubbo.common.utils.MethodUtils.overrides;
+import static org.apache.dubbo.common.utils.MethodUtils.excludedDeclaredClass;
+import static org.apache.dubbo.common.utils.MethodUtils.getAllMethods;
+
+/**
+ * The abstract {@link ServiceRestMetadataResolver} class to provider some template methods assemble the instance of
+ * {@link ServiceRestMetadata} will extended by the sub-classes.
+ *
+ * @since 2.7.6
+ */
+public abstract class AbstractServiceRestMetadataResolver implements ServiceRestMetadataResolver {
+
+    private final Map<String, List<AnnotatedMethodParameterProcessor>> parameterProcessorsMap;
+    private final Set<NoAnnotatedParameterRequestTagProcessor> noAnnotatedParameterRequestTagProcessors;
+
+    public AbstractServiceRestMetadataResolver(ApplicationModel applicationModel) {
+        this.parameterProcessorsMap = loadAnnotatedMethodParameterProcessors(applicationModel);
+        this.noAnnotatedParameterRequestTagProcessors = loadNoAnnotatedMethodParameterProcessors(applicationModel);
+    }
+
+    @Override
+    public final boolean supports(Class<?> serviceType) {
+        return supports(serviceType, false);
+    }
+
+    @Override
+    public final boolean supports(Class<?> serviceType, boolean consumer) {
+
+        if (serviceType == null) {
+            return false;
+        }
+
+        // for consumer
+        //  it is possible serviceType is impl
+        // for provider
+        // for xml config bean  && isServiceAnnotationPresent(serviceType)
+        //  isImplementedInterface(serviceType) SpringController
+        return supports0(serviceType);
+    }
+
+    protected final boolean isImplementedInterface(Class<?> serviceType) {
+        return !getAllInterfaces(serviceType).isEmpty();
+    }
+
+    protected final boolean isServiceAnnotationPresent(Class<?> serviceType) {
+        if (Dubbo2CompactUtils.isEnabled() && Dubbo2CompactUtils.isServiceClassLoaded()) {
+            return isAnyAnnotationPresent(
+                    serviceType, DubboService.class, Service.class, Dubbo2CompactUtils.getServiceClass());
+        } else {
+            return isAnyAnnotationPresent(serviceType, DubboService.class, Service.class);
+        }
+    }
+
+    /**
+     * internal support method
+     *
+     * @param serviceType Dubbo Service interface or type
+     * @return If supports, return <code>true</code>, or <code>false</code>
+     */
+    protected abstract boolean supports0(Class<?> serviceType);
+
+    @Override
+    public final ServiceRestMetadata resolve(Class<?> serviceType) {
+        ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata();
+
+        // Process ServiceRestMetadata
+        processServiceRestMetadata(serviceRestMetadata, serviceType);
+
+        return resolve(serviceType, serviceRestMetadata);
+    }
+
+    @Override
+    public final ServiceRestMetadata resolve(Class<?> serviceType, ServiceRestMetadata serviceRestMetadata) {
+        serviceRestMetadata.setCodeStyle(this.getClass());
+        // Process RestMethodMetadata
+        processAllRestMethodMetadata(serviceRestMetadata, serviceType);
+
+        return serviceRestMetadata;
+    }
+
+    /**
+     * Process the service type including the sub-routines:
+     * <ul>
+     *     <li>{@link ServiceRestMetadata#setServiceInterface(String)}</li>
+     *     <li>{@link ServiceRestMetadata#setVersion(String)}</li>
+     *     <li>{@link ServiceRestMetadata#setGroup(String)}</li>
+     * </ul>
+     *
+     * @param serviceRestMetadata {@link ServiceRestMetadata}
+     * @param serviceType         Dubbo Service interface or type
+     */
+    protected void processServiceRestMetadata(ServiceRestMetadata serviceRestMetadata, Class<?> serviceType) {
+        ServiceAnnotationResolver resolver = new ServiceAnnotationResolver(serviceType);
+        serviceRestMetadata.setServiceInterface(resolver.resolveInterfaceClassName());
+        serviceRestMetadata.setVersion(resolver.resolveVersion());
+        serviceRestMetadata.setGroup(resolver.resolveGroup());
+    }
+
+    /**
+     * Process all {@link RestMethodMetadata}
+     *
+     * @param serviceRestMetadata {@link ServiceRestMetadata}
+     * @param serviceType         Dubbo Service interface or type
+     */
+    protected void processAllRestMethodMetadata(ServiceRestMetadata serviceRestMetadata, Class<?> serviceType) {
+        Class<?> serviceInterfaceClass = resolveServiceInterfaceClass(serviceRestMetadata, serviceType);
+        Map<Method, Method> serviceMethodsMap = resolveServiceMethodsMap(serviceType, serviceInterfaceClass);
+        for (Map.Entry<Method, Method> entry : serviceMethodsMap.entrySet()) {
+            // try the overrider method first
+            Method serviceMethod = entry.getKey();
+            // If failed, it indicates the overrider method does not contain metadata , then try the declared method
+            if (!processRestMethodMetadata(
+                    serviceMethod,
+                    serviceType,
+                    serviceInterfaceClass,
+                    serviceRestMetadata::addRestMethodMetadata,
+                    serviceRestMetadata)) {
+                Method declaredServiceMethod = entry.getValue();
+                processRestMethodMetadata(
+                        declaredServiceMethod,
+                        serviceType,
+                        serviceInterfaceClass,
+                        serviceRestMetadata::addRestMethodMetadata,
+                        serviceRestMetadata);
+            }
+        }
+    }
+
+    /**
+     * Resolve a map of all public services methods from the specified service type and its interface class, whose key is the
+     * declared method, and the value is the overrider method
+     *
+     * @param serviceType           the service interface implementation class
+     * @param serviceInterfaceClass the service interface class
+     * @return non-null read-only {@link Map}
+     */
+    protected Map<Method, Method> resolveServiceMethodsMap(Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        Map<Method, Method> serviceMethodsMap = new LinkedHashMap<>();
+        // exclude the public methods declared in java.lang.Object.class
+        List<Method> declaredServiceMethods =
+                new ArrayList<>(getAllMethods(serviceInterfaceClass, excludedDeclaredClass(Object.class)));
+
+        // controller class
+        if (serviceType.equals(serviceInterfaceClass)) {
+            putServiceMethodToMap(serviceMethodsMap, declaredServiceMethods);
+            return unmodifiableMap(serviceMethodsMap);
+        }
+
+        // for interface , such as consumer interface
+        if (serviceType.isInterface()) {
+            putServiceMethodToMap(serviceMethodsMap, declaredServiceMethods);
+            return unmodifiableMap(serviceMethodsMap);
+        }
+
+        List<Method> serviceMethods = new ArrayList<>(getAllMethods(serviceType, excludedDeclaredClass(Object.class)));
+
+        // sort methods
+        sort(declaredServiceMethods, MethodComparator.INSTANCE);
+        sort(serviceMethods, MethodComparator.INSTANCE);
+
+        // prevent from repeat method (impl proxy) & leaving out method(interface proxy)
+        HashSet<String> methodComparators = new HashSet<>();
+
+        // TODO Map key: method desc &  value: Set<Method> for accelerate loop speed
+        for (Method declaredServiceMethod : declaredServiceMethods) {
+            for (Method serviceMethod : serviceMethods) {
+
+                if (!overrides(serviceMethod, declaredServiceMethod)) {
+                    continue;
+                }
+
+                String methodDesc = getMethodDesc(serviceMethod);
+
+                if (!methodComparators.add(methodDesc)) {
+                    continue;
+                }
+
+                serviceMethodsMap.put(serviceMethod, declaredServiceMethod);
+            }
+        }
+
+        // make them to be read-only
+        return unmodifiableMap(serviceMethodsMap);
+    }
+
+    /**
+     * For simple method desc
+     *
+     * @param serviceMethod
+     * @return
+     */
+    private String getMethodDesc(Method serviceMethod) {
+        return serviceMethod.getName() + Arrays.toString(serviceMethod.getParameterTypes());
+    }
+
+    private void putServiceMethodToMap(Map<Method, Method> serviceMethodsMap, List<Method> declaredServiceMethods) {
+        declaredServiceMethods.stream().forEach(method -> {
+
+            // filter static private default
+            if (isStatic(method) || isPrivate(method) || method.isDefault()) {
+                return;
+            }
+            serviceMethodsMap.put(method, method);
+        });
+    }
+
+    /**
+     * Resolve the class of Dubbo Service interface
+     *
+     * @param serviceRestMetadata {@link ServiceRestMetadata}
+     * @param serviceType         Dubbo Service interface or type
+     * @return non-null
+     * @throws RuntimeException If the class is not found, the {@link RuntimeException} wraps the cause will be thrown
+     */
+    protected Class<?> resolveServiceInterfaceClass(ServiceRestMetadata serviceRestMetadata, Class<?> serviceType) {
+        return execute(serviceType.getClassLoader(), classLoader -> {
+            String serviceInterface = serviceRestMetadata.getServiceInterface();
+            return forName(serviceInterface, classLoader);
+        });
+    }
+
+    /**
+     * Process the single {@link RestMethodMetadata} by the specified {@link Consumer} if present
+     *
+     * @param serviceMethod         Dubbo Service method
+     * @param serviceType           Dubbo Service interface or type
+     * @param serviceInterfaceClass The type of Dubbo Service interface
+     * @param metadataToProcess     {@link RestMethodMetadata} to process if present
+     * @return if processed successfully, return <code>true</code>, or <code>false</code>
+     */
+    protected boolean processRestMethodMetadata(
+            Method serviceMethod,
+            Class<?> serviceType,
+            Class<?> serviceInterfaceClass,
+            Consumer<RestMethodMetadata> metadataToProcess,
+            ServiceRestMetadata serviceRestMetadata) {
+
+        if (!isRestCapableMethod(serviceMethod, serviceType, serviceInterfaceClass)) {
+            return false;
+        }
+
+        String requestPath =
+                resolveRequestPath(serviceMethod, serviceType, serviceInterfaceClass); // requestPath is required
+
+        if (requestPath == null) {
+            return false;
+        }
+
+        String requestMethod =
+                resolveRequestMethod(serviceMethod, serviceType, serviceInterfaceClass); // requestMethod is required
+
+        if (requestMethod == null) {
+            return false;
+        }
+
+        RestMethodMetadata metadata = new RestMethodMetadata();
+
+        metadata.setCodeStyle(this.getClass());
+
+        // to consumer service map
+        metadata.setReflectMethod(serviceMethod);
+
+        MethodDefinition methodDefinition = resolveMethodDefinition(serviceMethod, serviceType, serviceInterfaceClass);
+        // Set MethodDefinition
+        metadata.setMethod(methodDefinition);
+
+        // process produces
+        Set<String> produces = new LinkedHashSet<>();
+        processProduces(serviceMethod, serviceType, serviceInterfaceClass, produces);
+
+        // process consumes
+        Set<String> consumes = new LinkedHashSet<>();
+        processConsumes(serviceMethod, serviceType, serviceInterfaceClass, consumes);
+
+        // Initialize RequestMetadata
+        RequestMetadata request = metadata.getRequest();
+        request.setPath(requestPath);
+        request.appendContextPathFromUrl(serviceRestMetadata.getContextPathFromUrl());
+        request.setMethod(requestMethod);
+        request.setProduces(produces);
+        request.setConsumes(consumes);
+
+        // process the annotated method parameters
+        processAnnotatedMethodParameters(serviceMethod, serviceType, serviceInterfaceClass, metadata);
+
+        // Post-Process
+        postResolveRestMethodMetadata(serviceMethod, serviceType, serviceInterfaceClass, metadata);
+
+        // Accept RestMethodMetadata
+        metadataToProcess.accept(metadata);
+
+        return true;
+    }
+
+    /**
+     * Test the service method is capable of REST or not?
+     *
+     * @param serviceMethod         Dubbo Service method
+     * @param serviceType           Dubbo Service interface or type
+     * @param serviceInterfaceClass The type of Dubbo Service interface
+     * @return If capable, return <code>true</code>
+     */
+    protected abstract boolean isRestCapableMethod(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass);
+
+    /**
+     * Resolve the request method
+     *
+     * @param serviceMethod         Dubbo Service method
+     * @param serviceType           Dubbo Service interface or type
+     * @param serviceInterfaceClass The type of Dubbo Service interface
+     * @return if can't be resolve, return <code>null</code>
+     */
+    protected abstract String resolveRequestMethod(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass);
+
+    /**
+     * Resolve the request path
+     *
+     * @param serviceMethod         Dubbo Service method
+     * @param serviceType           Dubbo Service interface or type
+     * @param serviceInterfaceClass The type of Dubbo Service interface
+     * @return if can't be resolve, return <code>null</code>
+     */
+    protected abstract String resolveRequestPath(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass);
+
+    /**
+     * Resolve the {@link MethodDefinition}
+     *
+     * @param serviceMethod         Dubbo Service method
+     * @param serviceType           Dubbo Service interface or type
+     * @param serviceInterfaceClass The type of Dubbo Service interface
+     * @return if can't be resolve, return <code>null</code>
+     * @see MethodDefinitionBuilder
+     */
+    protected MethodDefinition resolveMethodDefinition(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        MethodDefinitionBuilder builder = new MethodDefinitionBuilder();
+        return builder.build(serviceMethod);
+    }
+
+    private void processAnnotatedMethodParameters(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, RestMethodMetadata metadata) {
+        int paramCount = serviceMethod.getParameterCount();
+        Parameter[] parameters = serviceMethod.getParameters();
+        for (int i = 0; i < paramCount; i++) {
+            Parameter parameter = parameters[i];
+            // Add indexed parameter name
+            metadata.addIndexToName(i, parameter.getName());
+            processAnnotatedMethodParameter(parameter, i, serviceMethod, serviceType, serviceInterfaceClass, metadata);
+        }
+    }
+
+    private void processAnnotatedMethodParameter(
+            Parameter parameter,
+            int parameterIndex,
+            Method serviceMethod,
+            Class<?> serviceType,
+            Class<?> serviceInterfaceClass,
+            RestMethodMetadata metadata) {
+        Annotation[] annotations = parameter.getAnnotations();
+
+        if (annotations == null || annotations.length == 0) {
+
+            for (NoAnnotatedParameterRequestTagProcessor processor : noAnnotatedParameterRequestTagProcessors) {
+                // no annotation only one default annotationType
+                if (processor.process(parameter, parameterIndex, metadata)) {
+                    return;
+                }
+            }
+        }
+
+        for (Annotation annotation : annotations) {
+            String annotationType = annotation.annotationType().getName();
+            parameterProcessorsMap.getOrDefault(annotationType, emptyList()).forEach(processor -> {
+                processor.process(
+                        annotation,
+                        parameter,
+                        parameterIndex,
+                        serviceMethod,
+                        serviceType,
+                        serviceInterfaceClass,
+                        metadata);
+            });
+        }
+    }
+
+    protected abstract void processProduces(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> produces);
+
+    protected abstract void processConsumes(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> consumes);
+
+    protected void postResolveRestMethodMetadata(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, RestMethodMetadata metadata) {
+
+        // parse pathVariable index from url by annotation info
+        PathUtil.setArgInfoSplitIndex(metadata.getRequest().getPath(), metadata.getArgInfos());
+    }
+
+    private static Map<String, List<AnnotatedMethodParameterProcessor>> loadAnnotatedMethodParameterProcessors(
+            ApplicationModel applicationModel) {
+        Map<String, List<AnnotatedMethodParameterProcessor>> parameterProcessorsMap = new LinkedHashMap<>();
+        applicationModel
+                .getExtensionLoader(AnnotatedMethodParameterProcessor.class)
+                .getSupportedExtensionInstances()
+                .forEach(processor -> {
+                    List<AnnotatedMethodParameterProcessor> processors = parameterProcessorsMap.computeIfAbsent(
+                            processor.getAnnotationName(), k -> new LinkedList<>());
+                    processors.add(processor);
+                });
+        return parameterProcessorsMap;
+    }
+
+    private static Set<NoAnnotatedParameterRequestTagProcessor> loadNoAnnotatedMethodParameterProcessors(
+            ApplicationModel applicationModel) {
+        Set<NoAnnotatedParameterRequestTagProcessor> supportedExtensionInstances = applicationModel
+                .getExtensionLoader(NoAnnotatedParameterRequestTagProcessor.class)
+                .getSupportedExtensionInstances();
+
+        return supportedExtensionInstances;
+    }
+
+    public static boolean isServiceMethodAnnotationPresent(Class<?> serviceImpl, String annotationClass) {
+        List<Method> allMethods = getAllMethods(serviceImpl, excludedDeclaredClass(Object.class));
+
+        for (Method method : allMethods) {
+            if (isAnnotationPresent(method, annotationClass)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AnnotatedMethodParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AnnotatedMethodParameterProcessor.java
new file mode 100644
index 0000000..21e47d8
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/AnnotatedMethodParameterProcessor.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.common.lang.Prioritized;
+
+import javax.lang.model.element.VariableElement;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+/**
+ * The interface to process the annotated method parameter
+ *
+ * @since 2.7.6
+ */
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface AnnotatedMethodParameterProcessor extends Prioritized {
+
+    /**
+     * The string presenting the annotation name
+     *
+     * @return non-null
+     */
+    String getAnnotationName();
+
+    /**
+     * The string presenting the annotation type
+     *
+     * @return non-null
+     */
+    Class getAnnotationClass();
+
+    /**
+     * Process the specified method {@link VariableElement parameter}
+     *
+     * @param annotation            {@link Annotation the target annotation} whose type is {@link #getAnnotationName()}
+     * @param parameter             the method parameter
+     * @param parameterIndex        the index of method parameter
+     * @param method                {@link Method method that parameter belongs to}
+     * @param serviceType           Dubbo Service interface or type
+     * @param serviceInterfaceClass The type of Dubbo Service interface
+     * @param restMethodMetadata    {@link org.apache.dubbo.metadata.rest.RestMethodMetadata the metadata is used to update}
+     */
+    void process(
+        Annotation annotation,
+        Parameter parameter,
+        int parameterIndex,
+        Method method,
+        Class<?> serviceType,
+        Class<?> serviceInterfaceClass,
+        RestMethodMetadata restMethodMetadata);
+
+    /**
+     * Build the default value
+     *
+     * @param parameterIndex the index of parameter
+     * @return the placeholder
+     */
+    static String buildDefaultValue(int parameterIndex) {
+        return "{" + parameterIndex + "}";
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ArgInfo.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ArgInfo.java
new file mode 100644
index 0000000..c7f483d
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ArgInfo.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+
+/**
+ * description of service method args info
+ */
+public class ArgInfo {
+    /**
+     * method arg index 0,1,2,3
+     */
+    private int index;
+    /**
+     * method annotation name or name
+     */
+    private String annotationNameAttribute;
+
+    /**
+     * param annotation type
+     */
+    private Class paramAnnotationType;
+
+    /**
+     * param Type
+     */
+    private Class paramType;
+
+    /**
+     *  param actual Type(collection,map,array)
+     */
+    private Type actualType;
+
+    /**
+     * param name
+     */
+    private String paramName;
+
+    /**
+     * url split("/") String[n]  index
+     */
+    private int urlSplitIndex;
+
+    private Object defaultValue;
+
+    private boolean formContentType;
+
+    public ArgInfo(int index, String name, Class paramType) {
+        this.index = index;
+        this.paramName = name;
+        this.paramType = paramType;
+    }
+
+    public ArgInfo(int index, Parameter parameter) {
+        this(index, parameter.getName(), parameter.getType());
+        this.actualType = parameter.getParameterizedType();
+    }
+
+    public ArgInfo() {}
+
+    public int getIndex() {
+        return index;
+    }
+
+    public ArgInfo setIndex(int index) {
+        this.index = index;
+        return this;
+    }
+
+    public String getAnnotationNameAttribute() {
+        if (annotationNameAttribute == null) {
+            // such as String param no annotation
+            return paramName;
+        }
+        return annotationNameAttribute;
+    }
+
+    public ArgInfo setAnnotationNameAttribute(String annotationNameAttribute) {
+        this.annotationNameAttribute = annotationNameAttribute;
+        return this;
+    }
+
+    public Class getParamAnnotationType() {
+        return paramAnnotationType;
+    }
+
+    public ArgInfo setParamAnnotationType(Class paramAnnotationType) {
+        this.paramAnnotationType = paramAnnotationType;
+        return this;
+    }
+
+    public Class getParamType() {
+        return paramType;
+    }
+
+    public void setParamType(Class paramType) {
+        this.paramType = paramType;
+    }
+
+    public int getUrlSplitIndex() {
+        return urlSplitIndex;
+    }
+
+    public void setUrlSplitIndex(int urlSplitIndex) {
+        this.urlSplitIndex = urlSplitIndex;
+    }
+
+    public static ArgInfo build() {
+        return new ArgInfo();
+    }
+
+    public static ArgInfo build(int index, Parameter parameter) {
+        return new ArgInfo(index, parameter);
+    }
+
+    public String getParamName() {
+        return paramName;
+    }
+
+    public ArgInfo setParamName(String paramName) {
+        this.paramName = paramName;
+        return this;
+    }
+
+    public Object getDefaultValue() {
+        return defaultValue;
+    }
+
+    public ArgInfo setDefaultValue(Object defaultValue) {
+        this.defaultValue = defaultValue;
+        return this;
+    }
+
+    public boolean isFormContentType() {
+        return formContentType;
+    }
+
+    public ArgInfo setFormContentType(boolean isFormContentType) {
+        this.formContentType = isFormContentType;
+        return this;
+    }
+
+    public Type actualReflectType() {
+        return actualType;
+    }
+
+    @Override
+    public String toString() {
+        return "ArgInfo{" + "index="
+                + index + ", annotationNameAttribute='"
+                + annotationNameAttribute + '\'' + ", paramAnnotationType="
+                + paramAnnotationType + ", paramType="
+                + paramType + ", paramName='"
+                + paramName + '\'' + ", urlSplitIndex="
+                + urlSplitIndex + ", defaultValue="
+                + defaultValue + ", formContentType="
+                + formContentType + '}';
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ClassPathServiceRestMetadataReader.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ClassPathServiceRestMetadataReader.java
new file mode 100644
index 0000000..5016699
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ClassPathServiceRestMetadataReader.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.common.utils.IOUtils;
+import org.apache.dubbo.common.utils.JsonUtils;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+
+import static java.util.Collections.unmodifiableList;
+import static org.apache.dubbo.common.function.ThrowableAction.execute;
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.METADATA_ENCODING;
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SERVICE_REST_METADATA_RESOURCE_PATH;
+
+/**
+ * Class-Path based {@link ServiceRestMetadataReader} implementation
+ *
+ * @see ServiceRestMetadataReader
+ * @since 2.7.6
+ */
+public class ClassPathServiceRestMetadataReader implements ServiceRestMetadataReader {
+
+    private final String serviceRestMetadataJsonResourcePath;
+
+    public ClassPathServiceRestMetadataReader() {
+        this(SERVICE_REST_METADATA_RESOURCE_PATH);
+    }
+
+    public ClassPathServiceRestMetadataReader(String serviceRestMetadataJsonResourcePath) {
+        this.serviceRestMetadataJsonResourcePath = serviceRestMetadataJsonResourcePath;
+    }
+
+    @Override
+    public List<ServiceRestMetadata> read() {
+
+        List<ServiceRestMetadata> serviceRestMetadataList = new LinkedList<>();
+
+        ClassLoader classLoader = getClass().getClassLoader();
+
+        execute(() -> {
+            Enumeration<URL> resources = classLoader.getResources(serviceRestMetadataJsonResourcePath);
+            while (resources.hasMoreElements()) {
+                URL resource = resources.nextElement();
+                InputStream inputStream = resource.openStream();
+                String json = IOUtils.read(inputStream, METADATA_ENCODING);
+                serviceRestMetadataList.addAll(JsonUtils.toJavaList(json, ServiceRestMetadata.class));
+            }
+        });
+
+        return unmodifiableList(serviceRestMetadataList);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/DefaultServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/DefaultServiceRestMetadataResolver.java
new file mode 100644
index 0000000..190f21e
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/DefaultServiceRestMetadataResolver.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.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.lang.reflect.Method;
+import java.util.Set;
+
+/**
+ * The default implementation {@link ServiceRestMetadataResolver}
+ *
+ * @since 2.7.6
+ */
+public class DefaultServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
+    public DefaultServiceRestMetadataResolver(ApplicationModel applicationModel) {
+        super(applicationModel);
+    }
+
+    @Override
+    protected boolean supports0(Class<?> serviceType) {
+        return false;
+    }
+
+    @Override
+    protected boolean isRestCapableMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        return false;
+    }
+
+    @Override
+    protected String resolveRequestMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        return null;
+    }
+
+    @Override
+    protected String resolveRequestPath(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        return null;
+    }
+
+    @Override
+    protected void processProduces(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> produces) {}
+
+    @Override
+    protected void processConsumes(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> consumes) {}
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/JAXRSClassConstants.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/JAXRSClassConstants.java
new file mode 100644
index 0000000..4a15f81
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/JAXRSClassConstants.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader;
+import static org.apache.dubbo.common.utils.ClassUtils.resolveClass;
+
+public interface JAXRSClassConstants extends RestMetadataConstants.JAX_RS {
+    /**
+     * The annotation class of @Path
+     */
+    Class PATH_ANNOTATION_CLASS = resolveClass(PATH_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @FormParam
+     */
+    Class FORM_PARAM_ANNOTATION_CLASS = resolveClass(FORM_PARAM_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @Form
+     */
+    Class FORM_BODY_ANNOTATION_CLASS = resolveClass(REST_EASY_FORM_BODY_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @HeaderParam
+     */
+    Class HEADER_PARAM_ANNOTATION_CLASS = resolveClass(HEADER_PARAM_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @MatrixParam
+     */
+    Class MATRIX_PARAM_ANNOTATION_CLASS = resolveClass(MATRIX_PARAM_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class  of @QueryParam
+     */
+    Class QUERY_PARAM_ANNOTATION_CLASS = resolveClass(QUERY_PARAM_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @Body
+     */
+    Class REST_EASY_BODY_ANNOTATION_CLASS = resolveClass(REST_EASY_BODY_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @PathParam
+     */
+    Class PATH_PARAM_ANNOTATION_CLASS = resolveClass(PATH_PARAM_ANNOTATION_CLASS_NAME, getClassLoader());
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/NoAnnotatedParameterRequestTagProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/NoAnnotatedParameterRequestTagProcessor.java
new file mode 100644
index 0000000..6bc89ff
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/NoAnnotatedParameterRequestTagProcessor.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+
+import java.lang.reflect.Parameter;
+
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface NoAnnotatedParameterRequestTagProcessor {
+    MediaType consumerContentType();
+
+    String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata);
+
+    boolean process(Parameter parameter, int parameterIndex, RestMethodMetadata restMethodMetadata);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ParamType.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ParamType.java
new file mode 100644
index 0000000..dee7b99
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ParamType.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.metadata.extension.rest.api.tag.BodyTag;
+import org.apache.dubbo.metadata.extension.rest.api.tag.ParamTag;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum ParamType {
+    HEADER(addSupportTypes(
+            JAXRSClassConstants.HEADER_PARAM_ANNOTATION_CLASS,
+            SpringMvcClassConstants.REQUEST_HEADER_ANNOTATION_CLASS)),
+
+    PARAM(addSupportTypes(
+            JAXRSClassConstants.QUERY_PARAM_ANNOTATION_CLASS,
+            SpringMvcClassConstants.REQUEST_PARAM_ANNOTATION_CLASS,
+            ParamTag.class)),
+
+    BODY(addSupportTypes(
+            JAXRSClassConstants.REST_EASY_BODY_ANNOTATION_CLASS,
+            SpringMvcClassConstants.REQUEST_BODY_ANNOTATION_CLASS,
+            BodyTag.class)),
+
+    PATH(addSupportTypes(
+            JAXRSClassConstants.PATH_PARAM_ANNOTATION_CLASS, SpringMvcClassConstants.PATH_VARIABLE_ANNOTATION_CLASS)),
+
+    FORM(addSupportTypes(
+            JAXRSClassConstants.FORM_PARAM_ANNOTATION_CLASS,
+            JAXRSClassConstants.FORM_BODY_ANNOTATION_CLASS,
+            SpringMvcClassConstants.REQUEST_BODY_ANNOTATION_CLASS)),
+
+    PROVIDER_BODY(addSupportTypes(
+            JAXRSClassConstants.REST_EASY_BODY_ANNOTATION_CLASS,
+            JAXRSClassConstants.FORM_PARAM_ANNOTATION_CLASS,
+            SpringMvcClassConstants.REQUEST_BODY_ANNOTATION_CLASS,
+            BodyTag.class,
+            JAXRSClassConstants.FORM_BODY_ANNOTATION_CLASS)),
+
+    EMPTY(addSupportTypes());
+    private List<Class> annotationClasses;
+
+    ParamType(List<Class> annotationClasses) {
+        this.annotationClasses = annotationClasses;
+    }
+
+    public boolean supportAnno(Class anno) {
+        if (anno == null) {
+            return false;
+        }
+        return this.annotationClasses.contains(anno);
+    }
+
+    /**
+     * exclude null types
+     *
+     * @param classes
+     * @return
+     */
+    private static List<Class> addSupportTypes(Class... classes) {
+
+        ArrayList<Class> types = new ArrayList<>();
+
+        for (Class clazz : classes) {
+
+            if (clazz == null) {
+                continue;
+            }
+
+            types.add(clazz);
+        }
+
+        return types;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/PathMatcher.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/PathMatcher.java
new file mode 100644
index 0000000..3fa571d
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/PathMatcher.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * for http request  path match
+ */
+public class PathMatcher {
+    private static final String SEPARATOR = "/";
+    private String path;
+    private String version; // service version
+    private String group; // service group
+    private Integer port; // service port
+    private String[] pathSplits;
+    private boolean hasPathVariable;
+    private String contextPath;
+    private String httpMethod;
+    // for provider http method compare,http 405
+    private boolean needCompareHttpMethod = true;
+    //  compare method directly (for get Invoker by method)
+    private boolean needCompareServiceMethod = false;
+
+    // service method
+    private Method method;
+
+    public PathMatcher(String path) {
+        this(path, null, null, null);
+    }
+
+    public PathMatcher(String path, String version, String group, Integer port) {
+        this.path = path;
+        dealPathVariable(path);
+        this.version = version;
+        this.group = group;
+        this.port = (port == null || port == -1 || port == 0) ? null : port;
+    }
+
+    public PathMatcher(String path, String version, String group, Integer port, String httpMethod) {
+        this(path, version, group, port);
+        setHttpMethod(httpMethod);
+    }
+
+    public PathMatcher(Method method) {
+        this.method = method;
+    }
+
+    private void dealPathVariable(String path) {
+        if (path == null) {
+            return;
+        }
+        this.pathSplits = path.split(SEPARATOR);
+
+        for (String pathSplit : pathSplits) {
+
+            if (isPlaceHold(pathSplit)) {
+                hasPathVariable = true;
+                break;
+            }
+        }
+    }
+
+    private void setPath(String path) {
+        this.path = path;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+    public void setPort(Integer port) {
+        this.port = port;
+    }
+
+    public void setContextPath(String contextPath) {
+
+        contextPath = contextPathFormat(contextPath);
+
+        this.contextPath = contextPath;
+
+        setPath(contextPath + path);
+
+        dealPathVariable(path);
+    }
+
+    public static PathMatcher getInvokeCreatePathMatcher(
+            String path, String version, String group, Integer port, String method) {
+        return new PathMatcher(path, version, group, port, method).compareHttpMethod(false);
+    }
+
+    public static PathMatcher getInvokeCreatePathMatcher(Method serviceMethod) {
+        return new PathMatcher(serviceMethod).setNeedCompareServiceMethod(true);
+    }
+
+    public static PathMatcher convertPathMatcher(PathMatcher pathMatcher) {
+        return getInvokeCreatePathMatcher(
+                pathMatcher.path, pathMatcher.version, pathMatcher.group, pathMatcher.port, pathMatcher.httpMethod);
+    }
+
+    public boolean hasPathVariable() {
+        return hasPathVariable;
+    }
+
+    public Integer getPort() {
+        return port;
+    }
+
+    public String getHttpMethod() {
+        return httpMethod;
+    }
+
+    public PathMatcher setHttpMethod(String httpMethod) {
+        this.httpMethod = httpMethod;
+        return this;
+    }
+
+    public PathMatcher compareHttpMethod(boolean needCompareHttpMethod) {
+        this.needCompareHttpMethod = needCompareHttpMethod;
+        return this;
+    }
+
+    public Method getMethod() {
+        return method;
+    }
+
+    public void setMethod(Method method) {
+        this.method = method;
+    }
+
+    private PathMatcher setNeedCompareServiceMethod(boolean needCompareServiceMethod) {
+        this.needCompareServiceMethod = needCompareServiceMethod;
+        return this;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PathMatcher that = (PathMatcher) o;
+        return serviceMethodEqual(that, this) || pathMatch(that);
+    }
+
+    private boolean pathMatch(PathMatcher that) {
+        return (!that.needCompareServiceMethod && !needCompareServiceMethod) // no need service method compare
+                && pathEqual(that) // path compare
+                && Objects.equals(version, that.version) // service  version compare
+                && httpMethodMatch(that) // http method compare
+                && Objects.equals(group, that.group)
+                && Objects.equals(port, that.port);
+    }
+
+    /**
+     * it is needed to compare http method when one of needCompareHttpMethod is true,and don`t compare when both needCompareHttpMethod are false
+     *
+     * @param that
+     * @return
+     */
+    private boolean httpMethodMatch(PathMatcher that) {
+        return !that.needCompareHttpMethod || !this.needCompareHttpMethod
+                ? true
+                : Objects.equals(this.httpMethod, that.httpMethod);
+    }
+
+    private boolean serviceMethodEqual(PathMatcher thatPathMatcher, PathMatcher thisPathMatcher) {
+        Method thatMethod = thatPathMatcher.method;
+        Method thisMethod = thisPathMatcher.method;
+        return thatMethod != null
+                && thisMethod != null
+                && (thatPathMatcher.needCompareServiceMethod || thisPathMatcher.needCompareServiceMethod)
+                && thisMethod.getName().equals(thatMethod.getName())
+                && Arrays.equals(thisMethod.getParameterTypes(), thatMethod.getParameterTypes());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(version, group, port);
+    }
+
+    private boolean pathEqual(PathMatcher pathMatcher) {
+        // path is null return false directly
+        if (this.path == null || pathMatcher.path == null) {
+            return false;
+        }
+
+        // no place hold
+        if (!pathMatcher.hasPathVariable) {
+            return this.path.equals(pathMatcher.path);
+        }
+
+        String[] pathSplits = pathMatcher.pathSplits;
+        String[] thisPathSplits = this.pathSplits;
+
+        if (thisPathSplits.length != pathSplits.length) {
+            return false;
+        }
+
+        for (int i = 0; i < pathSplits.length; i++) {
+            boolean equals = thisPathSplits[i].equals(pathSplits[i]);
+            if (equals) {
+                continue;
+            } else {
+                if (placeHoldCompare(pathSplits[i], thisPathSplits[i])) {
+                    continue;
+                } else {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private boolean placeHoldCompare(String pathSplit, String pathToCompare) {
+        boolean startAndEndEqual = isPlaceHold(pathSplit) || isPlaceHold(pathToCompare);
+
+        // start {  end }
+        if (!startAndEndEqual) {
+            return false;
+        }
+
+        // exclude  {}
+        boolean lengthCondition = pathSplit.length() >= 3 || pathToCompare.length() >= 3;
+
+        if (!lengthCondition) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean isPlaceHold(String pathSplit) {
+        return pathSplit.startsWith("{") && pathSplit.endsWith("}");
+    }
+
+    private String contextPathFormat(String contextPath) {
+
+        if (contextPath == null || contextPath.equals(SEPARATOR) || contextPath.length() == 0) {
+            return "";
+        }
+
+        return pathFormat(contextPath);
+    }
+
+    private String pathFormat(String path) {
+        if (path.startsWith(SEPARATOR)) {
+            return path;
+        } else {
+            return SEPARATOR + path;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "PathMatcher{" + "path='"
+                + path + '\'' + ", version='"
+                + version + '\'' + ", group='"
+                + group + '\'' + ", port="
+                + port + ", hasPathVariable="
+                + hasPathVariable + ", contextPath='"
+                + contextPath + '\'' + ", httpMethod='"
+                + httpMethod + '\'' + '}';
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/PathUtil.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/PathUtil.java
new file mode 100644
index 0000000..4371352
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/PathUtil.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.metadata.MetadataConstants;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * is used to parse url pathVariable
+ * <p>
+ * String[] splits= url.split("/")
+ * List<String> strings = Arrays.asList(split);
+ * strings.set(UrlSplitIndex, (String) args.get(argIndex));
+ */
+public class PathUtil {
+    private static final String SEPARATOR = MetadataConstants.PATH_SEPARATOR;
+
+    /**
+     * generate real path from  rawPath according to argInfo and method args
+     *
+     * @param rawPath
+     * @param argInfos
+     * @param args
+     * @return
+     */
+    public static String resolvePathVariable(String rawPath, List<ArgInfo> argInfos, List<Object> args) {
+
+        String[] split = rawPath.split(SEPARATOR);
+
+        List<String> strings = Arrays.asList(split);
+
+        List<ArgInfo> pathArgInfos = new ArrayList<>();
+
+        for (ArgInfo argInfo : argInfos) {
+            if (ParamType.PATH.supportAnno(argInfo.getParamAnnotationType())) {
+                pathArgInfos.add(argInfo);
+            }
+        }
+
+        for (ArgInfo pathArgInfo : pathArgInfos) {
+            strings.set(pathArgInfo.getUrlSplitIndex(), String.valueOf(args.get(pathArgInfo.getIndex())));
+        }
+
+        String pat = SEPARATOR;
+
+        for (String string : strings) {
+
+            if (string.length() == 0) {
+                continue;
+            }
+
+            pat = pat + string + SEPARATOR;
+        }
+
+        if (pat.endsWith(SEPARATOR)) {
+            pat = pat.substring(0, pat.lastIndexOf(SEPARATOR));
+        }
+
+        return pat;
+    }
+
+    /**
+     * parse pathVariable index from url by annotation info
+     *
+     * @param rawPath
+     * @param argInfos
+     */
+    public static void setArgInfoSplitIndex(String rawPath, List<ArgInfo> argInfos) {
+        String[] split = rawPath.split(SEPARATOR);
+
+        List<PathPair> pathPairs = new ArrayList<>();
+
+        for (ArgInfo argInfo : argInfos) {
+            if (ParamType.PATH.supportAnno(argInfo.getParamAnnotationType())) {
+                pathPairs.add(new PathPair(argInfo));
+            }
+        }
+
+        for (int i = 0; i < split.length; i++) {
+            String s = split[i];
+            for (PathPair pathPair : pathPairs) {
+                boolean match = pathPair.match(s);
+                if (match) {
+                    pathPair.setArgInfoSplitIndex(i);
+                }
+            }
+        }
+    }
+
+    public static class PathPair {
+
+        String value;
+
+        ArgInfo argInfo;
+
+        public PathPair(ArgInfo argInfo) {
+            this.argInfo = argInfo;
+            this.value = argInfo.getAnnotationNameAttribute();
+        }
+
+        public String getPatten() {
+            return "{" + value + "}";
+        }
+
+        public String getLeftPatten() {
+            return "{" + value;
+        }
+
+        public String getRightPatten() {
+            return "}";
+        }
+
+        public boolean match(String value) {
+            return getPatten().equals(value) // for : {id}
+                    || (value.startsWith(getLeftPatten()) && value.endsWith(getRightPatten())); // for : {id: \d+}
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setArgInfo(ArgInfo argInfo) {
+            this.argInfo = argInfo;
+        }
+
+        public void setArgInfoSplitIndex(int urlSplitIndex) {
+            this.argInfo.setUrlSplitIndex(urlSplitIndex);
+        }
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/RequestMetadata.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/RequestMetadata.java
new file mode 100644
index 0000000..3f12ecf
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/RequestMetadata.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.dubbo.common.utils.PathUtils.normalize;
+import static org.apache.dubbo.common.utils.StringUtils.SLASH;
+import static org.apache.dubbo.common.utils.StringUtils.isBlank;
+
+/**
+ * The metadata class for REST request
+ *
+ * @since 2.7.6
+ */
+public class RequestMetadata implements Serializable {
+
+    private static final long serialVersionUID = -240099840085329958L;
+
+    private String method;
+
+    private String path;
+
+    private Map<String, List<String>> params = new LinkedHashMap<>();
+
+    private Map<String, List<String>> headers = new LinkedHashMap<>();
+
+    private Set<String> consumes = new LinkedHashSet<>();
+
+    private Set<String> produces = new LinkedHashSet<>();
+
+    /**
+     * Default Constructor
+     */
+    public RequestMetadata() {}
+
+    public String getMethod() {
+        return method;
+    }
+
+    public void setMethod(String method) {
+        this.method = method == null ? null : method.toUpperCase();
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = normalize(path);
+
+        if (!path.startsWith(SLASH)) {
+            this.path = SLASH + path;
+        }
+    }
+
+    public Map<String, List<String>> getParams() {
+        return unmodifiableMap(params);
+    }
+
+    public void setParams(Map<String, List<String>> params) {
+        params(params);
+    }
+
+    private static void add(Map<String, List<String>> multiValueMap, String key, String value) {
+        if (isBlank(key)) {
+            return;
+        }
+        List<String> values = get(multiValueMap, key, true);
+        values.add(value);
+    }
+
+    private static <T extends Collection<String>> void addAll(
+            Map<String, List<String>> multiValueMap, Map<String, T> source) {
+        for (Map.Entry<String, T> entry : source.entrySet()) {
+            String key = entry.getKey();
+            for (String value : entry.getValue()) {
+                add(multiValueMap, key, value);
+            }
+        }
+    }
+
+    private static String getFirst(Map<String, List<String>> multiValueMap, String key) {
+        List<String> values = get(multiValueMap, key);
+        return CollectionUtils.isNotEmpty(values) ? values.get(0) : null;
+    }
+
+    private static List<String> get(Map<String, List<String>> multiValueMap, String key) {
+        return get(multiValueMap, key, false);
+    }
+
+    private static List<String> get(Map<String, List<String>> multiValueMap, String key, boolean createIfAbsent) {
+        return createIfAbsent ? multiValueMap.computeIfAbsent(key, k -> new LinkedList<>()) : multiValueMap.get(key);
+    }
+
+    public Map<String, List<String>> getHeaders() {
+        return unmodifiableMap(headers);
+    }
+
+    public void setHeaders(Map<String, List<String>> headers) {
+        headers(headers);
+    }
+
+    public Set<String> getConsumes() {
+        return consumes;
+    }
+
+    public void setConsumes(Set<String> consumes) {
+        this.consumes = consumes;
+    }
+
+    public Set<String> getProduces() {
+        return produces;
+    }
+
+    public void setProduces(Set<String> produces) {
+        this.produces = produces;
+    }
+
+    public Set<String> getParamNames() {
+        return new HashSet<>(params.keySet());
+    }
+
+    public Set<String> getHeaderNames() {
+        return new HashSet<>(headers.keySet());
+    }
+
+    //    public List<MediaType> getConsumeMediaTypes() {
+    //        return toMediaTypes(consumes);
+    //    }
+    //
+    //    public List<MediaType> getProduceMediaTypes() {
+    //        return toMediaTypes(produces);
+    //    }
+
+    public String getParameter(String name) {
+        return getFirst(params, name);
+    }
+
+    public String getHeader(String name) {
+        return getFirst(headers, name);
+    }
+
+    public RequestMetadata addParam(String name, String value) {
+        add(params, name, value);
+        return this;
+    }
+
+    public RequestMetadata addHeader(String name, String value) {
+        add(headers, name, value);
+        return this;
+    }
+
+    private <T extends Collection<String>> RequestMetadata params(Map<String, T> params) {
+        addAll(this.params, params);
+        return this;
+    }
+
+    private <T extends Collection<String>> RequestMetadata headers(Map<String, List<String>> headers) {
+        if (headers != null && !headers.isEmpty()) {
+            Map<String, List<String>> httpHeaders = new LinkedHashMap<>();
+            // Add all headers
+            addAll(httpHeaders, headers);
+            // Handles "Content-Type" and "Accept" headers if present
+            //            mediaTypes(httpHeaders, HttpHeaders.CONTENT_TYPE, this.consumes);
+            //            mediaTypes(httpHeaders, HttpHeaders.ACCEPT, this.produces);
+            this.headers.putAll(httpHeaders);
+        }
+        return this;
+    }
+
+    public void appendContextPathFromUrl(String contextPathFromUrl) {
+        if (contextPathFromUrl == null) {
+            return;
+        }
+        setPath(contextPathFromUrl + path);
+    }
+
+    public boolean methodAllowed(String method) {
+        return method != null && method.equals(this.method);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof RequestMetadata)) {
+            return false;
+        }
+        RequestMetadata that = (RequestMetadata) o;
+        return Objects.equals(method, that.method)
+                && Objects.equals(path, that.path)
+                && Objects.equals(consumes, that.consumes)
+                && Objects.equals(produces, that.produces)
+                &&
+                // Metadata should not compare the values
+                Objects.equals(getParamNames(), that.getParamNames())
+                && Objects.equals(getHeaderNames(), that.getHeaderNames());
+    }
+
+    @Override
+    public int hashCode() {
+        // The values of metadata should not use for the hashCode() method
+        return Objects.hash(method, path, consumes, produces, getParamNames(), getHeaderNames());
+    }
+
+    @Override
+    public String toString() {
+        return "RequestMetadata{" + "method='" + method + '\'' + ", path='" + path + '\''
+                + ", params=" + params + ", headers=" + headers + ", consumes=" + consumes
+                + ", produces=" + produces + '}';
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/RestMetadataConstants.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/RestMetadataConstants.java
new file mode 100644
index 0000000..abe035d
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/RestMetadataConstants.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import java.lang.annotation.Annotation;
+
+import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader;
+import static org.apache.dubbo.common.utils.ClassUtils.resolveClass;
+
+/**
+ * The REST Metadata Constants definition interface
+ *
+ * @since 2.7.6
+ */
+public interface RestMetadataConstants {
+
+    /**
+     * The encoding of metadata
+     */
+    String METADATA_ENCODING = "UTF-8";
+
+    /**
+     * {@link ServiceRestMetadata} Resource PATH
+     */
+    String SERVICE_REST_METADATA_RESOURCE_PATH = "META-INF/dubbo/service-rest-metadata.json";
+
+    /**
+     * JAX-RS
+     */
+    interface JAX_RS {
+
+        /**
+         * The annotation class name of @Path
+         */
+        String PATH_ANNOTATION_CLASS_NAME = "javax.ws.rs.Path";
+
+        /**
+         * The annotation class name of @HttpMethod
+         */
+        String HTTP_METHOD_ANNOTATION_CLASS_NAME = "javax.ws.rs.HttpMethod";
+
+        /**
+         * The annotation class name of @Produces
+         */
+        String PRODUCES_ANNOTATION_CLASS_NAME = "javax.ws.rs.Produces";
+
+        /**
+         * The annotation class name of @Consumes
+         */
+        String CONSUMES_ANNOTATION_CLASS_NAME = "javax.ws.rs.Consumes";
+
+        /**
+         * The annotation class name of @DefaultValue
+         */
+        String DEFAULT_VALUE_ANNOTATION_CLASS_NAME = "javax.ws.rs.DefaultValue";
+
+        /**
+         * The annotation class name of @FormParam
+         */
+        String FORM_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.FormParam";
+
+        /**
+         * The annotation class name of @HeaderParam
+         */
+        String HEADER_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.HeaderParam";
+
+        /**
+         * The annotation class name of @MatrixParam
+         */
+        String MATRIX_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.MatrixParam";
+
+        /**
+         * The annotation class name of @QueryParam
+         */
+        String QUERY_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.QueryParam";
+
+        /**
+         * The annotation class name of @Body
+         */
+        String REST_EASY_BODY_ANNOTATION_CLASS_NAME = "org.jboss.resteasy.annotations.Body";
+
+        /**
+         * The annotation class name of @Form
+         */
+        String REST_EASY_FORM_BODY_ANNOTATION_CLASS_NAME = "org.jboss.resteasy.annotations.Form";
+
+        /**
+         * The annotation class name of @PathParam
+         */
+        String PATH_PARAM_ANNOTATION_CLASS_NAME = "javax.ws.rs.PathParam";
+    }
+
+    /**
+     * Spring MVC
+     */
+    interface SPRING_MVC {
+
+        /**
+         * The annotation class name of @Controller
+         */
+        String CONTROLLER_ANNOTATION_CLASS_NAME = "org.springframework.stereotype.Controller";
+
+        /**
+         * The annotation class name of @RequestMapping
+         */
+        String REQUEST_MAPPING_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestMapping";
+
+        /**
+         * The annotation class name of @RequestHeader
+         */
+        String REQUEST_HEADER_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestHeader";
+
+        /**
+         * The annotation class name of @RequestParam
+         */
+        String REQUEST_PARAM_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestParam";
+
+        /**
+         * The annotation class name of @RequestBody
+         */
+        String REQUEST_BODY_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.RequestBody";
+
+        /**
+         * The annotation class name of @PathVariable
+         */
+        String PATH_VARIABLE_ANNOTATION_CLASS_NAME = "org.springframework.web.bind.annotation.PathVariable";
+
+        /**
+         * The class of @Controller
+         *
+         * @since 2.7.9
+         */
+        Class<? extends Annotation> CONTROLLER_ANNOTATION_CLASS =
+                (Class<? extends Annotation>) resolveClass(CONTROLLER_ANNOTATION_CLASS_NAME, getClassLoader());
+
+        /**
+         * The class of @RequestMapping
+         *
+         * @since 2.7.9
+         */
+        Class<? extends Annotation> REQUEST_MAPPING_ANNOTATION_CLASS =
+                (Class<? extends Annotation>) resolveClass(REQUEST_MAPPING_ANNOTATION_CLASS_NAME, getClassLoader());
+
+        /**
+         * The annotation class name of AnnotatedElementUtils
+         *
+         * @since 2.7.9
+         */
+        String ANNOTATED_ELEMENT_UTILS_CLASS_NAME = "org.springframework.core.annotation.AnnotatedElementUtils";
+
+        /**
+         * The class of AnnotatedElementUtils
+         *
+         * @since 2.7.9
+         */
+        Class<?> ANNOTATED_ELEMENT_UTILS_CLASS = resolveClass(ANNOTATED_ELEMENT_UTILS_CLASS_NAME, getClassLoader());
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/RestMethodMetadata.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/RestMethodMetadata.java
new file mode 100644
index 0000000..0466ee5
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/RestMethodMetadata.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.metadata.definition.model.MethodDefinition;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static java.util.Collections.emptyList;
+
+/**
+ * The metadata class for {@link RequestMetadata HTTP(REST) request} and
+ * its binding {@link MethodDefinition method definition}
+ *
+ * @since 2.7.6
+ */
+public class RestMethodMetadata implements Serializable {
+
+    private static final long serialVersionUID = 2935252016200830694L;
+
+    private MethodDefinition method;
+
+    private RequestMetadata request;
+
+    private Integer urlIndex;
+
+    private Integer bodyIndex;
+
+    private Integer headerMapIndex;
+
+    private String bodyType;
+
+    private Map<Integer, Collection<String>> indexToName;
+
+    private List<String> formParams;
+
+    private Map<Integer, Boolean> indexToEncoded;
+
+    private List<ArgInfo> argInfos;
+
+    private Method reflectMethod;
+
+    /**
+     *  make a distinction between mvc & resteasy
+     */
+    private Class codeStyle;
+
+    public MethodDefinition getMethod() {
+        if (method == null) {
+            method = new MethodDefinition();
+        }
+        return method;
+    }
+
+    public void setMethod(MethodDefinition method) {
+        this.method = method;
+    }
+
+    public RequestMetadata getRequest() {
+        if (request == null) {
+            request = new RequestMetadata();
+        }
+        return request;
+    }
+
+    public void setRequest(RequestMetadata request) {
+        this.request = request;
+    }
+
+    public Integer getUrlIndex() {
+        return urlIndex;
+    }
+
+    public void setUrlIndex(Integer urlIndex) {
+        this.urlIndex = urlIndex;
+    }
+
+    public Integer getBodyIndex() {
+        return bodyIndex;
+    }
+
+    public void setBodyIndex(Integer bodyIndex) {
+        this.bodyIndex = bodyIndex;
+    }
+
+    public Integer getHeaderMapIndex() {
+        return headerMapIndex;
+    }
+
+    public void setHeaderMapIndex(Integer headerMapIndex) {
+        this.headerMapIndex = headerMapIndex;
+    }
+
+    public String getBodyType() {
+        return bodyType;
+    }
+
+    public void setBodyType(String bodyType) {
+        this.bodyType = bodyType;
+    }
+
+    public Map<Integer, Collection<String>> getIndexToName() {
+        if (indexToName == null) {
+            indexToName = new LinkedHashMap<>();
+        }
+        return indexToName;
+    }
+
+    public void setIndexToName(Map<Integer, Collection<String>> indexToName) {
+        this.indexToName = indexToName;
+    }
+
+    public void addIndexToName(Integer index, String name) {
+        if (index == null) {
+            return;
+        }
+
+        if (name.startsWith("arg") && name.endsWith(index.toString())) {
+            // Ignore this value because of the Java byte-code without the metadata of method parameters
+            return;
+        }
+
+        Map<Integer, Collection<String>> indexToName = getIndexToName();
+        Collection<String> parameterNames = indexToName.computeIfAbsent(index, i -> new ArrayList<>(1));
+        parameterNames.add(name);
+    }
+
+    public boolean hasIndexedName(Integer index, String name) {
+        Map<Integer, Collection<String>> indexToName = getIndexToName();
+        return indexToName.getOrDefault(index, emptyList()).contains(name);
+    }
+
+    public List<String> getFormParams() {
+        return formParams;
+    }
+
+    public void setFormParams(List<String> formParams) {
+        this.formParams = formParams;
+    }
+
+    public Map<Integer, Boolean> getIndexToEncoded() {
+        return indexToEncoded;
+    }
+
+    public void setIndexToEncoded(Map<Integer, Boolean> indexToEncoded) {
+        this.indexToEncoded = indexToEncoded;
+    }
+
+    public List<ArgInfo> getArgInfos() {
+        if (argInfos == null) {
+            argInfos = new ArrayList<>();
+        }
+        return argInfos;
+    }
+
+    public void addArgInfo(ArgInfo argInfo) {
+        getArgInfos().add(argInfo);
+    }
+
+    public Method getReflectMethod() {
+        return reflectMethod;
+    }
+
+    public void setReflectMethod(Method reflectMethod) {
+        this.reflectMethod = reflectMethod;
+    }
+
+    public Class getCodeStyle() {
+        return codeStyle;
+    }
+
+    public void setCodeStyle(Class codeStyle) {
+        this.codeStyle = codeStyle;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof RestMethodMetadata)) {
+            return false;
+        }
+        RestMethodMetadata that = (RestMethodMetadata) o;
+        return Objects.equals(getMethod(), that.getMethod())
+                && Objects.equals(getRequest(), that.getRequest())
+                && Objects.equals(getUrlIndex(), that.getUrlIndex())
+                && Objects.equals(getBodyIndex(), that.getBodyIndex())
+                && Objects.equals(getHeaderMapIndex(), that.getHeaderMapIndex())
+                && Objects.equals(getBodyType(), that.getBodyType())
+                && Objects.equals(getFormParams(), that.getFormParams())
+                && Objects.equals(getIndexToEncoded(), that.getIndexToEncoded());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                getMethod(),
+                getRequest(),
+                getUrlIndex(),
+                getBodyIndex(),
+                getHeaderMapIndex(),
+                getBodyType(),
+                getFormParams(),
+                getIndexToEncoded());
+    }
+
+    @Override
+    public String toString() {
+        return "RestMethodMetadata{" + "method="
+                + method + ", request="
+                + request + ", urlIndex="
+                + urlIndex + ", bodyIndex="
+                + bodyIndex + ", headerMapIndex="
+                + headerMapIndex + ", bodyType='"
+                + bodyType + '\'' + ", indexToName="
+                + indexToName + ", formParams="
+                + formParams + ", indexToEncoded="
+                + indexToEncoded + ", argInfos="
+                + argInfos + ", reflectMethod="
+                + reflectMethod + ", codeStyle="
+                + codeStyle + '}';
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ServiceRestMetadata.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ServiceRestMetadata.java
new file mode 100644
index 0000000..59d2840
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ServiceRestMetadata.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.common.utils.PathUtils;
+import org.apache.dubbo.metadata.ParameterTypesComparator;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * The metadata class for {@link RequestMetadata HTTP(REST) request} and
+ * its binding Dubbo service metadata
+ *
+ * @since 2.7.6
+ */
+public class ServiceRestMetadata implements Serializable {
+
+    private static final long serialVersionUID = -4549723140727443569L;
+
+    private String serviceInterface;
+
+    private String version;
+
+    private String group;
+
+    private Set<RestMethodMetadata> meta;
+
+    private Integer port;
+
+    private boolean consumer;
+
+    private String contextPathFromUrl;
+
+    /**
+     * make a distinction between mvc & resteasy
+     */
+    private Class codeStyle;
+
+    private Map<PathMatcher, RestMethodMetadata> pathToServiceMapContainPathVariable = new HashMap<>();
+    private Map<PathMatcher, RestMethodMetadata> pathToServiceMapUnContainPathVariable = new HashMap<>();
+    private Map<String, Map<ParameterTypesComparator, RestMethodMetadata>> methodToServiceMap = new HashMap<>();
+
+    public ServiceRestMetadata(String serviceInterface, String version, String group, boolean consumer) {
+        this.serviceInterface = serviceInterface;
+        this.version = version;
+        this.group = group;
+        this.consumer = consumer;
+    }
+
+    public ServiceRestMetadata() {}
+
+    public ServiceRestMetadata(String serviceInterface, String version, String group) {
+        this(serviceInterface, version, group, false);
+    }
+
+    public String getServiceInterface() {
+        return serviceInterface;
+    }
+
+    public void setServiceInterface(String serviceInterface) {
+        this.serviceInterface = serviceInterface;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public void setGroup(String group) {
+        this.group = group;
+    }
+
+    public Set<RestMethodMetadata> getMeta() {
+        if (meta == null) {
+            meta = new LinkedHashSet<>();
+        }
+        return meta;
+    }
+
+    public void setMeta(Set<RestMethodMetadata> meta) {
+        this.meta = meta;
+    }
+
+    public void addRestMethodMetadata(RestMethodMetadata restMethodMetadata) {
+        PathMatcher pathMather = new PathMatcher(
+                restMethodMetadata.getRequest().getPath(),
+                this.getVersion(),
+                this.getGroup(),
+                this.getPort(),
+                restMethodMetadata.getRequest().getMethod());
+        pathMather.setMethod(restMethodMetadata.getReflectMethod());
+        addPathToServiceMap(pathMather, restMethodMetadata);
+        addMethodToServiceMap(restMethodMetadata);
+        getMeta().add(restMethodMetadata);
+    }
+
+    public Map<PathMatcher, RestMethodMetadata> getPathContainPathVariableToServiceMap() {
+        return pathToServiceMapContainPathVariable;
+    }
+
+    public Map<PathMatcher, RestMethodMetadata> getPathUnContainPathVariableToServiceMap() {
+        return pathToServiceMapUnContainPathVariable;
+    }
+
+    public void addPathToServiceMap(PathMatcher pathMather, RestMethodMetadata restMethodMetadata) {
+
+        if (pathMather.hasPathVariable()) {
+            doublePathCheck(pathToServiceMapContainPathVariable, pathMather, restMethodMetadata, true);
+        } else {
+            doublePathCheck(pathToServiceMapUnContainPathVariable, pathMather, restMethodMetadata, false);
+        }
+    }
+
+    private void doublePathCheck(
+            Map<PathMatcher, RestMethodMetadata> pathMatcherRestMethodMetadataMap,
+            PathMatcher pathMather,
+            RestMethodMetadata restMethodMetadata,
+            boolean containPathVariable) {
+        if (pathMatcherRestMethodMetadataMap.containsKey(pathMather)) {
+            if (containPathVariable) {
+                throw new IllegalArgumentException(
+                        "dubbo rest metadata resolve double path error,and contain path variable  is:  " + pathMather
+                                + ", rest method metadata is: " + restMethodMetadata);
+
+            } else {
+                throw new IllegalArgumentException(
+                        "dubbo rest metadata resolve double path error,and do not  contain path variable  is: "
+                                + pathMather + ", rest method metadata is: " + restMethodMetadata);
+            }
+        }
+
+        pathMatcherRestMethodMetadataMap.put(pathMather, restMethodMetadata);
+    }
+
+    public Integer getPort() {
+        return port;
+    }
+
+    public void setPort(Integer port) {
+        this.port = port;
+        setPort(port, getPathContainPathVariableToServiceMap());
+        setPort(port, getPathUnContainPathVariableToServiceMap());
+    }
+
+    private void setPort(Integer port, Map<PathMatcher, RestMethodMetadata> pathToServiceMapContainPathVariable) {
+        for (PathMatcher pathMather : pathToServiceMapContainPathVariable.keySet()) {
+            pathMather.setPort(port);
+        }
+    }
+
+    public boolean isConsumer() {
+        return consumer;
+    }
+
+    public void setConsumer(boolean consumer) {
+        this.consumer = consumer;
+    }
+
+    public Map<String, Map<ParameterTypesComparator, RestMethodMetadata>> getMethodToServiceMap() {
+        return methodToServiceMap;
+    }
+
+    public void addMethodToServiceMap(RestMethodMetadata restMethodMetadata) {
+        if (this.methodToServiceMap == null) {
+            this.methodToServiceMap = new HashMap<>();
+        }
+
+        this.methodToServiceMap
+                .computeIfAbsent(restMethodMetadata.getReflectMethod().getName(), k -> new HashMap<>())
+                .put(
+                        ParameterTypesComparator.getInstance(
+                                restMethodMetadata.getReflectMethod().getParameterTypes()),
+                        restMethodMetadata);
+    }
+
+    public Class getCodeStyle() {
+        return codeStyle;
+    }
+
+    public void setCodeStyle(Class codeStyle) {
+        this.codeStyle = codeStyle;
+    }
+
+    public String getContextPathFromUrl() {
+        return contextPathFromUrl == null ? "" : contextPathFromUrl;
+    }
+
+    public void setContextPathFromUrl(String contextPathFromUrl) {
+        this.contextPathFromUrl = PathUtils.normalize(contextPathFromUrl);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ServiceRestMetadata)) {
+            return false;
+        }
+        ServiceRestMetadata that = (ServiceRestMetadata) o;
+        return Objects.equals(getServiceInterface(), that.getServiceInterface())
+                && Objects.equals(getVersion(), that.getVersion())
+                && Objects.equals(getGroup(), that.getGroup())
+                && Objects.equals(getMeta(), that.getMeta())
+                && Objects.equals(getPort(), that.getPort());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getServiceInterface(), getVersion(), getGroup(), getMeta(), getPort());
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("ServiceRestMetadata{");
+        sb.append("serviceInterface='").append(serviceInterface).append('\'');
+        sb.append(", version='").append(version).append('\'');
+        sb.append(", group='").append(group).append('\'');
+        sb.append(", meta=").append(meta);
+        sb.append(", port=").append(port);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ServiceRestMetadataReader.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ServiceRestMetadataReader.java
new file mode 100644
index 0000000..361fe12
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ServiceRestMetadataReader.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.common.extension.SPI;
+
+import java.util.List;
+
+/**
+ * An interface to read {@link ServiceRestMetadata}
+ *
+ * @see ServiceRestMetadata
+ * @since 2.7.6
+ */
+@SPI
+public interface ServiceRestMetadataReader {
+
+    /**
+     * Read the instances of {@link ServiceRestMetadata}
+     *
+     * @return non-null
+     */
+    List<ServiceRestMetadata> read();
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ServiceRestMetadataResolver.java
new file mode 100644
index 0000000..54baec0
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/ServiceRestMetadataResolver.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+
+/**
+ * The interface to resolve the {@link ServiceRestMetadata REST metadata} from the specified
+ * Dubbo Service interface or type.
+ *
+ * @since 2.7.6
+ */
+@SPI(scope = ExtensionScope.APPLICATION)
+public interface ServiceRestMetadataResolver {
+
+    /**
+     * Support to resolve {@link ServiceRestMetadata REST metadata} or not
+     *
+     * @param serviceType Dubbo Service interface or type
+     * @return If supports, return <code>true</code>, or <code>false</code>
+     */
+    boolean supports(Class<?> serviceType);
+
+    boolean supports(Class<?> serviceType, boolean consumer);
+
+    /**
+     * Resolve the {@link ServiceRestMetadata REST metadata} from the specified
+     * Dubbo Service interface or type
+     *
+     * @param serviceType Dubbo Service interface or type
+     * @return
+     */
+    ServiceRestMetadata resolve(Class<?> serviceType);
+
+    ServiceRestMetadata resolve(Class<?> serviceType, ServiceRestMetadata serviceRestMetadata);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/SpringMvcClassConstants.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/SpringMvcClassConstants.java
new file mode 100644
index 0000000..33c7fa0
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/SpringMvcClassConstants.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import static org.apache.dubbo.common.utils.ClassUtils.getClassLoader;
+import static org.apache.dubbo.common.utils.ClassUtils.resolveClass;
+
+public interface SpringMvcClassConstants extends RestMetadataConstants.SPRING_MVC {
+    /**
+     * The annotation class of @RequestMapping
+     */
+    Class REQUEST_MAPPING_ANNOTATION_CLASS = resolveClass(REQUEST_MAPPING_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @RequestHeader
+     */
+    Class REQUEST_HEADER_ANNOTATION_CLASS = resolveClass(REQUEST_HEADER_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @RequestParam
+     */
+    Class REQUEST_PARAM_ANNOTATION_CLASS = resolveClass(REQUEST_PARAM_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @RequestBody
+     */
+    Class REQUEST_BODY_ANNOTATION_CLASS = resolveClass(REQUEST_BODY_ANNOTATION_CLASS_NAME, getClassLoader());
+
+    /**
+     * The annotation class of @RequestBody
+     */
+    Class PATH_VARIABLE_ANNOTATION_CLASS = resolveClass(PATH_VARIABLE_ANNOTATION_CLASS_NAME, getClassLoader());
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/BodyParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/BodyParameterProcessor.java
new file mode 100644
index 0000000..e3a1c39
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/BodyParameterProcessor.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.dubbo.metadata.extension.rest.api.jaxrs;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.REST_EASY_BODY_ANNOTATION_CLASS_NAME;
+
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @FormParam
+ *
+ * @since 2.7.6
+ */
+public class BodyParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return REST_EASY_BODY_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    public void process(
+            Annotation annotation,
+            Parameter parameter,
+            int parameterIndex,
+            Method method,
+            Class<?> serviceType,
+            Class<?> serviceInterfaceClass,
+            RestMethodMetadata restMethodMetadata) {
+        ArgInfo argInfo = ArgInfo.build(parameterIndex, parameter).setParamAnnotationType(getAnnotationClass());
+        restMethodMetadata.addArgInfo(argInfo);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/DefaultValueParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/DefaultValueParameterProcessor.java
new file mode 100644
index 0000000..84ac1dd
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/DefaultValueParameterProcessor.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.jaxrs;
+
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RequestMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @DefaultValue
+ * *
+ *
+ * @since 2.7.6
+ */
+public class DefaultValueParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return RestMetadataConstants.JAX_RS.DEFAULT_VALUE_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected void process(
+            String annotationValue,
+            String defaultValue,
+            Annotation annotation,
+            Parameter parameter,
+            int parameterIndex,
+            Method method,
+            RestMethodMetadata restMethodMetadata) {
+        RequestMetadata requestMetadata = restMethodMetadata.getRequest();
+
+        // process the request parameters
+        setDefaultValue(requestMetadata.getParams(), defaultValue, annotationValue);
+        // process the request headers
+        setDefaultValue(requestMetadata.getHeaders(), defaultValue, annotationValue);
+    }
+
+    private void setDefaultValue(Map<String, List<String>> source, String placeholderValue, String defaultValue) {
+        OUTTER:
+        for (Map.Entry<String, List<String>> entry : source.entrySet()) {
+            List<String> values = entry.getValue();
+            int size = values.size();
+            for (int i = 0; i < size; i++) {
+                String value = values.get(i);
+                if (placeholderValue.equals(value)) {
+                    values.set(i, defaultValue);
+                    break OUTTER;
+                }
+            }
+        }
+    }
+
+    @Override
+    public int getPriority() {
+        return MIN_PRIORITY;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/FormBodyParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/FormBodyParameterProcessor.java
new file mode 100644
index 0000000..bc771ab
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/FormBodyParameterProcessor.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.REST_EASY_FORM_BODY_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.extension.rest.api.media.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @FormParam
+ *
+ * @since 2.7.6
+ */
+public class FormBodyParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return REST_EASY_FORM_BODY_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    public void process(
+            Annotation annotation,
+            Parameter parameter,
+            int parameterIndex,
+            Method method,
+            Class<?> serviceType,
+            Class<?> serviceInterfaceClass,
+            RestMethodMetadata restMethodMetadata) {
+        super.process(
+                annotation, parameter, parameterIndex, method, serviceType, serviceInterfaceClass, restMethodMetadata);
+        restMethodMetadata.getRequest().getConsumes().add(APPLICATION_FORM_URLENCODED_VALUE.value);
+    }
+
+    @Override
+    protected String getAnnotationValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+        return null;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/FormParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/FormParamParameterProcessor.java
new file mode 100644
index 0000000..ad75455
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/FormParamParameterProcessor.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.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.FORM_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @FormParam
+ *
+ * @since 2.7.6
+ */
+public class FormParamParameterProcessor extends ParamAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return FORM_PARAM_ANNOTATION_CLASS_NAME;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/HeaderParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/HeaderParamParameterProcessor.java
new file mode 100644
index 0000000..65d6d2c
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/HeaderParamParameterProcessor.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.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RequestMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+import static org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor.buildDefaultValue;
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.HEADER_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @HeaderParam
+ *
+ * @since 2.7.6
+ */
+public class HeaderParamParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return HEADER_PARAM_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected void process(
+            String headerName,
+            String defaultValue,
+            Annotation annotation,
+            Parameter parameter,
+            int parameterIndex,
+            Method method,
+            RestMethodMetadata restMethodMetadata) {
+        RequestMetadata requestMetadata = restMethodMetadata.getRequest();
+        // Add the placeholder as header value
+        requestMetadata.addHeader(headerName, buildDefaultValue(parameterIndex));
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/JAXRSServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/JAXRSServiceRestMetadataResolver.java
new file mode 100644
index 0000000..61a45a1
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/JAXRSServiceRestMetadataResolver.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadataResolver;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Set;
+import java.util.stream.Stream;
+
+
+import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.findMetaAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getValue;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent;
+import static org.apache.dubbo.common.utils.PathUtils.buildPath;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.CONSUMES_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.HTTP_METHOD_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.PATH_ANNOTATION_CLASS_NAME;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.PRODUCES_ANNOTATION_CLASS_NAME;
+
+/**
+ * JAX-RS {@link ServiceRestMetadataResolver} implementation
+ *
+ * @since 2.7.6
+ */
+public class JAXRSServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
+    public JAXRSServiceRestMetadataResolver(ApplicationModel applicationModel) {
+        super(applicationModel);
+    }
+
+    @Override
+    protected boolean supports0(Class<?> serviceType) {
+        return isAnnotationPresent(serviceType, PATH_ANNOTATION_CLASS_NAME)
+                // method @Path
+                || isServiceMethodAnnotationPresent(serviceType, PATH_ANNOTATION_CLASS_NAME);
+    }
+
+    @Override
+    protected boolean isRestCapableMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        return isAnnotationPresent(serviceMethod, HTTP_METHOD_ANNOTATION_CLASS_NAME);
+    }
+
+    @Override
+    protected String resolveRequestMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        Annotation httpMethod = findMetaAnnotation(serviceMethod, HTTP_METHOD_ANNOTATION_CLASS_NAME);
+        return getValue(httpMethod);
+    }
+
+    @Override
+    protected String resolveRequestPath(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        String requestBasePath = resolveRequestPathFromType(serviceType, serviceInterfaceClass);
+        String requestRelativePath = resolveRequestPathFromMethod(serviceMethod);
+        return buildPath(requestBasePath, requestRelativePath);
+    }
+
+    private String resolveRequestPathFromType(Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        Annotation path = findAnnotation(serviceType, PATH_ANNOTATION_CLASS_NAME);
+        if (path == null) {
+            path = findAnnotation(serviceInterfaceClass, PATH_ANNOTATION_CLASS_NAME);
+        }
+        return getValue(path);
+    }
+
+    private String resolveRequestPathFromMethod(Method serviceMethod) {
+        Annotation path = findAnnotation(serviceMethod, PATH_ANNOTATION_CLASS_NAME);
+        return getValue(path);
+    }
+
+    @Override
+    protected void processProduces(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> produces) {
+        addAnnotationValues(serviceMethod, PRODUCES_ANNOTATION_CLASS_NAME, produces);
+        addAnnotationValues(serviceType, PRODUCES_ANNOTATION_CLASS_NAME, produces);
+        addAnnotationValues(serviceInterfaceClass, PRODUCES_ANNOTATION_CLASS_NAME, produces);
+    }
+
+    @Override
+    protected void processConsumes(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> consumes) {
+        addAnnotationValues(serviceMethod, CONSUMES_ANNOTATION_CLASS_NAME, consumes);
+        addAnnotationValues(serviceType, CONSUMES_ANNOTATION_CLASS_NAME, consumes);
+        addAnnotationValues(serviceInterfaceClass, CONSUMES_ANNOTATION_CLASS_NAME, consumes);
+    }
+
+    private void addAnnotationValues(Method serviceMethod, String annotationAttributeName, Set<String> result) {
+        Annotation annotation = findAnnotation(serviceMethod, annotationAttributeName);
+        String[] value = getValue(annotation);
+        if (value != null) {
+            Stream.of(value).forEach(result::add);
+        }
+    }
+
+    private void addAnnotationValues(Class serviceType, String annotationAttributeName, Set<String> result) {
+        Annotation annotation = findAnnotation(serviceType, annotationAttributeName);
+        String[] value = getValue(annotation);
+        if (value != null) {
+            Stream.of(value).forEach(result::add);
+        }
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/MatrixParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/MatrixParamParameterProcessor.java
new file mode 100644
index 0000000..75fe1ca
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/MatrixParamParameterProcessor.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.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.MATRIX_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @MatrixParam
+ *
+ * @since 2.7.6
+ */
+public class MatrixParamParameterProcessor extends ParamAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return MATRIX_PARAM_ANNOTATION_CLASS_NAME;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/ParamAnnotationParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/ParamAnnotationParameterProcessor.java
new file mode 100644
index 0000000..3f74685
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/ParamAnnotationParameterProcessor.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RequestMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+/**
+ * The abstract {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @*Param
+ */
+public abstract class ParamAnnotationParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    protected void process(
+            String name,
+            String defaultValue,
+            Annotation annotation,
+            Parameter parameter,
+            int parameterIndex,
+            Method method,
+            RestMethodMetadata restMethodMetadata) {
+        RequestMetadata requestMetadata = restMethodMetadata.getRequest();
+        requestMetadata.addParam(name, defaultValue);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/PathParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/PathParamParameterProcessor.java
new file mode 100644
index 0000000..4209332
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/PathParamParameterProcessor.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.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.PATH_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @PathParam
+ *
+ * @since 2.7.6
+ */
+public class PathParamParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return PATH_PARAM_ANNOTATION_CLASS_NAME;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/QueryParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/QueryParamParameterProcessor.java
new file mode 100644
index 0000000..81b9f1f
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/QueryParamParameterProcessor.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.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.JAX_RS.QUERY_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for JAX-RS's @QueryParam
+ *
+ * @since 2.7.6
+ */
+public class QueryParamParameterProcessor extends ParamAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return QUERY_PARAM_ANNOTATION_CLASS_NAME;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/media/MediaType.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/media/MediaType.java
new file mode 100644
index 0000000..0cc0589
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/media/MediaType.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.media;
+
+import java.util.Arrays;
+import java.util.List;
+
+public enum MediaType {
+    ALL_VALUE("*/*"),
+    APPLICATION_JSON_VALUE("application/json"),
+    APPLICATION_FORM_URLENCODED_VALUE("application/x-www-form-urlencoded"),
+    TEXT_PLAIN("text/plain"),
+    TEXT_XML("text/xml"),
+    OCTET_STREAM("application/octet-stream"),
+    ;
+
+    MediaType(String value) {
+        this.value = value;
+    }
+
+    public String value;
+
+    public static String getAllContentType() {
+
+        MediaType[] values = MediaType.values();
+
+        StringBuilder stringBuilder = new StringBuilder();
+
+        for (MediaType mediaType : values) {
+            stringBuilder.append(mediaType.value + " ");
+        }
+        return stringBuilder.toString();
+    }
+
+    public static List<MediaType> getSupportMediaTypes() {
+        return Arrays.asList(
+                APPLICATION_JSON_VALUE, APPLICATION_FORM_URLENCODED_VALUE, TEXT_PLAIN, TEXT_XML, OCTET_STREAM);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/AbstractRequestAnnotationParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/AbstractRequestAnnotationParameterProcessor.java
new file mode 100644
index 0000000..551149e
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/AbstractRequestAnnotationParameterProcessor.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.springmvc;
+
+import org.apache.dubbo.common.utils.AnnotationUtils;
+import org.apache.dubbo.metadata.extension.rest.api.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Parameter;
+import java.util.Objects;
+
+import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute;
+
+/**
+ * The abstract {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @Request*
+ */
+public abstract class AbstractRequestAnnotationParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    protected String getAnnotationValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+        // try to get "value" attribute first
+        String name = super.getAnnotationValue(annotation, parameter, parameterIndex);
+
+        // try to get "name" attribute if required
+        if (isEmpty(name)) {
+            name = getAttribute(annotation, "name");
+        }
+
+        // finally , try to the name of parameter
+        if (isEmpty(name)) {
+            name = parameter.getName();
+        }
+
+        return name;
+    }
+
+    @Override
+    protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+        String attributeName = "defaultValue";
+        String attributeValue = getAttribute(annotation, attributeName);
+
+        if (isEmpty(attributeValue) || isDefaultValue(annotation, attributeName, attributeValue)) {
+            attributeValue = super.getDefaultValue(annotation, parameter, parameterIndex);
+        }
+        return attributeValue;
+    }
+
+    private boolean isDefaultValue(Annotation annotation, String attributeName, Object attributeValue) {
+        String defaultValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
+        return Objects.deepEquals(attributeValue, defaultValue);
+    }
+
+    protected boolean isEmpty(String str) {
+        return str == null || str.isEmpty();
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/FormBodyNoAnnotatedProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/FormBodyNoAnnotatedProcessor.java
new file mode 100644
index 0000000..cf7802d
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/FormBodyNoAnnotatedProcessor.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.metadata.extension.rest.api.tag.BodyTag;
+import org.apache.dubbo.metadata.extension.rest.api.AbstractNoAnnotatedParameterProcessor;
+
+public class FormBodyNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProcessor {
+    @Override
+    public MediaType consumerContentType() {
+        return MediaType.APPLICATION_FORM_URLENCODED_VALUE;
+    }
+
+    @Override
+    public String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata) {
+        return BodyTag.class.getName();
+    }
+
+    @Override
+    protected boolean isFormContentType(RestMethodMetadata restMethodMetadata) {
+        return true;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/JsonBodyNoAnnotatedProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/JsonBodyNoAnnotatedProcessor.java
new file mode 100644
index 0000000..9296ea3
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/JsonBodyNoAnnotatedProcessor.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.dubbo.metadata.extension.rest.api.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractNoAnnotatedParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.metadata.extension.rest.api.tag.BodyTag;
+
+public class JsonBodyNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProcessor {
+    @Override
+    public MediaType consumerContentType() {
+        return MediaType.APPLICATION_JSON_VALUE;
+    }
+
+    @Override
+    public String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata) {
+        return BodyTag.class.getName();
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/ParamNoAnnotatedProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/ParamNoAnnotatedProcessor.java
new file mode 100644
index 0000000..ad85a11
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/ParamNoAnnotatedProcessor.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 org.apache.dubbo.metadata.extension.rest.api.springmvc;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractNoAnnotatedParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.metadata.extension.rest.api.tag.BodyTag;
+import org.apache.dubbo.metadata.extension.rest.api.tag.ParamTag;
+
+public class ParamNoAnnotatedProcessor extends AbstractNoAnnotatedParameterProcessor {
+    @Override
+    public MediaType consumerContentType() {
+        return MediaType.ALL_VALUE;
+    }
+
+    @Override
+    public String defaultAnnotationClassName(RestMethodMetadata restMethodMetadata) {
+
+        if (JAXRSServiceRestMetadataResolver.class.equals(restMethodMetadata.getCodeStyle())) {
+            return BodyTag.class.getName();
+        }
+
+        return ParamTag.class.getName();
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/PathVariableParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/PathVariableParameterProcessor.java
new file mode 100644
index 0000000..96c39a8
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/PathVariableParameterProcessor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.springmvc;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Parameter;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SPRING_MVC.PATH_VARIABLE_ANNOTATION_CLASS_NAME;
+
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @PathVariable
+ */
+public class PathVariableParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return PATH_VARIABLE_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+        return null;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/RequestBodyParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/RequestBodyParameterProcessor.java
new file mode 100644
index 0000000..44bd739
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/RequestBodyParameterProcessor.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 org.apache.dubbo.metadata.extension.rest.api.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractAnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Parameter;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SPRING_MVC.REQUEST_BODY_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @RequestBody
+ */
+public class RequestBodyParameterProcessor extends AbstractAnnotatedMethodParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return REQUEST_BODY_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected String getAnnotationValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+        return null;
+    }
+
+    @Override
+    protected String getDefaultValue(Annotation annotation, Parameter parameter, int parameterIndex) {
+        return null;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/RequestHeaderParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/RequestHeaderParameterProcessor.java
new file mode 100644
index 0000000..00fc2fc
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/RequestHeaderParameterProcessor.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+import org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @RequestHeader
+ */
+public class RequestHeaderParameterProcessor extends AbstractRequestAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return RestMetadataConstants.SPRING_MVC.REQUEST_HEADER_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected void process(
+            String name,
+            String defaultValue,
+            Annotation annotation,
+            Parameter parameter,
+            int parameterIndex,
+            Method method,
+            RestMethodMetadata restMethodMetadata) {
+        restMethodMetadata.getRequest().addHeader(name, defaultValue);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/RequestParamParameterProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/RequestParamParameterProcessor.java
new file mode 100644
index 0000000..6569172
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/RequestParamParameterProcessor.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+import static org.apache.dubbo.metadata.extension.rest.api.RestMetadataConstants.SPRING_MVC.REQUEST_PARAM_ANNOTATION_CLASS_NAME;
+
+/**
+ * The {@link AnnotatedMethodParameterProcessor} implementation for Spring Web MVC's @RequestParam
+ */
+public class RequestParamParameterProcessor extends AbstractRequestAnnotationParameterProcessor {
+
+    @Override
+    public String getAnnotationName() {
+        return REQUEST_PARAM_ANNOTATION_CLASS_NAME;
+    }
+
+    @Override
+    protected void process(
+            String name,
+            String defaultValue,
+            Annotation annotation,
+            Parameter parameter,
+            int parameterIndex,
+            Method method,
+            RestMethodMetadata restMethodMetadata) {
+        restMethodMetadata.getRequest().addParam(name, defaultValue);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/SpringMvcServiceRestMetadataResolver.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/SpringMvcServiceRestMetadataResolver.java
new file mode 100644
index 0000000..436f095
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/SpringMvcServiceRestMetadataResolver.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.springmvc;
+
+import org.apache.dubbo.metadata.extension.rest.api.AbstractServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadataResolver;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import static java.lang.String.valueOf;
+import static java.lang.reflect.Array.getLength;
+import static java.util.stream.Stream.of;
+import static org.apache.dubbo.common.utils.AnnotationUtils.findAnnotation;
+import static org.apache.dubbo.common.utils.AnnotationUtils.getAttribute;
+import static org.apache.dubbo.common.utils.AnnotationUtils.isAnnotationPresent;
+import static org.apache.dubbo.common.utils.ArrayUtils.isEmpty;
+import static org.apache.dubbo.common.utils.ArrayUtils.isNotEmpty;
+import static org.apache.dubbo.common.utils.MethodUtils.findMethod;
+import static org.apache.dubbo.common.utils.PathUtils.buildPath;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.ANNOTATED_ELEMENT_UTILS_CLASS;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.CONTROLLER_ANNOTATION_CLASS;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_MAPPING_ANNOTATION_CLASS;
+import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.REQUEST_MAPPING_ANNOTATION_CLASS_NAME;
+
+/**
+ * {@link ServiceRestMetadataResolver}
+ *
+ * @since 2.7.6
+ */
+public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
+
+    private static final int FIRST_ELEMENT_INDEX = 0;
+
+    public SpringMvcServiceRestMetadataResolver(ApplicationModel applicationModel) {
+        super(applicationModel);
+    }
+
+    @Override
+    protected boolean supports0(Class<?> serviceType) {
+        // class @Controller or @RequestMapping
+        return isAnnotationPresent(serviceType, CONTROLLER_ANNOTATION_CLASS)
+                || isAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS)
+                // method @RequestMapping
+                || isServiceMethodAnnotationPresent(serviceType, REQUEST_MAPPING_ANNOTATION_CLASS_NAME);
+    }
+
+    @Override
+    protected boolean isRestCapableMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        // method only match @RequestMapping
+        return isAnnotationPresent(serviceMethod, REQUEST_MAPPING_ANNOTATION_CLASS);
+    }
+
+    @Override
+    protected String resolveRequestMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        Annotation requestMapping = getRequestMapping(serviceMethod);
+
+        // httpMethod is an array of RequestMethod
+        Object httpMethod = getAttribute(requestMapping, "method");
+
+        if (httpMethod == null || getLength(httpMethod) < 1) {
+            return null;
+        }
+
+        // TODO Is is required to support more request methods?
+        return valueOf(Array.get(httpMethod, FIRST_ELEMENT_INDEX));
+    }
+
+    @Override
+    protected String resolveRequestPath(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
+        String requestBasePath = resolveRequestPath(serviceType);
+        String requestRelativePath = resolveRequestPath(serviceMethod);
+        return buildPath(requestBasePath, requestRelativePath);
+    }
+
+    @Override
+    protected void processProduces(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> produces) {
+        addMediaTypes(serviceMethod, "produces", produces);
+        addMediaTypes(serviceType, "produces", produces);
+        addMediaTypes(serviceInterfaceClass, "produces", produces);
+    }
+
+    @Override
+    protected void processConsumes(
+            Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> consumes) {
+        addMediaTypes(serviceMethod, "consumes", consumes);
+        addMediaTypes(serviceType, "consumes", consumes);
+        addMediaTypes(serviceInterfaceClass, "consumes", consumes);
+    }
+
+    private String resolveRequestPath(AnnotatedElement annotatedElement) {
+        Annotation mappingAnnotation = getRequestMapping(annotatedElement);
+
+        // try "value" first
+        String[] value = getAttribute(mappingAnnotation, "value");
+
+        if (isEmpty(value)) { // try "path" later
+            value = getAttribute(mappingAnnotation, "path");
+        }
+
+        if (isEmpty(value)) {
+            return "";
+        }
+        // TODO Is is required to support more request paths?
+        return value[FIRST_ELEMENT_INDEX];
+    }
+
+    private void addMediaTypes(Method serviceMethod, String annotationAttributeName, Set<String> mediaTypesSet) {
+
+        Annotation mappingAnnotation = getRequestMapping(serviceMethod);
+
+        String[] mediaTypes = getAttribute(mappingAnnotation, annotationAttributeName);
+
+        if (isNotEmpty(mediaTypes)) {
+            of(mediaTypes).forEach(mediaTypesSet::add);
+        }
+    }
+
+    private void addMediaTypes(Class serviceType, String annotationAttributeName, Set<String> mediaTypesSet) {
+
+        Annotation mappingAnnotation = getRequestMapping(serviceType);
+
+        String[] mediaTypes = getAttribute(mappingAnnotation, annotationAttributeName);
+
+        if (isNotEmpty(mediaTypes)) {
+            of(mediaTypes).forEach(mediaTypesSet::add);
+        }
+    }
+
+    private Annotation getRequestMapping(AnnotatedElement annotatedElement) {
+        // try "@RequestMapping" first
+        Annotation requestMapping = findAnnotation(annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS);
+        if (requestMapping == null) {
+            // To try the meta-annotated annotation if can't be found.
+            // For example, if the annotation "@GetMapping" is used in the Spring Framework is 4.2 or above,
+            // because of "@GetMapping" alias for ("@AliasFor") "@RequestMapping" , both of them belongs to
+            // the artifact "spring-web" which depends on "spring-core", thus Spring core's
+            // AnnotatedElementUtils.findMergedAnnotation(AnnotatedElement, Class) must be involved.
+            Method method = findMethod(
+                    ANNOTATED_ELEMENT_UTILS_CLASS, "findMergedAnnotation", AnnotatedElement.class, Class.class);
+            if (method != null) {
+                try {
+                    requestMapping =
+                            (Annotation) method.invoke(null, annotatedElement, REQUEST_MAPPING_ANNOTATION_CLASS);
+                } catch (Exception ignored) {
+                }
+            }
+        }
+        return requestMapping;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/tag/BodyTag.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/tag/BodyTag.java
new file mode 100644
index 0000000..b11494e
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/tag/BodyTag.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.dubbo.metadata.extension.rest.api.tag;
+
+/**
+ *  for @RequestBody class no found
+ */
+public interface BodyTag {}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/tag/ParamTag.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/tag/ParamTag.java
new file mode 100644
index 0000000..e3b77a8
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/java/org/apache/dubbo/metadata/extension/rest/api/tag/ParamTag.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.dubbo.metadata.extension.rest.api.tag;
+
+/**
+ *  for @RequestParam or @QueryParam class no found
+ */
+public interface ParamTag {}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor
new file mode 100644
index 0000000..a3ae952
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.annotation.processing.AnnotatedMethodParameterProcessor
@@ -0,0 +1,10 @@
+# JAX-RS's implementations
+jax-rs.query-param = org.apache.dubbo.metadata.extension.rest.annotation.processing.jaxrs.QueryParamParameterProcessor
+jax-rs.form-param = org.apache.dubbo.metadata.extension.rest.annotation.processing.jaxrs.FormParamParameterProcessor
+jax-rs.matrix-param = org.apache.dubbo.metadata.extension.rest.annotation.processing.jaxrs.MatrixParamParameterProcessor
+jax-rs.header-param = org.apache.dubbo.metadata.extension.rest.annotation.processing.jaxrs.HeaderParamParameterProcessor
+jax-rs.default-value-param = org.apache.dubbo.metadata.extension.rest.annotation.processing.jaxrs.DefaultValueParameterProcessor
+
+# Spring Web MVC's implementations
+spring-webmvc.request-param = org.apache.dubbo.metadata.extension.rest.annotation.processing.springmvc.RequestParamParameterProcessor
+spring-webmvc.request-header = org.apache.dubbo.metadata.extension.rest.annotation.processing.springmvc.RequestHeaderParameterProcessor
\ No newline at end of file
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.annotation.processing.ServiceRestMetadataResolver b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.annotation.processing.ServiceRestMetadataResolver
new file mode 100644
index 0000000..06a2c0e
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.annotation.processing.ServiceRestMetadataResolver
@@ -0,0 +1,3 @@
+default = org.apache.dubbo.metadata.extension.rest.annotation.processing.DefaultServiceRestMetadataResolver
+jax-rs = org.apache.dubbo.metadata.extension.rest.annotation.processing.jaxrs.JAXRSServiceRestMetadataResolver
+spring-webmvc = org.apache.dubbo.metadata.extension.rest.annotation.processing.springmvc.SpringMvcServiceRestMetadataResolver
\ No newline at end of file
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor
new file mode 100644
index 0000000..6c88109
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.api.AnnotatedMethodParameterProcessor
@@ -0,0 +1,15 @@
+# JAX-RS's implementations
+jax-rs.query-param = org.apache.dubbo.metadata.extension.rest.api.jaxrs.QueryParamParameterProcessor
+jax-rs.form-param = org.apache.dubbo.metadata.extension.rest.api.jaxrs.FormParamParameterProcessor
+jax-rs.matrix-param = org.apache.dubbo.metadata.extension.rest.api.jaxrs.MatrixParamParameterProcessor
+jax-rs.header-param = org.apache.dubbo.metadata.extension.rest.api.jaxrs.HeaderParamParameterProcessor
+jax-rs.default-value-param = org.apache.dubbo.metadata.extension.rest.api.jaxrs.DefaultValueParameterProcessor
+jax-rs.body = org.apache.dubbo.metadata.extension.rest.api.jaxrs.BodyParameterProcessor
+jax-rs.form-body = org.apache.dubbo.metadata.extension.rest.api.jaxrs.FormBodyParameterProcessor
+jax-rs.path-param = org.apache.dubbo.metadata.extension.rest.api.jaxrs.PathParamParameterProcessor
+
+# Spring Web MVC's implementations
+spring-webmvc.request-param = org.apache.dubbo.metadata.extension.rest.api.springmvc.RequestParamParameterProcessor
+spring-webmvc.request-header = org.apache.dubbo.metadata.extension.rest.api.springmvc.RequestHeaderParameterProcessor
+spring-webmvc.request-body = org.apache.dubbo.metadata.extension.rest.api.springmvc.RequestBodyParameterProcessor
+spring-webmvc.request-path = org.apache.dubbo.metadata.extension.rest.api.springmvc.PathVariableParameterProcessor
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.api.NoAnnotatedParameterRequestTagProcessor b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.api.NoAnnotatedParameterRequestTagProcessor
new file mode 100644
index 0000000..8d7a846
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.api.NoAnnotatedParameterRequestTagProcessor
@@ -0,0 +1,3 @@
+body-form=org.apache.dubbo.metadata.extension.rest.api.springmvc.FormBodyNoAnnotatedProcessor
+body-json=org.apache.dubbo.metadata.extension.rest.api.springmvc.JsonBodyNoAnnotatedProcessor
+param=org.apache.dubbo.metadata.extension.rest.api.springmvc.ParamNoAnnotatedProcessor
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadataResolver b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadataResolver
new file mode 100644
index 0000000..245b40d
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadataResolver
@@ -0,0 +1,3 @@
+default =
+jax-rs = org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver
+spring-webmvc = org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver
\ No newline at end of file
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AnnotatedMethodParameterProcessorTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AnnotatedMethodParameterProcessorTest.java
new file mode 100644
index 0000000..24b2aab
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/AnnotatedMethodParameterProcessorTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.common.AbstractAnnotationProcessingTest;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * The abstract class for {@link AnnotatedMethodParameterProcessor}'s test cases
+ *
+ * @since 2.7.6
+ */
+public abstract class AnnotatedMethodParameterProcessorTest extends AbstractAnnotationProcessingTest {
+
+    protected AnnotatedMethodParameterProcessor processor;
+
+    protected RestMethodMetadata restMethodMetadata;
+
+    protected abstract AnnotatedMethodParameterProcessor createTestInstance();
+
+    @BeforeEach
+    public final void prepare() {
+        this.processor = createTestInstance();
+        this.restMethodMetadata = createRestMethodMetadata();
+    }
+
+    protected RestMethodMetadata createRestMethodMetadata() {
+        return new RestMethodMetadata();
+    }
+
+    protected abstract String getExpectedAnnotationType();
+
+    @Test
+    void testGetAnnotationType() {
+        String expectedAnnotationType = getExpectedAnnotationType();
+        assertNull(processor.getAnnotationType());
+        assertEquals(expectedAnnotationType, processor.getAnnotationType());
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/RestServiceTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/RestServiceTest.java
new file mode 100644
index 0000000..f6346fc
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/RestServiceTest.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.common.Compiler;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.RestService;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.SpringRestService;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.StandardRestService;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.User;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+/**
+ * {@link RestService} Test
+ *
+ * @since 2.7.6
+ */
+class RestServiceTest {
+
+    @Test
+    void test() throws IOException {
+        Compiler compiler = new Compiler();
+        compiler.compile(User.class, RestService.class, StandardRestService.class, SpringRestService.class);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/SpringRestServiceTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/SpringRestServiceTest.java
new file mode 100644
index 0000000..ad28b17
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/SpringRestServiceTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.common.Compiler;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.RestService;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.SpringRestService;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.User;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+/**
+ * {@link SpringRestService} Test
+ *
+ * @since 2.7.6
+ */
+class SpringRestServiceTest {
+
+    @Test
+    void test() throws IOException {
+        Compiler compiler = new Compiler();
+        compiler.compile(User.class, RestService.class, SpringRestService.class);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/StandardRestServiceTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/StandardRestServiceTest.java
new file mode 100644
index 0000000..7cef61a
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/StandardRestServiceTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing;
+
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.common.Compiler;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.RestService;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.StandardRestService;
+import org.apache.dubbo.metadata.extension.rest.annotation.processing.rest.User;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+
+/**
+ * The test case for {@link StandardRestService}
+ *
+ * @since 2.7.6
+ */
+class StandardRestServiceTest {
+
+    @Test
+    void test() throws IOException {
+        Compiler compiler = new Compiler();
+        compiler.compile(User.class, RestService.class, StandardRestService.class);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/AbstractAnnotationProcessingTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/AbstractAnnotationProcessingTest.java
new file mode 100644
index 0000000..fd09779
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/AbstractAnnotationProcessingTest.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.dubbo.metadata.extension.rest.annotation.processing.common;
+
+import org.apache.dubbo.metadata.annotation.processing.util.TypeUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+/**
+ * Abstract {@link Annotation} Processing Test case
+ *
+ * @since 2.7.6
+ */
+@ExtendWith(CompilerInvocationInterceptor.class)
+public abstract class AbstractAnnotationProcessingTest {
+
+    static ThreadLocal<AbstractAnnotationProcessingTest> testInstanceHolder = new ThreadLocal<>();
+
+    protected ProcessingEnvironment processingEnv;
+
+    protected Elements elements;
+
+    protected Types types;
+
+    @BeforeEach
+    public final void init() {
+        testInstanceHolder.set(this);
+    }
+
+    @AfterEach
+    public final void destroy() {
+        testInstanceHolder.remove();
+    }
+
+    protected abstract void addCompiledClasses(Set<Class<?>> classesToBeCompiled);
+
+    protected abstract void beforeEach();
+
+    protected TypeElement getType(Class<?> type) {
+        return TypeUtils.getType(processingEnv, type);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/AnnotationProcessingTestProcessor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/AnnotationProcessingTestProcessor.java
new file mode 100644
index 0000000..cc64814
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/AnnotationProcessingTestProcessor.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.common;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.InvocationInterceptor;
+import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import static javax.lang.model.SourceVersion.latestSupported;
+
+@SupportedAnnotationTypes("*")
+public class AnnotationProcessingTestProcessor extends AbstractProcessor {
+
+    private final AbstractAnnotationProcessingTest abstractAnnotationProcessingTest;
+    private final InvocationInterceptor.Invocation<Void> invocation;
+
+    private final ReflectiveInvocationContext<Method> invocationContext;
+
+    private final ExtensionContext extensionContext;
+
+    public AnnotationProcessingTestProcessor(
+            AbstractAnnotationProcessingTest abstractAnnotationProcessingTest,
+            InvocationInterceptor.Invocation<Void> invocation,
+            ReflectiveInvocationContext<Method> invocationContext,
+            ExtensionContext extensionContext) {
+        this.abstractAnnotationProcessingTest = abstractAnnotationProcessingTest;
+        this.invocation = invocation;
+        this.invocationContext = invocationContext;
+        this.extensionContext = extensionContext;
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if (!roundEnv.processingOver()) {
+            prepare();
+            abstractAnnotationProcessingTest.beforeEach();
+            try {
+                invocation.proceed();
+            } catch (Throwable throwable) {
+                throw new RuntimeException(throwable);
+            }
+        }
+        return false;
+    }
+
+    private void prepare() {
+        abstractAnnotationProcessingTest.processingEnv = super.processingEnv;
+        abstractAnnotationProcessingTest.elements = super.processingEnv.getElementUtils();
+        abstractAnnotationProcessingTest.types = super.processingEnv.getTypeUtils();
+    }
+
+    @Override
+    public SourceVersion getSupportedSourceVersion() {
+        return latestSupported();
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/Compiler.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/Compiler.java
new file mode 100644
index 0000000..2a8993e
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/Compiler.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.common;
+
+import javax.annotation.processing.Processor;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+
+/**
+ * The Java Compiler
+ */
+public class Compiler {
+
+    private final File sourceDirectory;
+
+    private final JavaCompiler javaCompiler;
+
+    private final StandardJavaFileManager javaFileManager;
+
+    private final Set<Processor> processors = new LinkedHashSet<>();
+
+    public Compiler() throws IOException {
+        this(defaultTargetDirectory());
+    }
+
+    public Compiler(File targetDirectory) throws IOException {
+        this(defaultSourceDirectory(), targetDirectory);
+    }
+
+    public Compiler(File sourceDirectory, File targetDirectory) throws IOException {
+        this.sourceDirectory = sourceDirectory;
+        this.javaCompiler = ToolProvider.getSystemJavaCompiler();
+        this.javaFileManager = javaCompiler.getStandardFileManager(null, null, null);
+        this.javaFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(targetDirectory));
+        this.javaFileManager.setLocation(StandardLocation.SOURCE_OUTPUT, Collections.singleton(targetDirectory));
+    }
+
+    private static File defaultSourceDirectory() {
+        return new File(defaultRootDirectory(), "src/test/java");
+    }
+
+    private static File defaultRootDirectory() {
+        return detectClassPath(Compiler.class).getParentFile().getParentFile();
+    }
+
+    private static File defaultTargetDirectory() {
+        File dir = new File(defaultRootDirectory(), "target/generated-classes");
+        dir.mkdirs();
+        return dir;
+    }
+
+    private static File detectClassPath(Class<?> targetClass) {
+        URL classFileURL = targetClass.getProtectionDomain().getCodeSource().getLocation();
+        if ("file".equals(classFileURL.getProtocol())) {
+            return new File(classFileURL.getPath());
+        } else {
+            throw new RuntimeException("No support");
+        }
+    }
+
+    public Compiler processors(Processor... processors) {
+        this.processors.addAll(asList(processors));
+        return this;
+    }
+
+    private Iterable<? extends JavaFileObject> getJavaFileObjects(Class<?>... sourceClasses) {
+        int size = sourceClasses == null ? 0 : sourceClasses.length;
+        File[] javaSourceFiles = new File[size];
+        for (int i = 0; i < size; i++) {
+            File javaSourceFile = javaSourceFile(sourceClasses[i].getName());
+            javaSourceFiles[i] = javaSourceFile;
+        }
+        return javaFileManager.getJavaFileObjects(javaSourceFiles);
+    }
+
+    private File javaSourceFile(String sourceClassName) {
+        String javaSourceFilePath = sourceClassName.replace('.', '/').concat(".java");
+        return new File(sourceDirectory, javaSourceFilePath);
+    }
+
+    public boolean compile(Class<?>... sourceClasses) {
+        JavaCompiler.CompilationTask task = javaCompiler.getTask(
+                null,
+                this.javaFileManager,
+                null,
+                asList("-parameters", "-Xlint:unchecked", "-nowarn", "-Xlint:deprecation"),
+                //                null,
+                null,
+                getJavaFileObjects(sourceClasses));
+        if (!processors.isEmpty()) {
+            task.setProcessors(processors);
+        }
+        return task.call();
+    }
+
+    public JavaCompiler getJavaCompiler() {
+        return javaCompiler;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/CompilerInvocationInterceptor.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/CompilerInvocationInterceptor.java
new file mode 100644
index 0000000..d149876
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/common/CompilerInvocationInterceptor.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.common;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.InvocationInterceptor;
+import org.junit.jupiter.api.extension.ReflectiveInvocationContext;
+
+import java.lang.reflect.Method;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static org.apache.dubbo.metadata.extension.rest.annotation.processing.common.AbstractAnnotationProcessingTest.testInstanceHolder;
+
+
+public class CompilerInvocationInterceptor implements InvocationInterceptor {
+
+    @Override
+    public void interceptTestMethod(
+            Invocation<Void> invocation,
+            ReflectiveInvocationContext<Method> invocationContext,
+            ExtensionContext extensionContext)
+            throws Throwable {
+        Set<Class<?>> classesToBeCompiled = new LinkedHashSet<>();
+        AbstractAnnotationProcessingTest abstractAnnotationProcessingTest = testInstanceHolder.get();
+        classesToBeCompiled.add(getClass());
+        abstractAnnotationProcessingTest.addCompiledClasses(classesToBeCompiled);
+        Compiler compiler = new Compiler();
+        compiler.processors(new AnnotationProcessingTestProcessor(
+                abstractAnnotationProcessingTest, invocation, invocationContext, extensionContext));
+        compiler.compile(classesToBeCompiled.toArray(new Class[0]));
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/DefaultRestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/DefaultRestService.java
new file mode 100644
index 0000000..8694103
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/DefaultRestService.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.rest;
+
+import org.apache.dubbo.config.annotation.DubboService;
+
+import java.util.Map;
+
+/**
+ * The default implementation of {@link RestService}
+ *
+ * @since 2.7.6
+ */
+@DubboService(version = "1.0.0", group = "default")
+public class DefaultRestService implements RestService {
+
+    @Override
+    public String param(String param) {
+        return null;
+    }
+
+    @Override
+    public String params(int a, String b) {
+        return null;
+    }
+
+    @Override
+    public String headers(String header, String header2, Integer param) {
+        return null;
+    }
+
+    @Override
+    public String pathVariables(String path1, String path2, String param) {
+        return null;
+    }
+
+    @Override
+    public String form(String form) {
+        return null;
+    }
+
+    @Override
+    public User requestBodyMap(Map<String, Object> data, String param) {
+        return null;
+    }
+
+    @Override
+    public Map<String, Object> requestBodyUser(User user) {
+        return null;
+    }
+
+    public User user(User user) {
+        return user;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/RestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/RestService.java
new file mode 100644
index 0000000..09872db
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/RestService.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.rest;
+
+import java.util.Map;
+
+/**
+ * An interface for REST service
+ *
+ * @since 2.7.6
+ */
+public interface RestService {
+
+    String param(String param);
+
+    String params(int a, String b);
+
+    String headers(String header, String header2, Integer param);
+
+    String pathVariables(String path1, String path2, String param);
+
+    String form(String form);
+
+    User requestBodyMap(Map<String, Object> data, String param);
+
+    Map<String, Object> requestBodyUser(User user);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/SpringRestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/SpringRestService.java
new file mode 100644
index 0000000..dabe3a1
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/SpringRestService.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.rest;
+
+import org.apache.dubbo.config.annotation.DubboService;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Spring MVC {@link RestService}
+ *
+ * @since 2.7.6
+ */
+@DubboService(version = "2.0.0", group = "spring")
+@RestController
+public class SpringRestService implements RestService {
+
+    @Override
+    @GetMapping(value = "/param")
+    public String param(@RequestParam(defaultValue = "value-param") String param) {
+        return null;
+    }
+
+    @Override
+    @PostMapping("/params")
+    public String params(
+            @RequestParam(defaultValue = "value-a") int a, @RequestParam(defaultValue = "value-b") String b) {
+        return null;
+    }
+
+    @Override
+    @GetMapping("/headers")
+    public String headers(
+            @RequestHeader(name = "h", defaultValue = "value-h") String header,
+            @RequestHeader(name = "h2", defaultValue = "value-h2") String header2,
+            @RequestParam(value = "v", defaultValue = "1") Integer param) {
+        return null;
+    }
+
+    @Override
+    @GetMapping("/path-variables/{p1}/{p2}")
+    public String pathVariables(
+        @PathVariable("p1") String path1, @PathVariable("p2") String path2, @RequestParam("v") String param) {
+        return null;
+    }
+
+    @Override
+    @PostMapping("/form")
+    public String form(@RequestParam("f") String form) {
+        return String.valueOf(form);
+    }
+
+    @Override
+    @PostMapping(value = "/request/body/map", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public User requestBodyMap(@RequestBody Map<String, Object> data, @RequestParam("param") String param) {
+        User user = new User();
+        user.setId(((Integer) data.get("id")).longValue());
+        user.setName((String) data.get("name"));
+        user.setAge((Integer) data.get("age"));
+        return user;
+    }
+
+    @PostMapping(value = "/request/body/user", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    @Override
+    public Map<String, Object> requestBodyUser(@RequestBody User user) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("id", user.getId());
+        map.put("name", user.getName());
+        map.put("age", user.getAge());
+        return map;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/StandardRestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/StandardRestService.java
new file mode 100644
index 0000000..592b0bc
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/StandardRestService.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.rest;
+
+import org.apache.dubbo.config.annotation.DubboService;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * JAX-RS {@link RestService}
+ */
+@DubboService(
+        version = "3.0.0",
+        protocol = {"dubbo", "rest"},
+        group = "standard")
+@Path("/")
+public class StandardRestService implements RestService {
+
+    @Override
+    @Path("param")
+    @GET
+    public String param(@QueryParam("param") String param) {
+        return param;
+    }
+
+    @Override
+    @Path("params")
+    @POST
+    public String params(@QueryParam("a") int a, @QueryParam("b") String b) {
+        return a + b;
+    }
+
+    @Override
+    @Path("headers")
+    @GET
+    public String headers(
+        @HeaderParam("h") String header, @HeaderParam("h2") String header2, @QueryParam("v") Integer param) {
+        String result = header + " , " + header2 + " , " + param;
+        return result;
+    }
+
+    @Override
+    @Path("path-variables/{p1}/{p2}")
+    @GET
+    public String pathVariables(
+        @PathParam("p1") String path1, @PathParam("p2") String path2, @QueryParam("v") String param) {
+        String result = path1 + " , " + path2 + " , " + param;
+        return result;
+    }
+
+    // @CookieParam does not support : https://github.com/OpenFeign/feign/issues/913
+    // @CookieValue also does not support
+
+    @Override
+    @Path("form")
+    @POST
+    public String form(@FormParam("f") String form) {
+        return String.valueOf(form);
+    }
+
+    @Override
+    @Path("request/body/map")
+    @POST
+    @Produces("application/json;charset=UTF-8")
+    public User requestBodyMap(Map<String, Object> data, @QueryParam("param") String param) {
+        User user = new User();
+        user.setId(((Integer) data.get("id")).longValue());
+        user.setName((String) data.get("name"));
+        user.setAge((Integer) data.get("age"));
+        return user;
+    }
+
+    @Path("request/body/user")
+    @POST
+    @Override
+    @Consumes("application/json;charset=UTF-8")
+    public Map<String, Object> requestBodyUser(User user) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("id", user.getId());
+        map.put("name", user.getName());
+        map.put("age", user.getAge());
+        return map;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/User.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/User.java
new file mode 100644
index 0000000..c03cd4e
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/annotation/processing/rest/User.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.annotation.processing.rest;
+
+import java.io.Serializable;
+
+/**
+ * User Entity
+ *
+ * @since 2.7.6
+ */
+public class User implements Serializable {
+
+    private Long id;
+
+    private String name;
+
+    private Integer age;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    @Override
+    public String toString() {
+        return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/DefaultRestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/DefaultRestService.java
new file mode 100644
index 0000000..0926902
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/DefaultRestService.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.config.annotation.Service;
+
+import java.util.Map;
+
+/**
+ * The default implementation of {@link RestService}
+ *
+ * @since 2.7.6
+ */
+@Service(version = "1.0.0")
+public class DefaultRestService implements RestService {
+
+    @Override
+    public String param(String param) {
+        return null;
+    }
+
+    @Override
+    public String params(int a, String b) {
+        return null;
+    }
+
+    @Override
+    public String headers(String header, String header2, Integer param) {
+        return null;
+    }
+
+    @Override
+    public String pathVariables(String path1, String path2, String param) {
+        return null;
+    }
+
+    @Override
+    public String form(String form) {
+        return null;
+    }
+
+    @Override
+    public User requestBodyMap(Map<String, Object> data, String param) {
+        return null;
+    }
+
+    @Override
+    public Map<String, Object> requestBodyUser(User user) {
+        return null;
+    }
+
+    @Override
+    public void noAnnotationJsonBody(User user) {}
+
+    @Override
+    public void noAnnotationFormBody(User user) {}
+
+    @Override
+    public void noAnnotationParam(String text) {}
+
+    public User user(User user) {
+        return user;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/NoAnnotationApiDemoResolverTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/NoAnnotationApiDemoResolverTest.java
new file mode 100644
index 0000000..cf95a28
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/NoAnnotationApiDemoResolverTest.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.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver;
+import org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+public class NoAnnotationApiDemoResolverTest {
+    private JAXRSServiceRestMetadataResolver jaxrsServiceRestMetadataResolver =
+            new JAXRSServiceRestMetadataResolver(ApplicationModel.defaultModel());
+    private SpringMvcServiceRestMetadataResolver springMvcServiceRestMetadataResolver =
+            new SpringMvcServiceRestMetadataResolver(ApplicationModel.defaultModel());
+
+    @Test
+    void testNoAnnotationApiResolver() {
+        Assertions.assertTrue(jaxrsServiceRestMetadataResolver.supports(JaxrsNoAnnotationApiDemoImpl.class));
+        Assertions.assertTrue(springMvcServiceRestMetadataResolver.supports(SpringMvcNoAnnotationApiDemoImpl.class));
+    }
+}
+
+class JaxrsNoAnnotationApiDemoImpl implements NoAnnotationApiDemo {
+    @Override
+    @Path("/test")
+    @GET
+    public String test(@QueryParam("test") String test) {
+        return "success" + test;
+    }
+}
+
+class SpringMvcNoAnnotationApiDemoImpl implements NoAnnotationApiDemo {
+    @Override
+    @RequestMapping("/test")
+    public String test(@RequestBody() String test) {
+        return "success" + test;
+    }
+}
+
+interface NoAnnotationApiDemo {
+
+    String test(String test);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/RestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/RestService.java
new file mode 100644
index 0000000..47307e2
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/RestService.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import java.util.Map;
+
+/**
+ * An interface for REST service
+ *
+ * @since 2.7.6
+ */
+public interface RestService {
+
+    String param(String param);
+
+    String params(int a, String b);
+
+    String headers(String header, String header2, Integer param);
+
+    String pathVariables(String path1, String path2, String param);
+
+    String form(String form);
+
+    User requestBodyMap(Map<String, Object> data, String param);
+
+    Map<String, Object> requestBodyUser(User user);
+
+    void noAnnotationJsonBody(User user);
+
+    void noAnnotationFormBody(User user);
+
+    void noAnnotationParam(String text);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/SpringRestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/SpringRestService.java
new file mode 100644
index 0000000..1fe4b59
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/SpringRestService.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.config.annotation.DubboService;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Spring MVC {@link RestService}
+ *
+ * @since 2.7.6
+ */
+@DubboService(version = "2.0.0", group = "spring")
+@RestController
+public class SpringRestService implements RestService {
+
+    @Override
+    @GetMapping(value = "/param")
+    public String param(@RequestParam(defaultValue = "value-param") String param) {
+        return null;
+    }
+
+    @Override
+    @PostMapping("/params")
+    public String params(
+            @RequestParam(defaultValue = "value-a") int a, @RequestParam(defaultValue = "value-b") String b) {
+        return null;
+    }
+
+    @Override
+    @GetMapping("/headers")
+    public String headers(
+            @RequestHeader(name = "h", defaultValue = "value-h") String header,
+            @RequestHeader(name = "h2", defaultValue = "value-h2") String header2,
+            @RequestParam(value = "v", defaultValue = "1") Integer param) {
+        return null;
+    }
+
+    @Override
+    @GetMapping("/path-variables/{p1}/{p2}")
+    public String pathVariables(
+        @PathVariable("p1") String path1, @PathVariable("p2") String path2, @RequestParam("v") String param) {
+        return null;
+    }
+
+    @Override
+    @PostMapping("/form")
+    public String form(@RequestParam("f") String form) {
+        return String.valueOf(form);
+    }
+
+    @Override
+    @PostMapping(value = "/request/body/map", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public User requestBodyMap(@RequestBody Map<String, Object> data, @RequestParam("param") String param) {
+        User user = new User();
+        user.setId(((Integer) data.get("id")).longValue());
+        user.setName((String) data.get("name"));
+        user.setAge((Integer) data.get("age"));
+        return user;
+    }
+
+    @PostMapping(value = "/request/body/user", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    @Override
+    public Map<String, Object> requestBodyUser(@RequestBody User user) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("id", user.getId());
+        map.put("name", user.getName());
+        map.put("age", user.getAge());
+        return map;
+    }
+
+    @PostMapping(value = "/request/body/user/json", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    @Override
+    public void noAnnotationJsonBody(User user) {}
+
+    @PostMapping(value = "/request/body/user/form", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    @Override
+    public void noAnnotationFormBody(User user) {}
+
+    @PostMapping(value = "/request/body/user/param")
+    @Override
+    public void noAnnotationParam(String text) {}
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/StandardRestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/StandardRestService.java
new file mode 100644
index 0000000..46e6488
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/StandardRestService.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import org.apache.dubbo.config.annotation.DubboService;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * JAX-RS {@link RestService}
+ */
+@DubboService(
+        version = "3.0.0",
+        protocol = {"dubbo", "rest"},
+        group = "standard")
+@Path("/")
+public class StandardRestService implements RestService {
+
+    @Override
+    @Path("param")
+    @GET
+    public String param(@QueryParam("param") String param) {
+        return param;
+    }
+
+    @Override
+    @Path("params")
+    @POST
+    public String params(@QueryParam("a") int a, @QueryParam("b") String b) {
+        return a + b;
+    }
+
+    @Override
+    @Path("headers")
+    @GET
+    public String headers(
+        @HeaderParam("h") String header, @HeaderParam("h2") String header2, @QueryParam("v") Integer param) {
+        String result = header + " , " + header2 + " , " + param;
+        return result;
+    }
+
+    @Override
+    @Path("path-variables/{p1}/{p2}")
+    @GET
+    public String pathVariables(
+        @PathParam("p1") String path1, @PathParam("p2") String path2, @QueryParam("v") String param) {
+        String result = path1 + " , " + path2 + " , " + param;
+        return result;
+    }
+
+    // @CookieParam does not support : https://github.com/OpenFeign/feign/issues/913
+    // @CookieValue also does not support
+
+    @Override
+    @Path("form")
+    @POST
+    public String form(@FormParam("f") String form) {
+        return String.valueOf(form);
+    }
+
+    @Override
+    @Path("request/body/map")
+    @POST
+    @Produces("application/json;charset=UTF-8")
+    public User requestBodyMap(Map<String, Object> data, @QueryParam("param") String param) {
+        User user = new User();
+        user.setId(((Integer) data.get("id")).longValue());
+        user.setName((String) data.get("name"));
+        user.setAge((Integer) data.get("age"));
+        return user;
+    }
+
+    @Path("request/body/user")
+    @POST
+    @Override
+    @Consumes("application/json;charset=UTF-8")
+    public Map<String, Object> requestBodyUser(User user) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("id", user.getId());
+        map.put("name", user.getName());
+        map.put("age", user.getAge());
+        return map;
+    }
+
+    @Path("noAnnotationJsonBody/json")
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Override
+    public void noAnnotationJsonBody(User user) {}
+
+    @Path("noAnnotationFormBody/form")
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @POST
+    @Override
+    public void noAnnotationFormBody(User user) {}
+
+    @Path("noAnnotationParam/text")
+    @POST
+    @Override
+    public void noAnnotationParam(String text) {}
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/User.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/User.java
new file mode 100644
index 0000000..6969fd5
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/User.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api;
+
+import java.io.Serializable;
+
+/**
+ * User Entity
+ *
+ * @since 2.7.6
+ */
+public class User implements Serializable {
+
+    private Long id;
+
+    private String name;
+
+    private Integer age;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    @Override
+    public String toString() {
+        return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/AnotherUserRestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/AnotherUserRestService.java
new file mode 100644
index 0000000..c3fe6d5
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/AnotherUserRestService.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.api;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.User;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+@Path("u")
+@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
+@Produces({MediaType.APPLICATION_JSON})
+public interface AnotherUserRestService {
+
+    @GET
+    @Path("{id : \\d+}")
+    User getUser(@PathParam("id") Long id);
+
+    @POST
+    @Path("register")
+    String registerUser(User user);
+
+    @GET
+    @Path("context")
+    String getContext();
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestDoubleCheckContainsPathVariableService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestDoubleCheckContainsPathVariableService.java
new file mode 100644
index 0000000..f5c55a6
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestDoubleCheckContainsPathVariableService.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.api;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+@Path("/test")
+public interface JaxrsRestDoubleCheckContainsPathVariableService {
+    @Path("/a/{b}")
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    @GET
+    String param(@QueryParam("param") String param);
+
+    @Path("/{b}/b")
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    @GET
+    String header(@HeaderParam("header") String header);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestDoubleCheckService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestDoubleCheckService.java
new file mode 100644
index 0000000..742a576
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestDoubleCheckService.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.api;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+@Path("/test")
+public interface JaxrsRestDoubleCheckService {
+    @Path("/param")
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    @GET
+    String param(@QueryParam("param") String param);
+
+    @Path("/param")
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    @GET
+    String header(@HeaderParam("header") String header);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestService.java
new file mode 100644
index 0000000..441cb4d
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestService.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.api;
+
+import org.apache.dubbo.metadata.extension.rest.api.User;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+public interface JaxrsRestService {
+
+    @Path("/param")
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    @GET
+    String param(@QueryParam("param") String param);
+
+    @Path("/header")
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    @GET
+    String header(@HeaderParam("header") String header);
+
+    @Path("/body")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    @POST
+    User body(User user);
+
+    @Path("/multiValue")
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Produces(MediaType.APPLICATION_FORM_URLENCODED)
+    @POST
+    MultivaluedMap multiValue(MultivaluedMap map);
+
+    @Path("/pathVariable/{a}")
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    @Produces(MediaType.APPLICATION_FORM_URLENCODED)
+    @POST
+    String pathVariable(@PathParam("a") String a);
+
+    @Path("/noAnno")
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.TEXT_PLAIN)
+    @POST
+    String noAnno(String a);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestServiceImpl.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestServiceImpl.java
new file mode 100644
index 0000000..981919c
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsRestServiceImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.api;
+
+import org.apache.dubbo.metadata.extension.rest.api.User;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+public class JaxrsRestServiceImpl implements JaxrsRestService {
+
+    @Override
+    public String param(String param) {
+        return param;
+    }
+
+    @Override
+    public String header(String header) {
+        return header;
+    }
+
+    @Override
+    public User body(User user) {
+        return user;
+    }
+
+    @Override
+    public MultivaluedMap multiValue(MultivaluedMap map) {
+        return map;
+    }
+
+    @Override
+    public String pathVariable(String a) {
+        return a;
+    }
+
+    @Override
+    public String noAnno(String a) {
+        return a;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsUsingService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsUsingService.java
new file mode 100644
index 0000000..8d8bf17
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/JaxrsUsingService.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.api;
+
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+@Path("usingService")
+@Consumes({MediaType.APPLICATION_JSON})
+@Produces({MediaType.APPLICATION_JSON})
+public interface JaxrsUsingService {
+
+    @GET
+    Response getUsers();
+
+    @POST
+    Response createUser(Object user);
+
+    @GET
+    @Path("{uid}")
+    Response getUserByUid(@PathParam("uid") String uid);
+
+    @DELETE
+    @Path("{uid}")
+    Response deleteUserByUid(@PathParam("uid") String uid);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/SpringControllerService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/SpringControllerService.java
new file mode 100644
index 0000000..604f239
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/SpringControllerService.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.api;
+
+import org.apache.dubbo.metadata.extension.rest.api.User;
+import org.springframework.http.MediaType;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@RestController
+public class SpringControllerService {
+    @RequestMapping(
+            value = "/param",
+            method = RequestMethod.GET,
+            consumes = MediaType.TEXT_PLAIN_VALUE,
+            produces = MediaType.TEXT_PLAIN_VALUE)
+    public String param(@RequestParam String param) {
+        return param;
+    }
+
+    @RequestMapping(
+            value = "/header",
+            method = RequestMethod.GET,
+            consumes = MediaType.TEXT_PLAIN_VALUE,
+            produces = MediaType.TEXT_PLAIN_VALUE)
+    public String header(@RequestHeader String header) {
+        return header;
+    }
+
+    @RequestMapping(
+            value = "/body",
+            method = RequestMethod.POST,
+            consumes = MediaType.APPLICATION_JSON_VALUE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    public User body(@RequestBody User user) {
+        return user;
+    }
+
+    @RequestMapping(
+            value = "/multiValue",
+            method = RequestMethod.POST,
+            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
+            produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    public MultiValueMap multiValue(@RequestBody MultiValueMap map) {
+        return map;
+    }
+
+    @RequestMapping(
+            value = "/pathVariable/{a}",
+            method = RequestMethod.POST,
+            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
+            produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    public String pathVariable(@PathVariable String a) {
+        return a;
+    }
+
+    @RequestMapping(
+            value = "/noAnnoParam",
+            method = RequestMethod.POST,
+            consumes = MediaType.TEXT_PLAIN_VALUE,
+            produces = MediaType.TEXT_PLAIN_VALUE)
+    public String noAnnoParam(String a) {
+        return a;
+    }
+
+    @RequestMapping(
+            value = "/noAnnoNumber",
+            method = RequestMethod.POST,
+            consumes = MediaType.ALL_VALUE,
+            produces = MediaType.ALL_VALUE)
+    public int noAnnoNumber(Integer b) {
+        return b;
+    }
+
+    @RequestMapping(
+            value = "/noAnnoPrimitive",
+            method = RequestMethod.POST,
+            consumes = MediaType.ALL_VALUE,
+            produces = MediaType.ALL_VALUE)
+    public int noAnnoPrimitive(int c) {
+        return c;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/SpringRestService.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/SpringRestService.java
new file mode 100644
index 0000000..256865b
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/SpringRestService.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.api;
+
+import org.apache.dubbo.metadata.extension.rest.api.User;
+import org.springframework.http.MediaType;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@RestController
+public interface SpringRestService {
+
+    @RequestMapping(
+            value = "/param",
+            method = RequestMethod.GET,
+            consumes = MediaType.TEXT_PLAIN_VALUE,
+            produces = MediaType.TEXT_PLAIN_VALUE)
+    String param(@RequestParam("param") String param);
+
+    @RequestMapping(
+            value = "/header",
+            method = RequestMethod.GET,
+            consumes = MediaType.TEXT_PLAIN_VALUE,
+            produces = MediaType.TEXT_PLAIN_VALUE)
+    String header(@RequestHeader("header") String header);
+
+    @RequestMapping(
+            value = "/body",
+            method = RequestMethod.POST,
+            consumes = MediaType.APPLICATION_JSON_VALUE,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    User body(@RequestBody User user);
+
+    @RequestMapping(
+            value = "/multiValue",
+            method = RequestMethod.POST,
+            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
+            produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    MultiValueMap multiValue(@RequestBody MultiValueMap map);
+
+    @RequestMapping(
+            value = "/pathVariable/{a}",
+            method = RequestMethod.POST,
+            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
+            produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    String pathVariable(@PathVariable String a);
+
+    @RequestMapping(
+            value = "/noAnnoParam",
+            method = RequestMethod.POST,
+            consumes = MediaType.TEXT_PLAIN_VALUE,
+            produces = MediaType.TEXT_PLAIN_VALUE)
+    String noAnnoParam(String a);
+
+    @RequestMapping(
+            value = "/noAnnoNumber",
+            method = RequestMethod.POST,
+            consumes = MediaType.ALL_VALUE,
+            produces = MediaType.ALL_VALUE)
+    int noAnnoNumber(Integer b);
+
+    @RequestMapping(
+            value = "/noAnnoPrimitive",
+            method = RequestMethod.POST,
+            consumes = MediaType.ALL_VALUE,
+            produces = MediaType.ALL_VALUE)
+    int noAnnoPrimitive(int c);
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/SpringRestServiceImpl.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/SpringRestServiceImpl.java
new file mode 100644
index 0000000..8178b24
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/api/SpringRestServiceImpl.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.api;
+
+import org.apache.dubbo.metadata.extension.rest.api.User;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class SpringRestServiceImpl implements SpringRestService {
+
+    @Override
+    public String param(String param) {
+        return param;
+    }
+
+    @Override
+    public String header(String header) {
+        return header;
+    }
+
+    @Override
+    public User body(User user) {
+        return user;
+    }
+
+    @Override
+    public MultiValueMap multiValue(MultiValueMap map) {
+        return map;
+    }
+
+    @Override
+    public String pathVariable(String a) {
+        return a;
+    }
+
+    @Override
+    public String noAnnoParam(String a) {
+        return a;
+    }
+
+    @Override
+    public int noAnnoNumber(Integer b) {
+        return b;
+    }
+
+    @Override
+    public int noAnnoPrimitive(int c) {
+        return c;
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/JAXRSServiceRestMetadataResolverTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/JAXRSServiceRestMetadataResolverTest.java
new file mode 100644
index 0000000..c763b8d
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/JAXRSServiceRestMetadataResolverTest.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.JsonUtils;
+
+import org.apache.dubbo.metadata.extension.rest.api.ClassPathServiceRestMetadataReader;
+import org.apache.dubbo.metadata.extension.rest.api.DefaultRestService;
+import org.apache.dubbo.metadata.extension.rest.api.PathUtil;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.RestService;
+import org.apache.dubbo.metadata.extension.rest.api.StandardRestService;
+import org.apache.dubbo.metadata.extension.rest.api.api.AnotherUserRestService;
+import org.apache.dubbo.metadata.extension.rest.api.api.JaxrsRestService;
+import org.apache.dubbo.metadata.extension.rest.api.api.JaxrsRestServiceImpl;
+import org.apache.dubbo.metadata.extension.rest.api.api.SpringRestService;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link JAXRSServiceRestMetadataResolver} Test
+ *
+ * @since 2.7.6
+ */
+class JAXRSServiceRestMetadataResolverTest {
+
+    private JAXRSServiceRestMetadataResolver instance =
+        new JAXRSServiceRestMetadataResolver(ApplicationModel.defaultModel());
+
+    @Test
+    void testSupports() {
+        // JAX-RS RestService class
+        assertTrue(instance.supports(StandardRestService.class));
+        // Spring MVC RestService class
+        assertFalse(instance.supports(SpringRestService.class));
+        // Default RestService class
+        assertFalse(instance.supports(DefaultRestService.class));
+        // No annotated RestService class
+        assertFalse(instance.supports(RestService.class));
+        // null
+        assertFalse(instance.supports(null));
+    }
+
+    @Test
+    @Disabled
+    void testResolve() {
+        // Generated by "dubbo-metadata-processor"
+        ClassPathServiceRestMetadataReader reader =
+            new ClassPathServiceRestMetadataReader("META-INF/dubbo/jax-rs-service-rest-metadata.json");
+        List<ServiceRestMetadata> serviceRestMetadataList = reader.read();
+
+        ServiceRestMetadata expectedServiceRestMetadata = serviceRestMetadataList.get(0);
+        ServiceRestMetadata serviceRestMetadata = instance.resolve(StandardRestService.class);
+
+        assertTrue(CollectionUtils.equals(expectedServiceRestMetadata.getMeta(), serviceRestMetadata.getMeta()));
+
+        assertEquals(expectedServiceRestMetadata, serviceRestMetadata);
+    }
+
+    @Test
+    void testResolves() {
+        testResolve(JaxrsRestService.class);
+        testResolve(JaxrsRestServiceImpl.class);
+    }
+
+    void testResolve(Class service) {
+        // Generated by "dubbo-metadata-processor"
+
+        List<String> jsons = Arrays.asList(
+            "{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.extension.rest.api.tag.BodyTag\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/noAnno\",\"produces\":[\"text/plain\"]}}",
+            "{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.PathParam\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":2}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/pathVariable/{a}\",\"produces\":[\"application/x-www-form-urlencoded\"]}}",
+            "{\"argInfos\":[{\"annotationNameAttribute\":\"map\",\"formContentType\":true,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.extension.rest.api.tag.BodyTag\",\"paramName\":\"map\",\"paramType\":\"javax.ws.rs.core.MultivaluedMap\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"map\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/multiValue\",\"produces\":[\"application/x-www-form-urlencoded\"]}}",
+            "{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.extension.rest.api.tag.BodyTag\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.extension.rest.api.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/body\",\"produces\":[\"application/json\"]}}",
+            "{\"argInfos\":[{\"annotationNameAttribute\":\"param\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.QueryParam\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"GET\",\"paramNames\":[\"param\"],\"params\":{\"param\":[\"{0}\"]},\"path\":\"/param\",\"produces\":[\"text/plain\"]}}",
+            "{\"argInfos\":[{\"annotationNameAttribute\":\"header\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"javax.ws.rs.HeaderParam\",\"paramName\":\"header\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver\",\"indexToName\":{0:[\"header\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[\"header\"],\"headers\":{\"header\":[\"{0}\"]},\"method\":\"GET\",\"paramNames\":[],\"params\":{},\"path\":\"/header\",\"produces\":[\"text/plain\"]}}");
+
+        ServiceRestMetadata jaxrsRestMetadata = new ServiceRestMetadata();
+        jaxrsRestMetadata.setServiceInterface(service.getName());
+        ServiceRestMetadata jaxrsMetadata = instance.resolve(service, jaxrsRestMetadata);
+
+        List<String> jsonsTmp = new ArrayList<>();
+        for (RestMethodMetadata restMethodMetadata : jaxrsMetadata.getMeta()) {
+            restMethodMetadata.setReflectMethod(null);
+            restMethodMetadata.setMethod(null);
+            jsonsTmp.add(JsonUtils.toJson(restMethodMetadata));
+        }
+
+        Comparator<String> comparator = new Comparator<String>() {
+            @Override
+            public int compare(String o1, String o2) {
+                return o1.length() - o2.length();
+            }
+        };
+        jsons.sort(comparator);
+        jsonsTmp.sort(comparator);
+
+        for (int i = 0; i < jsons.size(); i++) {
+            assertEquals(jsons.get(i), jsonsTmp.get(i));
+        }
+    }
+
+    @Test
+    void testJaxrsPathPattern() {
+        Class service = AnotherUserRestService.class;
+        ServiceRestMetadata jaxrsRestMetadata = new ServiceRestMetadata();
+        jaxrsRestMetadata.setServiceInterface(service.getName());
+        ServiceRestMetadata jaxrsMetadata = instance.resolve(service, jaxrsRestMetadata);
+
+        RestMethodMetadata[] objects = jaxrsMetadata.getMeta().toArray(new RestMethodMetadata[0]);
+        RestMethodMetadata object = null;
+        for (RestMethodMetadata obj : objects) {
+            if ("getUser".equals(obj.getReflectMethod().getName())) {
+                object = obj;
+            }
+        }
+
+        Assertions.assertEquals(
+            "/u/1", PathUtil.resolvePathVariable("/u/{id : \\d+}", object.getArgInfos(), Arrays.asList(1)));
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/JaxrsRestDoubleCheckTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/JaxrsRestDoubleCheckTest.java
new file mode 100644
index 0000000..6eff243
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/jaxrs/JaxrsRestDoubleCheckTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.jaxrs;
+
+import org.apache.dubbo.metadata.extension.rest.api.api.JaxrsRestDoubleCheckContainsPathVariableService;
+import org.apache.dubbo.metadata.extension.rest.api.api.JaxrsRestDoubleCheckService;
+import org.apache.dubbo.metadata.extension.rest.api.api.JaxrsUsingService;
+import org.apache.dubbo.metadata.extension.rest.api.PathMatcher;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+public class JaxrsRestDoubleCheckTest {
+    private org.apache.dubbo.metadata.extension.rest.api.jaxrs.JAXRSServiceRestMetadataResolver instance =
+            new JAXRSServiceRestMetadataResolver(ApplicationModel.defaultModel());
+
+    @Test
+    void testDoubleCheckException() {
+
+        Assertions.assertThrows(IllegalArgumentException.class, () -> {
+            ServiceRestMetadata resolve = new ServiceRestMetadata();
+            resolve.setServiceInterface(JaxrsRestDoubleCheckService.class.getName());
+            instance.resolve(JaxrsRestDoubleCheckService.class, resolve);
+        });
+
+        Assertions.assertThrows(IllegalArgumentException.class, () -> {
+            ServiceRestMetadata resolve = new ServiceRestMetadata();
+            resolve.setServiceInterface(JaxrsRestDoubleCheckContainsPathVariableService.class.getName());
+            instance.resolve(JaxrsRestDoubleCheckContainsPathVariableService.class, resolve);
+        });
+    }
+
+    @Test
+    void testSameHttpMethodException() {
+
+        Assertions.assertDoesNotThrow(() -> {
+            ServiceRestMetadata resolve = new ServiceRestMetadata();
+            resolve.setServiceInterface(JaxrsUsingService.class.getName());
+            instance.resolve(JaxrsUsingService.class, resolve);
+        });
+
+        ServiceRestMetadata resolve = new ServiceRestMetadata();
+        resolve.setServiceInterface(JaxrsUsingService.class.getName());
+        instance.resolve(JaxrsUsingService.class, resolve);
+
+        Map<PathMatcher, RestMethodMetadata> pathContainPathVariableToServiceMap =
+                resolve.getPathContainPathVariableToServiceMap();
+
+        RestMethodMetadata restMethodMetadata = pathContainPathVariableToServiceMap.get(
+                PathMatcher.getInvokeCreatePathMatcher("/usingService/aaa", null, null, null, "TEST"));
+
+        Assertions.assertNotNull(restMethodMetadata);
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/SpringMvcServiceRestMetadataResolverTest.java b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/SpringMvcServiceRestMetadataResolverTest.java
new file mode 100644
index 0000000..cbf1a9f
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/java/org/apache/dubbo/metadata/extension/rest/api/springmvc/SpringMvcServiceRestMetadataResolverTest.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metadata.extension.rest.api.springmvc;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.JsonUtils;
+
+import org.apache.dubbo.metadata.extension.rest.api.ClassPathServiceRestMetadataReader;
+import org.apache.dubbo.metadata.extension.rest.api.DefaultRestService;
+import org.apache.dubbo.metadata.extension.rest.api.PathMatcher;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.RestService;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.StandardRestService;
+import org.apache.dubbo.metadata.extension.rest.api.api.SpringControllerService;
+import org.apache.dubbo.metadata.extension.rest.api.api.SpringRestService;
+import org.apache.dubbo.metadata.extension.rest.api.api.SpringRestServiceImpl;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * {@link SpringMvcServiceRestMetadataResolver} Test
+ *
+ * @since 2.7.9
+ */
+class SpringMvcServiceRestMetadataResolverTest {
+
+    private SpringMvcServiceRestMetadataResolver instance =
+            new SpringMvcServiceRestMetadataResolver(ApplicationModel.defaultModel());
+
+    @Test
+    void testSupports() {
+        // Spring MVC RestService class
+        assertTrue(instance.supports(SpringRestService.class, true));
+        // JAX-RS RestService class
+        assertFalse(instance.supports(StandardRestService.class, true));
+        // Default RestService class
+        assertFalse(instance.supports(DefaultRestService.class, true));
+        // No annotated RestService class
+        assertFalse(instance.supports(RestService.class, true));
+        // null
+        assertFalse(instance.supports(null, true));
+    }
+
+    @Test
+    @Disabled
+    void testResolve() {
+        // Generated by "dubbo-metadata-processor"
+        ClassPathServiceRestMetadataReader reader =
+                new ClassPathServiceRestMetadataReader("META-INF/dubbo/spring-mvc-service-rest-metadata.json");
+        List<ServiceRestMetadata> serviceRestMetadataList = reader.read();
+
+        ServiceRestMetadata expectedServiceRestMetadata = serviceRestMetadataList.get(0);
+        ServiceRestMetadata serviceRestMetadata = instance.resolve(SpringRestService.class);
+
+        assertTrue(CollectionUtils.equals(expectedServiceRestMetadata.getMeta(), serviceRestMetadata.getMeta()));
+
+        assertEquals(expectedServiceRestMetadata, serviceRestMetadata);
+    }
+
+    @Test
+    void testResolves() {
+        testResolve(SpringRestService.class);
+        testResolve(SpringRestServiceImpl.class);
+        testResolve(SpringControllerService.class);
+    }
+
+    void testResolve(Class service) {
+        List<String> jsons = Arrays.asList(
+                "{\"argInfos\":[{\"annotationNameAttribute\":\"b\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.extension.rest.api.tag.ParamTag\",\"paramName\":\"b\",\"paramType\":\"java.lang.Integer\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"b\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"*/*\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/noAnnoNumber\",\"produces\":[\"*/*\"]}}",
+                "{\"argInfos\":[{\"annotationNameAttribute\":\"c\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.extension.rest.api.tag.ParamTag\",\"paramName\":\"c\",\"paramType\":\"int\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"c\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"*/*\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/noAnnoPrimitive\",\"produces\":[\"*/*\"]}}",
+                "{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.extension.rest.api.tag.ParamTag\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/noAnnoParam\",\"produces\":[\"text/plain\"]}}",
+                "{\"argInfos\":[{\"annotationNameAttribute\":\"\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.PathVariable\",\"paramName\":\"a\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":2}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/pathVariable/{a}\",\"produces\":[\"application/x-www-form-urlencoded\"]}}",
+                "{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.extension.rest.api.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/body\",\"produces\":[\"application/json\"]}}",
+                "{\"argInfos\":[{\"annotationNameAttribute\":\"param\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestParam\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[],\"headers\":{},\"method\":\"GET\",\"paramNames\":[\"param\"],\"params\":{\"param\":[\"{0}\"]},\"path\":\"/param\",\"produces\":[\"text/plain\"]}}",
+                "{\"argInfos\":[{\"annotationNameAttribute\":\"map\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestBody\",\"paramName\":\"map\",\"paramType\":\"org.springframework.util.MultiValueMap\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"map\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/x-www-form-urlencoded\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/multiValue\",\"produces\":[\"application/x-www-form-urlencoded\"]}}",
+                "{\"argInfos\":[{\"annotationNameAttribute\":\"header\",\"defaultValue\":\"{0}\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.springframework.web.bind.annotation.RequestHeader\",\"paramName\":\"header\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.extension.rest.api.springmvc.SpringMvcServiceRestMetadataResolver\",\"indexToName\":{0:[\"header\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"text/plain\"],\"headerNames\":[\"header\"],\"headers\":{\"header\":[\"{0}\"]},\"method\":\"GET\",\"paramNames\":[],\"params\":{},\"path\":\"/header\",\"produces\":[\"text/plain\"]}}");
+
+        ServiceRestMetadata springRestMetadata = new ServiceRestMetadata();
+        springRestMetadata.setServiceInterface(service.getName());
+        ServiceRestMetadata springMetadata = instance.resolve(service, springRestMetadata);
+
+        List<String> jsonsTmp = new ArrayList<>();
+        for (RestMethodMetadata restMethodMetadata : springMetadata.getMeta()) {
+            restMethodMetadata.setReflectMethod(null);
+            restMethodMetadata.setMethod(null);
+            jsonsTmp.add(JsonUtils.toJson(restMethodMetadata));
+        }
+
+        Comparator<String> comparator = new Comparator<String>() {
+            @Override
+            public int compare(String o1, String o2) {
+                return o1.length() - o2.length();
+            }
+        };
+        jsons.sort(comparator);
+        jsonsTmp.sort(comparator);
+
+        for (int i = 0; i < jsons.size(); i++) {
+            assertEquals(jsons.get(i), jsonsTmp.get(i));
+        }
+    }
+
+    @Test
+    void testDoubleCheck() {
+
+        ServiceRestMetadata springRestMetadata = new ServiceRestMetadata();
+        springRestMetadata.setServiceInterface(SpringRestServiceImpl.class.getName());
+        ServiceRestMetadata springMetadata = instance.resolve(SpringRestServiceImpl.class, springRestMetadata);
+
+        springMetadata.setContextPathFromUrl("context");
+
+        Assertions.assertEquals("context", springMetadata.getContextPathFromUrl());
+
+        springMetadata.setContextPathFromUrl("//context");
+        Assertions.assertEquals("/context", springMetadata.getContextPathFromUrl());
+        springMetadata.setPort(404);
+        Map<PathMatcher, RestMethodMetadata> pathContainPathVariableToServiceMap =
+                springMetadata.getPathContainPathVariableToServiceMap();
+
+        for (PathMatcher pathMatcher : pathContainPathVariableToServiceMap.keySet()) {
+            Assertions.assertTrue(pathMatcher.hasPathVariable());
+            Assertions.assertEquals(404, pathMatcher.getPort());
+        }
+
+        Map<PathMatcher, RestMethodMetadata> pathUnContainPathVariableToServiceMap =
+                springMetadata.getPathUnContainPathVariableToServiceMap();
+
+        for (PathMatcher pathMatcher : pathUnContainPathVariableToServiceMap.keySet()) {
+            Assertions.assertFalse(pathMatcher.hasPathVariable());
+            Assertions.assertEquals(404, pathMatcher.getPort());
+        }
+    }
+}
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
new file mode 100644
index 0000000..2354eff
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.metadata.report.MetadataReportFactory
@@ -0,0 +1 @@
+JTest=org.apache.dubbo.metadata.test.JTestMetadataReportFactory4Test
\ No newline at end of file
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/jax-rs-service-rest-metadata.json b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/jax-rs-service-rest-metadata.json
new file mode 100644
index 0000000..4f2ec00
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/jax-rs-service-rest-metadata.json
@@ -0,0 +1,324 @@
+[
+  {
+    "serviceInterface": "org.apache.dubbo.metadata.extension.rest.RestService",
+    "version": "3.0.0",
+    "group": "standard",
+    "meta": [
+      {
+        "method": {
+          "name": "form",
+          "parameterTypes": [
+            "java.lang.String"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "POST",
+          "path": "/form",
+          "params": {
+            "f": [
+              "{0}"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "form"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "headers",
+          "parameterTypes": [
+            "java.lang.String",
+            "java.lang.String",
+            "java.lang.Integer"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.Integer",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "GET",
+          "path": "/headers",
+          "params": {
+            "v": [
+              "{2}"
+            ]
+          },
+          "headers": {
+            "h": [
+              "{0}"
+            ],
+            "h2": [
+              "{1}"
+            ]
+          },
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "header"
+          ],
+          "1": [
+            "header2"
+          ],
+          "2": [
+            "param"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "param",
+          "parameterTypes": [
+            "java.lang.String"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "GET",
+          "path": "/param",
+          "params": {
+            "param": [
+              "{0}"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "param"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "params",
+          "parameterTypes": [
+            "int",
+            "java.lang.String"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "int",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "POST",
+          "path": "/params",
+          "params": {
+            "a": [
+              "{0}"
+            ],
+            "b": [
+              "{1}"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "a"
+          ],
+          "1": [
+            "b"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "pathVariables",
+          "parameterTypes": [
+            "java.lang.String",
+            "java.lang.String",
+            "java.lang.String"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "GET",
+          "path": "/path-variables/{p1}/{p2}",
+          "params": {
+            "v": [
+              "{2}"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "path1"
+          ],
+          "1": [
+            "path2"
+          ],
+          "2": [
+            "param"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "requestBodyMap",
+          "parameterTypes": [
+            "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e",
+            "java.lang.String"
+          ],
+          "returnType": "org.apache.dubbo.metadata.extension.rest.User",
+          "parameters": [
+            {
+              "type": "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e",
+              "items": [
+                "java.lang.String",
+                "java.lang.Object"
+              ],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "POST",
+          "path": "/request/body/map",
+          "params": {
+            "param": [
+              "{1}"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": [
+            "application/json;charset\u003dUTF-8"
+          ]
+        },
+        "indexToName": {
+          "0": [
+            "data"
+          ],
+          "1": [
+            "param"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "requestBodyUser",
+          "parameterTypes": [
+            "org.apache.dubbo.metadata.extension.rest.User"
+          ],
+          "returnType": "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e",
+          "parameters": [
+            {
+              "type": "org.apache.dubbo.metadata.extension.rest.User",
+              "items": [],
+              "enum": [],
+              "properties": {
+                "name": "java.lang.String",
+                "id": "java.lang.Long",
+                "age": "java.lang.Integer"
+              }
+            }
+          ]
+        },
+        "request": {
+          "method": "POST",
+          "path": "/request/body/user",
+          "params": {},
+          "headers": {},
+          "consumes": [
+            "application/json;charset\u003dUTF-8"
+          ],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "user"
+          ]
+        }
+      }
+    ]
+  }
+]
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/service-name-mapping.properties b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/service-name-mapping.properties
new file mode 100644
index 0000000..0ae294e
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/service-name-mapping.properties
@@ -0,0 +1,3 @@
+dubbo\:com.acme.Interface1\:default = Service1
+thirft\:com.acme.InterfaceX = Service1,Service2
+rest\:com.acme.interfaceN = Service3
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/spring-mvc-service-rest-metadata.json b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/spring-mvc-service-rest-metadata.json
new file mode 100644
index 0000000..93e814e
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/META-INF/dubbo/spring-mvc-service-rest-metadata.json
@@ -0,0 +1,321 @@
+[
+  {
+    "serviceInterface": "org.apache.dubbo.metadata.extension.rest.RestService",
+    "version": "2.0.0",
+    "group": "spring",
+    "meta": [
+      {
+        "method": {
+          "name": "form",
+          "parameterTypes": [
+            "java.lang.String"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "POST",
+          "path": "/form",
+          "params": {
+            "f": [
+              "{0}"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "form"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "headers",
+          "parameterTypes": [
+            "java.lang.String",
+            "java.lang.String",
+            "java.lang.Integer"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.Integer",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "GET",
+          "path": "/headers",
+          "params": {
+            "v": [
+              "1"
+            ]
+          },
+          "headers": {
+            "h": [
+              "value-h"
+            ],
+            "h2": [
+              "value-h2"
+            ]
+          },
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "header"
+          ],
+          "1": [
+            "header2"
+          ],
+          "2": [
+            "param"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "param",
+          "parameterTypes": [
+            "java.lang.String"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "GET",
+          "path": "/param",
+          "params": {
+            "param": [
+              "value-param"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "param"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "params",
+          "parameterTypes": [
+            "int",
+            "java.lang.String"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "int",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "POST",
+          "path": "/params",
+          "params": {
+            "a": [
+              "value-a"
+            ],
+            "b": [
+              "value-b"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "a"
+          ],
+          "1": [
+            "b"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "pathVariables",
+          "parameterTypes": [
+            "java.lang.String",
+            "java.lang.String",
+            "java.lang.String"
+          ],
+          "returnType": "java.lang.String",
+          "parameters": [
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "GET",
+          "path": "/path-variables/{p1}/{p2}",
+          "params": {
+            "v": [
+              "{2}"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "path1"
+          ],
+          "1": [
+            "path2"
+          ],
+          "2": [
+            "param"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "requestBodyMap",
+          "parameterTypes": [
+            "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e",
+            "java.lang.String"
+          ],
+          "returnType": "org.apache.dubbo.metadata.extension.rest.User",
+          "parameters": [
+            {
+              "type": "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e",
+              "items": ["java.lang.String", "java.lang.Object"],
+              "enum": [],
+              "properties": {}
+            },
+            {
+              "type": "java.lang.String",
+              "items": [],
+              "enum": [],
+              "properties": {}
+            }
+          ]
+        },
+        "request": {
+          "method": "POST",
+          "path": "/request/body/map",
+          "params": {
+            "param": [
+              "{1}"
+            ]
+          },
+          "headers": {},
+          "consumes": [],
+          "produces": [
+            "application/json;charset\u003dUTF-8"
+          ]
+        },
+        "indexToName": {
+          "0": [
+            "data"
+          ],
+          "1": [
+            "param"
+          ]
+        }
+      },
+      {
+        "method": {
+          "name": "requestBodyUser",
+          "parameterTypes": [
+            "org.apache.dubbo.metadata.extension.rest.User"
+          ],
+          "returnType": "java.util.Map\u003cjava.lang.String,java.lang.Object\u003e",
+          "parameters": [
+            {
+              "type": "org.apache.dubbo.metadata.extension.rest.User",
+              "items": [],
+              "enum": [],
+              "properties": {
+                "name": "java.lang.String",
+                "id": "java.lang.Long",
+                "age": "java.lang.Integer"
+              }
+            }
+          ]
+        },
+        "request": {
+          "method": "POST",
+          "path": "/request/body/user",
+          "params": {},
+          "headers": {},
+          "consumes": [
+            "application/json;charset\u003dUTF-8"
+          ],
+          "produces": []
+        },
+        "indexToName": {
+          "0": [
+            "user"
+          ]
+        }
+      }
+    ]
+  }
+]
diff --git a/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/dubbo.properties b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/dubbo.properties
new file mode 100644
index 0000000..1aade88
--- /dev/null
+++ b/dubbo-metadata-report-extensions/dubbo-metadata-rest/src/test/resources/dubbo.properties
@@ -0,0 +1,2 @@
+dubbo.application.enable-file-cache=false
+dubbo.service.shutdown.wait=200
diff --git a/dubbo-metadata-report-extensions/pom.xml b/dubbo-metadata-report-extensions/pom.xml
index bdce3c7..2414b42 100644
--- a/dubbo-metadata-report-extensions/pom.xml
+++ b/dubbo-metadata-report-extensions/pom.xml
@@ -33,5 +33,6 @@
     <modules>
         <module>dubbo-metadata-report-consul</module>
         <module>dubbo-metadata-report-etcd</module>
+        <module>dubbo-metadata-rest</module>
     </modules>
 </project>
diff --git a/dubbo-mock-extensions/dubbo-mock-admin/pom.xml b/dubbo-mock-extensions/dubbo-mock-admin/pom.xml
index ed61404..5438ce0 100644
--- a/dubbo-mock-extensions/dubbo-mock-admin/pom.xml
+++ b/dubbo-mock-extensions/dubbo-mock-admin/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>dubbo-mock-extensions</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>3.0.6-SNAPSHOT</version>
+        <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
@@ -31,7 +31,7 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-mock-api</artifactId>
-            <version>3.0.6-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
 
         <dependency>
diff --git a/dubbo-mock-extensions/dubbo-mock-admin/src/main/java/org/apache/dubbo/mock/handler/JsonTypeHandler.java b/dubbo-mock-extensions/dubbo-mock-admin/src/main/java/org/apache/dubbo/mock/handler/JsonTypeHandler.java
index fc4be09..a4a833b 100644
--- a/dubbo-mock-extensions/dubbo-mock-admin/src/main/java/org/apache/dubbo/mock/handler/JsonTypeHandler.java
+++ b/dubbo-mock-extensions/dubbo-mock-admin/src/main/java/org/apache/dubbo/mock/handler/JsonTypeHandler.java
@@ -17,9 +17,9 @@
 
 package org.apache.dubbo.mock.handler;
 
+import com.alibaba.fastjson2.JSON;
 import org.apache.dubbo.mock.exception.HandleFailException;
 
-import com.google.gson.Gson;
 
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
@@ -27,14 +27,12 @@
 import java.util.Objects;
 
 /**
- * handle the Json data. mainly work with {@link Gson}.
+ * handle the Json data. mainly work with {@link JSON}.
  */
 public class JsonTypeHandler implements TypeHandler<Object> {
 
-    private Gson gson;
 
     public JsonTypeHandler() {
-        gson = new Gson();
     }
 
     @Override
@@ -56,10 +54,10 @@
                 // for generic type parse
                 Type genericReturnType = method.getGenericReturnType();
                 if (genericReturnType instanceof ParameterizedType) {
-                    return gson.fromJson(resultContext.getData(), genericReturnType);
+                    return JSON.parseObject(resultContext.getData(), genericReturnType);
                 }
             }
-            return gson.fromJson(resultContext.getData(), targetType);
+            return JSON.parseObject(resultContext.getData(), targetType);
         } catch (Exception e) {
             throw new HandleFailException(e);
         }
diff --git a/dubbo-mock-extensions/dubbo-mock-api/pom.xml b/dubbo-mock-extensions/dubbo-mock-api/pom.xml
index d63e191..9d6196a 100644
--- a/dubbo-mock-extensions/dubbo-mock-api/pom.xml
+++ b/dubbo-mock-extensions/dubbo-mock-api/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>dubbo-mock-extensions</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>3.0.6-SNAPSHOT</version>
+        <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
diff --git a/dubbo-mock-extensions/pom.xml b/dubbo-mock-extensions/pom.xml
index ef926b6..17903cf 100644
--- a/dubbo-mock-extensions/pom.xml
+++ b/dubbo-mock-extensions/pom.xml
@@ -27,7 +27,7 @@
 
     <artifactId>dubbo-mock-extensions</artifactId>
     <packaging>pom</packaging>
-    <version>3.0.6-SNAPSHOT</version>
+    <version>${revision}</version>
     <modules>
         <module>dubbo-mock-api</module>
         <module>dubbo-mock-admin</module>
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/pom.xml b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/pom.xml
new file mode 100644
index 0000000..4675f7f
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/pom.xml
@@ -0,0 +1,54 @@
+<?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">
+    <parent>
+        <artifactId>dubbo-proxy-extensions</artifactId>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-proxy-bytebuddy</artifactId>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.dubbo</groupId>
+                <artifactId>dubbo-bom</artifactId>
+                <version>3.2.11</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>net.bytebuddy</groupId>
+            <artifactId>byte-buddy</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptor.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptor.java
new file mode 100644
index 0000000..2a579d9
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptor.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+public class ByteBuddyInterceptor {
+
+    private final InvocationHandler handler;
+
+    ByteBuddyInterceptor(InvocationHandler handler) {
+        this.handler = handler;
+    }
+
+    @RuntimeType
+    public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Origin Method method)
+            throws Throwable {
+        return handler.invoke(obj, method, allArguments);
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxy.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxy.java
new file mode 100644
index 0000000..a8f1827
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxy.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.description.ByteCodeElement;
+import net.bytebuddy.implementation.MethodDelegation;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.dubbo.common.constants.CommonConstants.MAX_PROXY_COUNT;
+
+public class ByteBuddyProxy {
+
+    private static final Map<ClassLoader, Map<CacheKey, ByteBuddyProxy>> PROXY_CACHE_MAP = new WeakHashMap<>();
+
+    private final Class<?> proxyClass;
+
+    private final InvocationHandler handler;
+
+    private ByteBuddyProxy(Class<?> proxyClass, InvocationHandler handler) {
+        this.proxyClass = proxyClass;
+        this.handler = handler;
+    }
+
+    public static Object newInstance(ClassLoader cl, Class<?>[] interfaces, InvocationHandler handler) {
+        return getProxy(cl, interfaces, handler).newInstance();
+    }
+
+    private static ByteBuddyProxy getProxy(ClassLoader cl, Class<?>[] interfaces, InvocationHandler handler) {
+        if (interfaces.length > MAX_PROXY_COUNT) {
+            throw new IllegalArgumentException("interface limit exceeded");
+        }
+        interfaces = interfaces.clone();
+        Arrays.sort(interfaces, Comparator.comparing(Class::getName));
+        CacheKey key = new CacheKey(interfaces);
+        // get cache by class loader.
+        final Map<CacheKey, ByteBuddyProxy> cache;
+        synchronized (PROXY_CACHE_MAP) {
+            cache = PROXY_CACHE_MAP.computeIfAbsent(cl, k -> new ConcurrentHashMap<>());
+        }
+
+        ByteBuddyProxy proxy = cache.get(key);
+        if (proxy == null) {
+            synchronized (interfaces[0]) {
+                proxy = cache.get(key);
+                if (proxy == null) {
+                    // create ByteBuddyProxy class.
+                    proxy = new ByteBuddyProxy(buildProxyClass(cl, interfaces, handler), handler);
+                    cache.put(key, proxy);
+                }
+            }
+        }
+        return proxy;
+    }
+
+    private Object newInstance() {
+        try {
+            Constructor<?> constructor = proxyClass.getDeclaredConstructor(InvocationHandler.class);
+            return constructor.newInstance(handler);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Class<?> buildProxyClass(ClassLoader cl, Class<?>[] ics, InvocationHandler handler) {
+        ElementMatcher.Junction<ByteCodeElement> methodMatcher = Arrays.stream(ics)
+                .map(ElementMatchers::isDeclaredBy)
+                .reduce(ElementMatcher.Junction::or)
+                .orElse(ElementMatchers.none())
+                .and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class)));
+        return new ByteBuddy()
+                .subclass(Proxy.class)
+                .implement(ics)
+                .method(methodMatcher)
+                .intercept(MethodDelegation.to(new ByteBuddyInterceptor(handler)))
+                .make()
+                .load(cl)
+                .getLoaded();
+    }
+
+    private static class CacheKey {
+
+        private final Class<?>[] classes;
+
+        private CacheKey(Class<?>[] classes) {
+            this.classes = classes;
+        }
+
+        public Class<?>[] getClasses() {
+            return classes;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            CacheKey that = (CacheKey) o;
+            return Arrays.equals(classes, that.classes);
+        }
+
+        @Override
+        public int hashCode() {
+            return Arrays.hashCode(classes);
+        }
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactory.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactory.java
new file mode 100644
index 0000000..7f34686
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.proxy.AbstractFallbackJdkProxyFactory;
+import org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;
+
+/**
+ * ByteBuddyRpcProxyFactory
+ */
+public class ByteBuddyProxyFactory extends AbstractFallbackJdkProxyFactory {
+
+    @Override
+    protected <T> Invoker<T> doGetInvoker(T proxy, Class<T> type, URL url) {
+        return ByteBuddyProxyInvoker.newInstance(proxy, type, url);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected <T> T doGetProxy(Invoker<T> invoker, Class<?>[] interfaces) {
+        ClassLoader classLoader = invoker.getInterface().getClassLoader();
+        return (T) ByteBuddyProxy.newInstance(classLoader, interfaces, new InvokerInvocationHandler(invoker));
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvoker.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvoker.java
new file mode 100644
index 0000000..9fd1778
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvoker.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.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.proxy.AbstractProxyInvoker;
+import org.apache.dubbo.rpc.proxy.MethodInvoker;
+
+class ByteBuddyProxyInvoker<T> extends AbstractProxyInvoker<T> {
+
+    private final MethodInvoker methodInvoker;
+
+    private ByteBuddyProxyInvoker(T proxy, Class<T> type, URL url, MethodInvoker methodInvoker) {
+        super(proxy, type, url);
+        this.methodInvoker = methodInvoker;
+    }
+
+    @Override
+    protected Object doInvoke(T instance, String methodName, Class<?>[] parameterTypes, Object[] arguments)
+            throws Throwable {
+        if ("getClass".equals(methodName)) {
+            return instance.getClass();
+        }
+        if ("hashCode".equals(methodName)) {
+            return instance.hashCode();
+        }
+        if ("toString".equals(methodName)) {
+            return instance.toString();
+        }
+        if ("equals".equals(methodName)) {
+            if (arguments.length == 1) {
+                return instance.equals(arguments[0]);
+            }
+            throw new IllegalArgumentException("Invoke method [" + methodName + "] argument number error.");
+        }
+        return methodInvoker.invoke(instance, methodName, parameterTypes, arguments);
+    }
+
+    static <T> ByteBuddyProxyInvoker<T> newInstance(T proxy, Class<T> type, URL url) {
+        return new ByteBuddyProxyInvoker<>(proxy, type, url, MethodInvoker.newInstance(proxy.getClass()));
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory
new file mode 100644
index 0000000..54bdcb8
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory
@@ -0,0 +1 @@
+bytebuddy=org.apache.dubbo.rpc.proxy.bytebuddy.ByteBuddyProxyFactory
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/AbstractProxyTest.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/AbstractProxyTest.java
new file mode 100644
index 0000000..8f5d8c4
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/AbstractProxyTest.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.service.Destroyable;
+import org.apache.dubbo.rpc.service.EchoService;
+import org.apache.dubbo.rpc.support.DemoService;
+import org.apache.dubbo.rpc.support.DemoServiceImpl;
+import org.apache.dubbo.rpc.support.MyInvoker;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+public abstract class AbstractProxyTest {
+
+    public static ProxyFactory factory;
+
+    @Test
+    void testGetProxy() {
+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+
+        MyInvoker<DemoService> invoker = new MyInvoker<>(url);
+
+        DemoService proxy = factory.getProxy(invoker);
+
+        Assertions.assertNotNull(proxy);
+
+        Assertions.assertTrue(Arrays.asList(proxy.getClass().getInterfaces()).contains(DemoService.class));
+        Assertions.assertTrue(Arrays.asList(proxy.getClass().getInterfaces()).contains(Destroyable.class));
+        Assertions.assertTrue(Arrays.asList(proxy.getClass().getInterfaces()).contains(EchoService.class));
+
+        Assertions.assertEquals(
+                invoker.invoke(new RpcInvocation(
+                                "echo",
+                                DemoService.class.getName(),
+                                DemoService.class.getName() + ":dubbo",
+                                new Class[] {String.class},
+                                new Object[] {"aa"}))
+                        .getValue(),
+                proxy.echo("aa"));
+
+        Destroyable destroyable = (Destroyable) proxy;
+        destroyable.$destroy();
+        Assertions.assertTrue(invoker.isDestroyed());
+    }
+
+    @Test
+    void testGetInvoker() {
+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+
+        DemoService origin = new DemoServiceImpl();
+
+        Invoker<DemoService> invoker = factory.getInvoker(new DemoServiceImpl(), DemoService.class, url);
+
+        Assertions.assertEquals(invoker.getInterface(), DemoService.class);
+
+        Assertions.assertEquals(
+                invoker.invoke(new RpcInvocation(
+                                "echo",
+                                DemoService.class.getName(),
+                                DemoService.class.getName() + ":dubbo",
+                                new Class[] {String.class},
+                                new Object[] {"aa"}))
+                        .getValue(),
+                origin.echo("aa"));
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/RemoteService.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/RemoteService.java
new file mode 100644
index 0000000..1cf9878
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/RemoteService.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface RemoteService extends Remote {
+    String sayHello(String name) throws RemoteException;
+
+    String getThreadName() throws RemoteException;
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptorTest.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptorTest.java
new file mode 100644
index 0000000..49ce27e
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyInterceptorTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+class ByteBuddyInterceptorTest {
+
+    @AfterEach
+    public void after() {
+        Mockito.clearAllCaches();
+    }
+
+    @Test
+    void testIntercept() throws Throwable {
+        InvocationHandler handler = Mockito.mock(InvocationHandler.class);
+        ByteBuddyInterceptor interceptor = new ByteBuddyInterceptor(handler);
+        Method method = Mockito.mock(Method.class);
+        Proxy proxy = Mockito.mock(Proxy.class);
+        Object[] args = new Object[0];
+        interceptor.intercept(proxy, args, method);
+        // 'intercept' method will call 'invoke' method directly
+        Mockito.verify(handler, Mockito.times(1)).invoke(proxy, method, args);
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactoryTest.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactoryTest.java
new file mode 100644
index 0000000..776dfa3
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyFactoryTest.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.rpc.proxy.AbstractProxyTest;
+
+class ByteBuddyProxyFactoryTest extends AbstractProxyTest {
+
+    static {
+        AbstractProxyTest.factory = new ByteBuddyProxyFactory();
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvokerTest.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvokerTest.java
new file mode 100644
index 0000000..6ff3121
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyInvokerTest.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.proxy.RemoteService;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+class ByteBuddyProxyInvokerTest {
+
+    @Test
+    void testNewInstance() throws Throwable {
+        URL url = URL.valueOf("test://test:11/test?group=dubbo&version=1.1");
+        RemoteService proxy = Mockito.mock(RemoteService.class);
+        ByteBuddyProxyInvoker<RemoteService> invoker =
+                ByteBuddyProxyInvoker.newInstance(proxy, RemoteService.class, url);
+        invoker.doInvoke(proxy, "sayHello", new Class[] {String.class}, new Object[] {"test"});
+        Mockito.verify(proxy, Mockito.times(1)).sayHello("test");
+
+        Assertions.assertThrows(
+                IllegalArgumentException.class,
+                () -> invoker.doInvoke(proxy, "equals", new Class[] {String.class}, new Object[] {"test", "test2"}));
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyTest.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyTest.java
new file mode 100644
index 0000000..f7f298e
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/proxy/bytebuddy/ByteBuddyProxyTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.proxy.bytebuddy;
+
+import org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;
+import org.apache.dubbo.rpc.proxy.RemoteService;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Proxy;
+
+import static org.mockito.ArgumentMatchers.any;
+
+class ByteBuddyProxyTest {
+
+    @Test
+    void testNewInstance() throws Throwable {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        InvokerInvocationHandler handler = Mockito.mock(InvokerInvocationHandler.class);
+        Object proxy = ByteBuddyProxy.newInstance(cl, new Class<?>[]{RemoteService.class}, handler);
+        Assertions.assertTrue(proxy instanceof RemoteService);
+        Assertions.assertTrue(proxy instanceof Proxy);
+        RemoteService remoteService = (RemoteService) proxy;
+        remoteService.getThreadName();
+        remoteService.sayHello("test");
+        Mockito.verify(handler, Mockito.times(2)).invoke(any(), any(), any());
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/support/DemoService.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/support/DemoService.java
new file mode 100644
index 0000000..8bc9395
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/support/DemoService.java
@@ -0,0 +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
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.support;
+
+public interface DemoService {
+    String echo(String text);
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/support/DemoServiceImpl.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/support/DemoServiceImpl.java
new file mode 100644
index 0000000..90fd7b1
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/support/DemoServiceImpl.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.support;
+
+/**
+ * DemoServiceImpl
+ */
+public class DemoServiceImpl implements DemoService {
+    public String echo(String text) {
+        return text;
+    }
+}
diff --git a/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/support/MyInvoker.java b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/support/MyInvoker.java
new file mode 100644
index 0000000..670a3f7
--- /dev/null
+++ b/dubbo-proxy-extensions/dubbo-proxy-bytebuddy/src/test/java/org/apache/dubbo/rpc/support/MyInvoker.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.support;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * MockInvoker.java
+ */
+public class MyInvoker<T> implements Invoker<T> {
+
+    URL url;
+    Class<T> type;
+    boolean hasException = false;
+    boolean destroyed = false;
+
+    public MyInvoker(URL url) {
+        this.url = url;
+        type = (Class<T>) DemoService.class;
+    }
+
+    public MyInvoker(URL url, boolean hasException) {
+        this.url = url;
+        type = (Class<T>) DemoService.class;
+        this.hasException = hasException;
+    }
+
+    @Override
+    public Class<T> getInterface() {
+        return type;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return false;
+    }
+
+    @Override
+    public Result invoke(Invocation invocation) throws RpcException {
+        AppResponse result = new AppResponse();
+        if (!hasException) {
+            result.setValue("alibaba");
+        } else {
+            result.setException(new RuntimeException("mocked exception"));
+        }
+
+        return new AsyncRpcResult(CompletableFuture.completedFuture(result), invocation);
+    }
+
+    @Override
+    public void destroy() {
+        destroyed = true;
+    }
+
+    public boolean isDestroyed() {
+        return destroyed;
+    }
+
+    @Override
+    public String toString() {
+        return "MyInvoker.toString()";
+    }
+}
diff --git a/dubbo-proxy-extensions/pom.xml b/dubbo-proxy-extensions/pom.xml
new file mode 100644
index 0000000..a2f8ce7
--- /dev/null
+++ b/dubbo-proxy-extensions/pom.xml
@@ -0,0 +1,36 @@
+<?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">
+    <parent>
+        <artifactId>extensions-parent</artifactId>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-proxy-extensions</artifactId>
+    <version>${revision}</version>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>dubbo-proxy-bytebuddy</module>
+    </modules>
+</project>
diff --git a/dubbo-registry-extensions/dubbo-registry-consul/pom.xml b/dubbo-registry-extensions/dubbo-registry-consul/pom.xml
index cd38639..0b46a79 100644
--- a/dubbo-registry-extensions/dubbo-registry-consul/pom.xml
+++ b/dubbo-registry-extensions/dubbo-registry-consul/pom.xml
@@ -25,7 +25,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-registry-consul</artifactId>
 
     <properties>
@@ -36,13 +36,11 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-registry-api</artifactId>
-            <version>${dubbo3.version}</version>
             <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>${dubbo3.version}</version>
             <optional>true</optional>
         </dependency>
         <dependency>
diff --git a/dubbo-registry-extensions/dubbo-registry-dns/pom.xml b/dubbo-registry-extensions/dubbo-registry-dns/pom.xml
index afa6592..f3d528a 100644
--- a/dubbo-registry-extensions/dubbo-registry-dns/pom.xml
+++ b/dubbo-registry-extensions/dubbo-registry-dns/pom.xml
@@ -29,7 +29,7 @@
     <artifactId>dubbo-registry-dns</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The DNS registry module of Dubbo project</description>
 
     <dependencies>
diff --git a/dubbo-registry-extensions/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscovery.java b/dubbo-registry-extensions/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscovery.java
index 3c26786..72224ba 100644
--- a/dubbo-registry-extensions/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscovery.java
+++ b/dubbo-registry-extensions/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscovery.java
@@ -42,6 +42,7 @@
 import java.util.concurrent.TimeUnit;
 
 public class DNSServiceDiscovery extends ReflectionBasedServiceDiscovery {
+
     private static final Logger logger = LoggerFactory.getLogger(DNSServiceDiscovery.class);
 
     /**
@@ -111,7 +112,13 @@
     public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
         listener.getServiceNames().forEach(serviceName -> {
             ScheduledFuture<?> scheduledFuture = pollingExecutorService.scheduleAtFixedRate(() -> {
-                    List<ServiceInstance> instances = getInstances(serviceName);
+                    List<ServiceInstance> instances;
+                    try {
+                        instances = getInstances(serviceName);
+                    } catch (Throwable throwable) {
+                        logger.error("Failed to get instances for " + serviceName, throwable);
+                        return;
+                    }
                     instances.sort(Comparator.comparingInt(ServiceInstance::hashCode));
                     notifyListener(serviceName, listener, instances);
                 },
@@ -133,7 +140,7 @@
 
         int port;
 
-        if (resolveResult.getPort().size() > 0) {
+        if (!resolveResult.getPort().isEmpty()) {
             // use first as default
             port = resolveResult.getPort().get(0);
         } else {
diff --git a/dubbo-registry-extensions/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSResolver.java b/dubbo-registry-extensions/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSResolver.java
index eb520d2..0bbd866 100644
--- a/dubbo-registry-extensions/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSResolver.java
+++ b/dubbo-registry-extensions/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSResolver.java
@@ -81,6 +81,7 @@
                     // Port
                     int port = buf.readUnsignedShort();
                     recordList.getPort().add(port);
+                    buf.release();
                 }
 
             } catch (InterruptedException e) {
diff --git a/dubbo-registry-extensions/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/DNSResolverTest.java b/dubbo-registry-extensions/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/DNSResolverTest.java
new file mode 100644
index 0000000..d3911a4
--- /dev/null
+++ b/dubbo-registry-extensions/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/DNSResolverTest.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.registry.dns;
+
+import org.apache.dubbo.registry.dns.util.DNSResolver;
+import org.apache.dubbo.registry.dns.util.ResolveResult;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class DNSResolverTest {
+
+    @Test
+    public void testResolve() {
+        DNSResolver dnsResolver = new DNSResolver("8.8.8.8", 53, 1);
+        ResolveResult resolve = dnsResolver.resolve("dubbo.apache.org");
+        Assertions.assertFalse(resolve.getHostnameList().isEmpty());
+    }
+}
diff --git a/dubbo-registry-extensions/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/util/DNSResolverTest.java b/dubbo-registry-extensions/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/util/DNSResolverTest.java
deleted file mode 100644
index 17088ac..0000000
--- a/dubbo-registry-extensions/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/util/DNSResolverTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.registry.dns.util;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-public class DNSResolverTest {
-
-    @Test
-    public void testResolve() {
-        DNSResolver dnsResolver = new DNSResolver("8.8.8.8", 53, 1);
-        ResolveResult resolve = dnsResolver.resolve("aliyun.com");
-        Assertions.assertTrue(resolve.getHostnameList().size() > 0);
-    }
-}
diff --git a/dubbo-registry-extensions/dubbo-registry-etcd3/pom.xml b/dubbo-registry-extensions/dubbo-registry-etcd3/pom.xml
index 2aefc98..d0c5638 100644
--- a/dubbo-registry-extensions/dubbo-registry-etcd3/pom.xml
+++ b/dubbo-registry-extensions/dubbo-registry-etcd3/pom.xml
@@ -25,7 +25,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-registry-etcd3</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
@@ -36,14 +36,12 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-registry-api</artifactId>
-            <version>3.2.7</version>
             <optional>true</optional>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>3.2.7</version>
             <optional>true</optional>
         </dependency>
 
@@ -56,7 +54,7 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-remoting-etcd3</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
     </dependencies>
 
diff --git a/dubbo-registry-extensions/dubbo-registry-nameservice/pom.xml b/dubbo-registry-extensions/dubbo-registry-nameservice/pom.xml
index deeaa90..bae0e98 100644
--- a/dubbo-registry-extensions/dubbo-registry-nameservice/pom.xml
+++ b/dubbo-registry-extensions/dubbo-registry-nameservice/pom.xml
@@ -25,7 +25,7 @@
         <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
-    <version>1.0.1-SNAPSHOT</version>
+    <version>${revision}</version>
 	<modelVersion>4.0.0</modelVersion>
 	<artifactId>dubbo-registry-nameservice</artifactId>
 	<name>dubbo-registry-nameservice</name>
diff --git a/dubbo-registry-extensions/dubbo-registry-nameservice/src/test/java/org/apache/dubbo/registry/nameservice/NameServiceRegistryTest.java b/dubbo-registry-extensions/dubbo-registry-nameservice/src/test/java/org/apache/dubbo/registry/nameservice/NameServiceRegistryTest.java
index 1c93536..1998573 100644
--- a/dubbo-registry-extensions/dubbo-registry-nameservice/src/test/java/org/apache/dubbo/registry/nameservice/NameServiceRegistryTest.java
+++ b/dubbo-registry-extensions/dubbo-registry-nameservice/src/test/java/org/apache/dubbo/registry/nameservice/NameServiceRegistryTest.java
@@ -136,16 +136,16 @@
     @Test
     public void runnableTest() throws Exception {
         PowerMockito.doAnswer(new Answer<Object>() {
-            @Override
-            public Object answer(InvocationOnMock invocation) throws Throwable {
-                Object[] arguments = invocation.getArguments();
-                Assert.assertNotNull(arguments[0]);
-                Assert.assertEquals(arguments[1], 10000L);
-                Assert.assertEquals(arguments[2], 30000L);
-                Assert.assertEquals(arguments[3], TimeUnit.MILLISECONDS);
-                return null;
-            }
-        }).when(scheduledExecutorService)
+                @Override
+                public Object answer(InvocationOnMock invocation) throws Throwable {
+                    Object[] arguments = invocation.getArguments();
+                    Assert.assertNotNull(arguments[0]);
+                    Assert.assertEquals(arguments[1], 10000L);
+                    Assert.assertEquals(arguments[2], 30000L);
+                    Assert.assertEquals(arguments[3], TimeUnit.MILLISECONDS);
+                    return null;
+                }
+            }).when(scheduledExecutorService)
             .scheduleAtFixedRate(Mockito.any(Runnable.class)
                 , Mockito.any(Long.class)
                 , Mockito.any(Long.class)
@@ -159,7 +159,7 @@
 
         Map<URL, Object> consumerRegistryInfoWrapperMap = ReflectUtils.getFieldValue(nameServiceRegistry, "consumerRegistryInfoWrapperMap");
 
-        Constructor<?> constructor = registryInfoWrapper.getConstructor(new Class<?>[] {NameServiceRegistry.class});
+        Constructor<?> constructor = registryInfoWrapper.getConstructor(new Class<?>[]{NameServiceRegistry.class});
         constructor.setAccessible(true);
         Object wrapper = constructor.newInstance(nameServiceRegistry);
         NotifyListener notifyListener = PowerMockito.mock(NotifyListener.class);
diff --git a/dubbo-registry-extensions/dubbo-registry-polaris/pom.xml b/dubbo-registry-extensions/dubbo-registry-polaris/pom.xml
index ab37000..134730a 100644
--- a/dubbo-registry-extensions/dubbo-registry-polaris/pom.xml
+++ b/dubbo-registry-extensions/dubbo-registry-polaris/pom.xml
@@ -25,7 +25,7 @@
 
     <artifactId>dubbo-registry-polaris</artifactId>
     <name>dubbo-registry-polaris</name>
-    <version>1.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>Dubbo registry extension for PolarisMesh, support instance register, discover, health-check capabilities.</description>
 
     <dependencies>
diff --git a/dubbo-registry-extensions/dubbo-registry-redis/pom.xml b/dubbo-registry-extensions/dubbo-registry-redis/pom.xml
index d7095b8..acf1201 100644
--- a/dubbo-registry-extensions/dubbo-registry-redis/pom.xml
+++ b/dubbo-registry-extensions/dubbo-registry-redis/pom.xml
@@ -23,7 +23,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-registry-redis</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
@@ -35,13 +35,12 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-registry-api</artifactId>
-            <version>${dubbo3.version}</version>
             <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-remoting-redis</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
         <dependency>
             <groupId>redis.clients</groupId>
diff --git a/dubbo-registry-extensions/dubbo-registry-redis/src/test/java/org/apache/dubbo/registry/redis/RedisRegistryTest.java b/dubbo-registry-extensions/dubbo-registry-redis/src/test/java/org/apache/dubbo/registry/redis/RedisRegistryTest.java
index 303971d..45ac89d 100644
--- a/dubbo-registry-extensions/dubbo-registry-redis/src/test/java/org/apache/dubbo/registry/redis/RedisRegistryTest.java
+++ b/dubbo-registry-extensions/dubbo-registry-redis/src/test/java/org/apache/dubbo/registry/redis/RedisRegistryTest.java
@@ -16,18 +16,19 @@
  */
 package org.apache.dubbo.registry.redis;
 
+import org.apache.commons.lang3.SystemUtils;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.registry.support.AbstractRegistry;
-
-import org.apache.commons.lang3.SystemUtils;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import redis.clients.jedis.exceptions.JedisConnectionException;
+import redis.clients.jedis.exceptions.JedisExhaustedPoolException;
 import redis.embedded.RedisServer;
 
 import java.io.IOException;
@@ -235,12 +236,13 @@
     }
 
     @Test
+    @Disabled
     public void testAvailableWithBackup() {
         URL url = URL.valueOf("redis://redisOne:8880").addParameter(BACKUP_KEY, "redisTwo:8881");
         Registry registry = new RedisRegistryFactory().createRegistry(url);
 
         Registry finalRegistry = registry;
-        assertThrows(JedisConnectionException.class, () -> finalRegistry.isAvailable());
+        assertThrows(JedisExhaustedPoolException.class, () -> finalRegistry.isAvailable());
 
         url = URL.valueOf(this.registryUrl.toFullString()).addParameter(BACKUP_KEY, "redisTwo:8881");
         registry = new RedisRegistryFactory().createRegistry(url);
diff --git a/dubbo-registry-extensions/dubbo-registry-sofa/pom.xml b/dubbo-registry-extensions/dubbo-registry-sofa/pom.xml
index 5d9486b..33f1725 100644
--- a/dubbo-registry-extensions/dubbo-registry-sofa/pom.xml
+++ b/dubbo-registry-extensions/dubbo-registry-sofa/pom.xml
@@ -24,7 +24,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-registry-sofa</artifactId>
     <name>${project.artifactId}</name>
     <description>The SOFARegistry module of Dubbo project</description>
@@ -40,13 +40,13 @@
             <artifactId>dubbo-registry-api</artifactId>
             <optional>true</optional>
         </dependency>
-
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
             <optional>true</optional>
         </dependency>
 
+
         <dependency>
             <groupId>com.alipay.sofa</groupId>
             <artifactId>registry-client-all</artifactId>
diff --git a/dubbo-registry-extensions/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistryServiceDiscovery.java b/dubbo-registry-extensions/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistryServiceDiscovery.java
index 6867685..9928be8 100644
--- a/dubbo-registry-extensions/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistryServiceDiscovery.java
+++ b/dubbo-registry-extensions/dubbo-registry-sofa/src/main/java/org/apache/dubbo/registry/sofa/SofaRegistryServiceDiscovery.java
@@ -16,15 +16,6 @@
  */
 package org.apache.dubbo.registry.sofa;
 
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.logger.Logger;
-import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
-import org.apache.dubbo.registry.client.DefaultServiceInstance;
-import org.apache.dubbo.registry.client.ServiceInstance;
-import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
-import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
-import org.apache.dubbo.rpc.model.ApplicationModel;
 
 import com.alipay.sofa.registry.client.api.Publisher;
 import com.alipay.sofa.registry.client.api.RegistryClientConfig;
@@ -36,7 +27,16 @@
 import com.alipay.sofa.registry.client.provider.DefaultRegistryClient;
 import com.alipay.sofa.registry.client.provider.DefaultRegistryClientConfigBuilder;
 import com.alipay.sofa.registry.core.model.ScopeEnum;
-import com.google.gson.Gson;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.JsonUtils;
+import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -70,7 +70,6 @@
     private final Map<String, SofaRegistryListener> sofaRegistryListenerMap = new ConcurrentHashMap<>();
 
 
-    private Gson gson = new Gson();
 
     public SofaRegistryServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
         super(applicationModel, registryURL);
@@ -105,11 +104,11 @@
         if (null == publisher) {
             PublisherRegistration registration = new PublisherRegistration(serviceInstance.getServiceName());
             registration.setGroup(DEFAULT_GROUP);
-            publisher = registryClient.register(registration, gson.toJson(sofaRegistryInstance));
+            publisher = registryClient.register(registration, JsonUtils.toJson(sofaRegistryInstance));
 
             publishers.put(serviceInstance.getServiceName(), publisher);
         } else {
-            publisher.republish(gson.toJson(sofaRegistryInstance));
+            publisher.republish(JsonUtils.toJson(sofaRegistryInstance));
         }
     }
 
@@ -202,7 +201,7 @@
                 List<ServiceInstance> newServiceInstances = new ArrayList<>(datas.size());
 
                 for (String serviceData : datas) {
-                    SofaRegistryInstance sri = gson.fromJson(serviceData, SofaRegistryInstance.class);
+                    SofaRegistryInstance sri = JsonUtils.toJavaObject(serviceData, SofaRegistryInstance.class);
 
                     DefaultServiceInstance serviceInstance = new DefaultServiceInstance(dataId, sri.getHost(), sri.getPort(), applicationModel);
                     serviceInstance.setMetadata(sri.getMetadata());
diff --git a/dubbo-registry-extensions/pom.xml b/dubbo-registry-extensions/pom.xml
index 82c68b0..48a11b9 100644
--- a/dubbo-registry-extensions/pom.xml
+++ b/dubbo-registry-extensions/pom.xml
@@ -32,7 +32,6 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
-        <dubbo3.version>3.2.9</dubbo3.version>
     </properties>
 
     <modules>
diff --git a/dubbo-remoting-extensions/dubbo-remoting-etcd3/pom.xml b/dubbo-remoting-extensions/dubbo-remoting-etcd3/pom.xml
index ea60079..b5c1b1b 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-etcd3/pom.xml
+++ b/dubbo-remoting-extensions/dubbo-remoting-etcd3/pom.xml
@@ -27,7 +27,7 @@
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-remoting-etcd3</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
@@ -42,7 +42,6 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-remoting-api</artifactId>
-            <version>3.2.0</version>
             <exclusions>
                 <exclusion>
                     <artifactId>dubbo-common</artifactId>
@@ -59,7 +58,6 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-serialization-api</artifactId>
-            <version>3.2.0</version>
             <exclusions>
                 <exclusion>
                     <artifactId>dubbo-common</artifactId>
@@ -72,7 +70,6 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>3.2.0</version>
             <optional>true</optional>
         </dependency>
         <dependency>
@@ -107,6 +104,12 @@
         </dependency>
 
         <dependency>
+            <groupId>io.etcd</groupId>
+            <artifactId>jetcd-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
         </dependency>
diff --git a/dubbo-remoting-extensions/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/LeaseTest.java b/dubbo-remoting-extensions/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/LeaseTest.java
index 6188984..2ddb361 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/LeaseTest.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-etcd3/src/test/java/org/apache/dubbo/remoting/etcd/jetcd/LeaseTest.java
@@ -39,11 +39,12 @@
 import io.etcd.jetcd.KV;
 import io.etcd.jetcd.Lease;
 import io.etcd.jetcd.launcher.EtcdCluster;
-import io.etcd.jetcd.launcher.EtcdClusterFactory;
+import io.etcd.jetcd.launcher.EtcdClusterImpl;
 import io.etcd.jetcd.lease.LeaseKeepAliveResponse;
 import io.etcd.jetcd.options.PutOption;
 import io.etcd.jetcd.support.CloseableClient;
 import io.etcd.jetcd.support.Observers;
+import io.etcd.jetcd.test.EtcdClusterExtension;
 import io.grpc.stub.StreamObserver;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
@@ -51,6 +52,8 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -65,6 +68,8 @@
 @Disabled
 public class LeaseTest {
 
+    private static final Logger logger = LoggerFactory.getLogger(LeaseTest.class);
+
     private static EtcdCluster cluster;
 
     private KV kvClient;
@@ -77,7 +82,16 @@
 
     @BeforeAll
     public static void beforeClass() {
-        cluster = EtcdClusterFactory.buildCluster("etcd-lease", 3, false);
+        EtcdClusterExtension clusterExtension = EtcdClusterExtension.builder()
+            .withClusterName("etcd-lease")
+            .withNodes(3)
+            .withSsl(false)
+            .build();
+        try {
+            cluster = clusterExtension.cluster();
+        } catch (Exception e) {
+            logger.error("Init etcd cluster failed");
+        }
         cluster.start();
     }
 
@@ -88,7 +102,7 @@
 
     @BeforeEach
     public void setUp() {
-        client = Client.builder().endpoints(cluster.getClientEndpoints()).build();
+        client = Client.builder().endpoints(cluster.clientEndpoints()).build();
         kvClient = client.getKVClient();
         leaseClient = client.getLeaseClient();
     }
diff --git a/dubbo-remoting-extensions/dubbo-remoting-grizzly/pom.xml b/dubbo-remoting-extensions/dubbo-remoting-grizzly/pom.xml
index 4d0dc51..8ded776 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-grizzly/pom.xml
+++ b/dubbo-remoting-extensions/dubbo-remoting-grizzly/pom.xml
@@ -23,7 +23,7 @@
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-remoting-grizzly</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
diff --git a/dubbo-remoting-extensions/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyClient.java b/dubbo-remoting-extensions/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyClient.java
index 9c290c7..300dcc3 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyClient.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyClient.java
@@ -36,6 +36,7 @@
 
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_TIMEOUT;
 import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+import static org.apache.dubbo.config.Constants.CLIENT_THREAD_POOL_NAME;
 
 /**
  * GrizzlyClient
diff --git a/dubbo-remoting-extensions/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyServer.java b/dubbo-remoting-extensions/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyServer.java
index b323841..d559a9f 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyServer.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-grizzly/src/main/java/org/apache/dubbo/remoting/transport/grizzly/GrizzlyServer.java
@@ -42,6 +42,7 @@
 import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS;
 import static org.apache.dubbo.common.constants.CommonConstants.THREADPOOL_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
+import static org.apache.dubbo.config.Constants.SERVER_THREAD_POOL_NAME;
 
 /**
  * GrizzlyServer
@@ -100,6 +101,11 @@
     }
 
     @Override
+    protected int getChannelsSize() {
+        return channels.size();
+    }
+
+    @Override
     public boolean isBound() {
         return !transport.isStopped();
     }
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/pom.xml b/dubbo-remoting-extensions/dubbo-remoting-http/pom.xml
new file mode 100644
index 0000000..721a192
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/pom.xml
@@ -0,0 +1,85 @@
+<?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>
+    <parent>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <artifactId>dubbo-remoting-extensions</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>dubbo-remoting-http</artifactId>
+    <version>${revision}</version>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The http remoting module of dubbo project</description>
+
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-remoting-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlet</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tomcat.embed</groupId>
+            <artifactId>tomcat-embed-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>fluent-hc</artifactId>
+            <version>4.5.5</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.squareup.okhttp3</groupId>
+            <artifactId>okhttp</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
+    </dependencies>
+</project>
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java
new file mode 100644
index 0000000..23c7841
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/BaseRestClient.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+
+public abstract class BaseRestClient<CLIENT> implements RestClient {
+
+    protected CLIENT client;
+
+    protected HttpClientConfig clientConfig;
+
+    public BaseRestClient(HttpClientConfig clientConfig) {
+        this.clientConfig = clientConfig;
+        client = createHttpClient(clientConfig);
+    }
+
+    protected abstract CLIENT createHttpClient(HttpClientConfig clientConfig);
+
+    public HttpClientConfig getClientConfig() {
+        return clientConfig;
+    }
+
+    public CLIENT getClient() {
+        return client;
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpBinder.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpBinder.java
new file mode 100644
index 0000000..26db36d
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpBinder.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.remoting.Constants;
+
+/**
+ * HttpBinder
+ */
+@SPI(value = "jetty", scope = ExtensionScope.FRAMEWORK)
+public interface HttpBinder {
+
+    /**
+     * bind the server.
+     *
+     * @param url server url.
+     * @return server.
+     */
+    @Adaptive({Constants.SERVER_KEY})
+    HttpServer bind(URL url, HttpHandler handler);
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.java
new file mode 100644
index 0000000..27085f6
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpHandler.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.dubbo.remoting.http;
+
+import java.io.IOException;
+
+/**
+ * http invocation handler.
+ */
+public interface HttpHandler<REQUEST, RESPONSE> {
+
+    /**
+     * invoke.
+     *
+     * @param request  request.
+     * @param response response.
+     * @throws IOException
+     */
+    void handle(REQUEST request, RESPONSE response) throws IOException;
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpServer.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpServer.java
new file mode 100644
index 0000000..d85582e
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/HttpServer.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.common.Resetable;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.RemotingServer;
+
+import java.net.InetSocketAddress;
+
+public interface HttpServer extends Resetable, RemotingServer {
+
+    /**
+     * get http handler.
+     *
+     * @return http handler.
+     */
+    HttpHandler getHttpHandler();
+
+    /**
+     * get url.
+     *
+     * @return url
+     */
+    URL getUrl();
+
+    /**
+     * get local address.
+     *
+     * @return local address.
+     */
+    InetSocketAddress getLocalAddress();
+
+    /**
+     * close the channel.
+     */
+    void close();
+
+    /**
+     * Graceful close the channel.
+     */
+    void close(int timeout);
+
+    /**
+     * is bound.
+     *
+     * @return bound
+     */
+    boolean isBound();
+
+    /**
+     * is closed.
+     *
+     * @return closed
+     */
+    boolean isClosed();
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java
new file mode 100644
index 0000000..5536e6c
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RequestTemplate.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.rpc.Invocation;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RequestTemplate implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String CONTENT_ENCODING = "Content-Encoding";
+    private static final String CONTENT_LENGTH = "Content-Length";
+    public static final String ENCODING_GZIP = "gzip";
+    public static final String ENCODING_DEFLATE = "deflate";
+    private static final List<String> EMPTY_ARRAYLIST = new ArrayList<>();
+
+    private final Map<String, Collection<String>> queries = new LinkedHashMap<>();
+    private final Map<String, Collection<String>> headers = new LinkedHashMap<>();
+    private String httpMethod;
+    private String path;
+    private String address;
+    private Object body;
+    private byte[] byteBody = new byte[0];
+    private String protocol = "http://";
+    private final Invocation invocation;
+    private String contextPath = "";
+    private Class<?> bodyType;
+
+    public RequestTemplate(Invocation invocation, String httpMethod, String address) {
+        this(invocation, httpMethod, address, "");
+    }
+
+    public RequestTemplate(Invocation invocation, String httpMethod, String address, String contextPath) {
+        this.httpMethod = httpMethod;
+        this.address = address;
+        this.invocation = invocation;
+        this.contextPath = contextPath;
+    }
+
+    public String getURL() {
+        StringBuilder stringBuilder = new StringBuilder(getProtocol() + address);
+
+        stringBuilder.append(getUri());
+        return stringBuilder.toString();
+    }
+
+    public String getUri() {
+        StringBuilder stringBuilder = new StringBuilder(getContextPath() + path);
+        return stringBuilder.append(getQueryString()).toString();
+    }
+
+    public String getQueryString() {
+
+        if (queries.isEmpty()) {
+            return "";
+        }
+
+        StringBuilder queryBuilder = new StringBuilder("?");
+        for (String field : queries.keySet()) {
+
+            Collection<String> queryValues = queries.get(field);
+
+            if (queryValues == null || queryValues.isEmpty()) {
+                continue;
+            }
+
+            for (String value : queryValues) {
+                queryBuilder.append('&');
+                queryBuilder.append(field);
+                if (value == null) {
+                    continue;
+                }
+
+                queryBuilder.append('=');
+                queryBuilder.append(value);
+            }
+        }
+
+        return queryBuilder.toString().replace("?&", "?");
+    }
+
+    public RequestTemplate path(String path) {
+        this.path = path;
+        return this;
+    }
+
+    public String getHttpMethod() {
+        return httpMethod;
+    }
+
+    public RequestTemplate httpMethod(String httpMethod) {
+        this.httpMethod = httpMethod;
+        return this;
+    }
+
+    public byte[] getSerializedBody() {
+        return byteBody;
+    }
+
+    public void serializeBody(byte[] body) {
+        addHeader(CONTENT_LENGTH, body.length); // must header
+        this.byteBody = body;
+    }
+
+    public boolean isBodyEmpty() {
+        return getUnSerializedBody() == null;
+    }
+
+    public RequestTemplate body(Object body, Class bodyType) {
+        this.body = body;
+        setBodyType(bodyType);
+        return this;
+    }
+
+    public Object getUnSerializedBody() {
+        return body;
+    }
+
+    public Map<String, Collection<String>> getAllHeaders() {
+        return headers;
+    }
+
+    public Collection<String> getHeaders(String name) {
+        return headers.get(name);
+    }
+
+    public String getHeader(String name) {
+        if (headers.containsKey(name)) {
+
+            Collection<String> headers = getHeaders(name);
+
+            if (headers.isEmpty()) {
+                return null;
+            }
+            String[] strings = headers.toArray(new String[0]);
+            return strings[0];
+
+        } else {
+            return null;
+        }
+    }
+
+    public Collection<String> getEncodingValues() {
+        if (headers.containsKey(CONTENT_ENCODING)) {
+            return headers.get(CONTENT_ENCODING);
+        }
+        return EMPTY_ARRAYLIST;
+    }
+
+    public boolean isGzipEncodedRequest() {
+        return getEncodingValues().contains(ENCODING_GZIP);
+    }
+
+    public boolean isDeflateEncodedRequest() {
+        return getEncodingValues().contains(ENCODING_DEFLATE);
+    }
+
+    public void addHeader(String key, String value) {
+        addValueByKey(key, value, this.headers);
+    }
+
+    public void addHeader(String key, Object value) {
+        addValueByKey(key, String.valueOf(value), this.headers);
+    }
+
+    public void addKeepAliveHeader(int time) {
+        addHeader(Constants.KEEP_ALIVE_HEADER, time);
+        addHeader(Constants.CONNECTION, Constants.KEEP_ALIVE);
+    }
+
+    public void addHeaders(String key, Collection<String> values) {
+        Collection<String> header = getHeaders(key);
+
+        if (header == null) {
+            header = new HashSet<>();
+            this.headers.put(key, header);
+        }
+        header.addAll(values);
+    }
+
+    public void addParam(String key, String value) {
+        addValueByKey(key, value, this.queries);
+    }
+
+    public void addParam(String key, Object value) {
+        addParam(key, String.valueOf(value));
+    }
+
+    public Map<String, Collection<String>> getQueries() {
+        return queries;
+    }
+
+    public Collection<String> getParam(String key) {
+        return getQueries().get(key);
+    }
+
+    public void addParams(String key, Collection<String> values) {
+        Collection<String> params = getParam(key);
+
+        if (params == null) {
+            params = new HashSet<>();
+            this.queries.put(key, params);
+        }
+        params.addAll(values);
+    }
+
+    public void addValueByKey(String key, String value, Map<String, Collection<String>> maps) {
+
+        if (value == null) {
+            return;
+        }
+
+        Collection<String> values = null;
+        if (!maps.containsKey(key)) {
+            values = new HashSet<>();
+            maps.put(key, values);
+        }
+        values = maps.get(key);
+
+        values.add(value);
+    }
+
+    public Integer getContentLength() {
+
+        if (!getAllHeaders().containsKey(CONTENT_LENGTH)) {
+            return null;
+        }
+
+        HashSet<String> strings = (HashSet<String>) getAllHeaders().get(CONTENT_LENGTH);
+
+        return Integer.parseInt(new ArrayList<>(strings).get(0));
+    }
+
+    public String getAddress() {
+        return address;
+    }
+
+    public void setAddress(String address) {
+        addHeader("Host", address); // must header
+        this.address = address;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public Invocation getInvocation() {
+        return invocation;
+    }
+
+    public String getContextPath() {
+        if (contextPath == null || contextPath.length() == 0) {
+            return "";
+        }
+
+        if (contextPath.startsWith("/")) {
+            return contextPath;
+        } else {
+            return "/" + contextPath;
+        }
+    }
+
+    public void setContextPath(String contextPath) {
+        this.contextPath = contextPath;
+    }
+
+    public Class<?> getBodyType() {
+        return bodyType;
+    }
+
+    public void setBodyType(Class<?> bodyType) {
+        this.bodyType = bodyType;
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.java
new file mode 100644
index 0000000..e5b8cec
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestClient.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http;
+
+import org.apache.dubbo.remoting.RemotingException;
+
+import java.util.concurrent.CompletableFuture;
+
+public interface RestClient {
+    /**
+     * send message.
+     *
+     * @param message
+     * @throws RemotingException
+     */
+    CompletableFuture<RestResult> send(RequestTemplate message);
+
+    /**
+     * close the channel.
+     */
+    void close();
+
+    /**
+     * Graceful close the channel.
+     */
+    void close(int timeout);
+
+    /**
+     * is closed.
+     *
+     * @return closed
+     */
+    boolean isClosed();
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.java
new file mode 100644
index 0000000..bb05767
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/RestResult.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 org.apache.dubbo.remoting.http;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * rest response facade
+ */
+public interface RestResult {
+    String getContentType();
+
+    byte[] getBody() throws IOException;
+
+    Map<String, List<String>> headers();
+
+    byte[] getErrorResponse() throws IOException;
+
+    int getResponseCode() throws IOException;
+
+    String getMessage() throws IOException;
+
+    default String appendErrorMessage(String message, String errorInfo) {
+        return message + "\n error info is: " + errorInfo;
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java
new file mode 100644
index 0000000..09e0ab9
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/config/HttpClientConfig.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.config;
+
+public class HttpClientConfig {
+    private int readTimeout = 6 * 1000;
+    private int writeTimeout = 6 * 1000;
+    private int connectTimeout = 6 * 1000;
+    private int chunkLength = 8196;
+
+    private int HTTP_CLIENT_CONNECTION_MANAGER_MAX_PER_ROUTE = 20;
+    private int HTTP_CLIENT_CONNECTION_MANAGER_MAX_TOTAL = 20;
+    private int HTTPCLIENT_KEEP_ALIVE_DURATION = 30 * 1000;
+    private int HTTP_CLIENT_CONNECTION_MANAGER_CLOSE_WAIT_TIME_MS = 1000;
+    private int HTTP_CLIENT_CONNECTION_MANAGER_CLOSE_IDLE_TIME_S = 30;
+
+    public HttpClientConfig() {}
+
+    public int getReadTimeout() {
+        return readTimeout;
+    }
+
+    public void setReadTimeout(int readTimeout) {
+        this.readTimeout = readTimeout;
+    }
+
+    public int getWriteTimeout() {
+        return writeTimeout;
+    }
+
+    public void setWriteTimeout(int writeTimeout) {
+        this.writeTimeout = writeTimeout;
+    }
+
+    public int getConnectTimeout() {
+        return connectTimeout;
+    }
+
+    public void setConnectTimeout(int connectTimeout) {
+        this.connectTimeout = connectTimeout;
+    }
+
+    public int getChunkLength() {
+        return chunkLength;
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.java
new file mode 100644
index 0000000..ad9e7f7
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/AbstractHttpClientFactory.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.dubbo.remoting.http.factory;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+import org.apache.dubbo.rpc.RpcException;
+
+/**
+ * AbstractHttpClientFactory
+ */
+public abstract class AbstractHttpClientFactory implements RestClientFactory {
+
+    protected final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+
+    // TODO   load config
+    protected HttpClientConfig httpClientConfig = new HttpClientConfig();
+
+    //////////////////////////////////////// implements start ///////////////////////////////////////////////
+    @Override
+    public RestClient createRestClient(URL url) throws RpcException {
+
+        beforeCreated(url);
+
+        // create a raw client
+        RestClient restClient = doCreateRestClient(url);
+
+        // postprocessor
+        afterCreated(restClient);
+
+        return restClient;
+    }
+
+    //////////////////////////////////////// implements end ///////////////////////////////////////////////
+
+    ////////////////////////////////////////   inner methods  ///////////////////////////////////////////////
+
+    protected void beforeCreated(URL url) {}
+
+    protected abstract RestClient doCreateRestClient(URL url) throws RpcException;
+
+    protected void afterCreated(RestClient client) {}
+
+    ////////////////////////////////////////   inner methods  ///////////////////////////////////////////////
+
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.java
new file mode 100644
index 0000000..c64453d
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/RestClientFactory.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.dubbo.remoting.http.factory;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Adaptive;
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.rpc.RpcException;
+
+/**
+ * RestClientFactory. (API/SPI, Singleton, ThreadSafe)
+ */
+@SPI(value = Constants.OK_HTTP, scope = ExtensionScope.FRAMEWORK)
+public interface RestClientFactory {
+
+    @Adaptive({Constants.CLIENT_KEY})
+    RestClient createRestClient(URL url) throws RpcException;
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.java
new file mode 100644
index 0000000..89d9c36
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/ApacheHttpClientFactory.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.dubbo.remoting.http.factory.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.factory.AbstractHttpClientFactory;
+import org.apache.dubbo.remoting.http.restclient.HttpClientRestClient;
+import org.apache.dubbo.rpc.RpcException;
+
+@Activate(Constants.APACHE_HTTP_CLIENT)
+public class ApacheHttpClientFactory extends AbstractHttpClientFactory {
+
+    @Override
+    protected RestClient doCreateRestClient(URL url) throws RpcException {
+
+        return new HttpClientRestClient(httpClientConfig);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.java
new file mode 100644
index 0000000..523df3c
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/OkHttpClientFactory.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.dubbo.remoting.http.factory.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.factory.AbstractHttpClientFactory;
+import org.apache.dubbo.remoting.http.restclient.OKHttpRestClient;
+import org.apache.dubbo.rpc.RpcException;
+
+@Activate(Constants.OK_HTTP)
+public class OkHttpClientFactory extends AbstractHttpClientFactory {
+
+    @Override
+    protected RestClient doCreateRestClient(URL url) throws RpcException {
+
+        return new OKHttpRestClient(httpClientConfig);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.java
new file mode 100644
index 0000000..40c2a82
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/factory/impl/URLConnectionClientFactory.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.dubbo.remoting.http.factory.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.factory.AbstractHttpClientFactory;
+import org.apache.dubbo.remoting.http.restclient.URLConnectionRestClient;
+import org.apache.dubbo.rpc.RpcException;
+
+@Activate(Constants.URL_CONNECTION)
+public class URLConnectionClientFactory extends AbstractHttpClientFactory {
+
+    @Override
+    protected RestClient doCreateRestClient(URL url) throws RpcException {
+
+        return new URLConnectionRestClient(httpClientConfig);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinder.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinder.java
new file mode 100644
index 0000000..05e5dc5
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinder.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.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.http.HttpBinder;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+/**
+ * JettyHttpTransporter
+ */
+public class JettyHttpBinder implements HttpBinder {
+
+    @Override
+    public HttpServer bind(URL url, HttpHandler handler) {
+        return new JettyHttpServer(url, handler);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpServer.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpServer.java
new file mode 100644
index 0000000..dfda971
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyHttpServer.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.servlet.DispatcherServlet;
+import org.apache.dubbo.remoting.http.servlet.ServletManager;
+import org.apache.dubbo.remoting.http.support.AbstractHttpServer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS;
+import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_STOP_HTTP_SERVER;
+
+public class JettyHttpServer extends AbstractHttpServer {
+
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(JettyHttpServer.class);
+
+    private Server server;
+
+    private URL url;
+
+    public JettyHttpServer(URL url, final HttpHandler handler) {
+        super(url, handler);
+        this.url = url;
+
+        // set dubbo's logger
+        System.setProperty("org.eclipse.jetty.util.log.class", JettyLoggerAdapter.class.getName());
+
+        DispatcherServlet.addHttpHandler(url.getParameter(Constants.BIND_PORT_KEY, url.getPort()), handler);
+
+        int threads = url.getParameter(THREADS_KEY, DEFAULT_THREADS);
+        QueuedThreadPool threadPool = new QueuedThreadPool();
+        threadPool.setDaemon(true);
+        threadPool.setMaxThreads(threads);
+        threadPool.setMinThreads(threads);
+
+        server = new Server(threadPool);
+
+        ServerConnector connector = new ServerConnector(server);
+
+        String bindIp = url.getParameter(Constants.BIND_IP_KEY, url.getHost());
+        if (!url.isAnyHost() && NetUtils.isValidLocalHost(bindIp)) {
+            connector.setHost(bindIp);
+        }
+        connector.setPort(url.getParameter(Constants.BIND_PORT_KEY, url.getPort()));
+
+        server.addConnector(connector);
+
+        ServletHandler servletHandler = new ServletHandler();
+        ServletHolder servletHolder = servletHandler.addServletWithMapping(DispatcherServlet.class, "/*");
+        servletHolder.setInitOrder(2);
+
+        // dubbo's original impl can't support the use of ServletContext
+        //        server.addHandler(servletHandler);
+        // TODO Context.SESSIONS is the best option here? (In jetty 9.x, it becomes ServletContextHandler.SESSIONS)
+        ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
+        context.setServletHandler(servletHandler);
+        ServletManager.getInstance()
+                .addServletContext(
+                        url.getParameter(Constants.BIND_PORT_KEY, url.getPort()), context.getServletContext());
+
+        try {
+            server.start();
+        } catch (Exception e) {
+            throw new IllegalStateException(
+                    "Failed to start jetty server on " + url.getParameter(Constants.BIND_IP_KEY) + ":"
+                            + url.getParameter(Constants.BIND_PORT_KEY) + ", cause: " + e.getMessage(),
+                    e);
+        }
+    }
+
+    @Override
+    public void close() {
+        super.close();
+
+        //
+        ServletManager.getInstance().removeServletContext(url.getParameter(Constants.BIND_PORT_KEY, url.getPort()));
+
+        if (server != null) {
+            try {
+                server.stop();
+            } catch (Exception e) {
+                logger.warn(COMMON_FAILED_STOP_HTTP_SERVER, "", "", e.getMessage(), e);
+            }
+        }
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapter.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapter.java
new file mode 100644
index 0000000..5005c94
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapter.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.eclipse.jetty.util.log.AbstractLogger;
+import org.eclipse.jetty.util.log.Logger;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION;
+
+/**
+ * logger adapter for jetty
+ */
+public class JettyLoggerAdapter extends AbstractLogger {
+    protected String name;
+
+    private final ErrorTypeAwareLogger logger;
+
+    private static boolean debugEnabled = false;
+
+    public JettyLoggerAdapter() {
+        this("org.apache.dubbo.remoting.http.jetty");
+    }
+
+    public JettyLoggerAdapter(Class<?> clazz) {
+        this(clazz.getName());
+    }
+
+    public JettyLoggerAdapter(String name) {
+        this.name = name;
+        this.logger = LoggerFactory.getErrorTypeAwareLogger(name);
+    }
+
+    @Override
+    protected Logger newLogger(String name) {
+        return new JettyLoggerAdapter(name);
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public void warn(String msg, Object... objects) {
+        if (logger.isWarnEnabled()) {
+            logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", this.format(msg, objects));
+        }
+    }
+
+    @Override
+    public void warn(Throwable throwable) {
+        if (logger.isWarnEnabled()) {
+            logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", throwable.getMessage(), throwable);
+        }
+    }
+
+    @Override
+    public void warn(String msg, Throwable throwable) {
+        if (logger.isWarnEnabled()) {
+            logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", msg, throwable);
+        }
+    }
+
+    @Override
+    public void info(String msg, Object... objects) {
+        if (logger.isInfoEnabled()) {
+            logger.info(this.format(msg, objects));
+        }
+    }
+
+    @Override
+    public void info(Throwable throwable) {
+        if (logger.isInfoEnabled()) {
+            logger.info(throwable);
+        }
+    }
+
+    @Override
+    public void info(String msg, Throwable throwable) {
+        if (logger.isInfoEnabled()) {
+            logger.info(msg, throwable);
+        }
+    }
+
+    @Override
+    public boolean isDebugEnabled() {
+        return debugEnabled;
+    }
+
+    @Override
+    public void setDebugEnabled(boolean enabled) {
+        debugEnabled = enabled;
+    }
+
+    @Override
+    public void debug(String msg, Object... objects) {
+        if (debugEnabled && logger.isDebugEnabled()) {
+            logger.debug(this.format(msg, objects));
+        }
+    }
+
+    @Override
+    public void debug(Throwable throwable) {
+        if (debugEnabled && logger.isDebugEnabled()) {
+            logger.debug(throwable);
+        }
+    }
+
+    @Override
+    public void debug(String msg, Throwable throwable) {
+        if (debugEnabled && logger.isDebugEnabled()) {
+            logger.debug(msg, throwable);
+        }
+    }
+
+    @Override
+    public void ignore(Throwable throwable) {
+        if (logger.isWarnEnabled()) {
+            logger.warn(COMMON_UNEXPECTED_EXCEPTION, "", "", "IGNORED EXCEPTION ", throwable);
+        }
+    }
+
+    private String format(String msg, Object... args) {
+        msg = String.valueOf(msg); // Avoids NPE
+        String braces = "{}";
+        StringBuilder builder = new StringBuilder();
+        int start = 0;
+        for (Object arg : args) {
+            int bracesIndex = msg.indexOf(braces, start);
+            if (bracesIndex < 0) {
+                builder.append(msg.substring(start));
+                builder.append(' ');
+                builder.append(arg);
+                start = msg.length();
+            } else {
+                builder.append(msg, start, bracesIndex);
+                builder.append(arg);
+                start = bracesIndex + braces.length();
+            }
+        }
+        builder.append(msg.substring(start));
+        return builder.toString();
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java
new file mode 100644
index 0000000..08c6f4f
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/HttpClientRestClient.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.restclient;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.RestResult;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+import org.apache.http.Header;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpOptions;
+import org.apache.http.client.methods.HttpPatch;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.methods.HttpTrace;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+public class HttpClientRestClient implements RestClient {
+    private final CloseableHttpClient closeableHttpClient;
+    private final HttpClientConfig httpClientConfig;
+
+    public HttpClientRestClient(HttpClientConfig clientConfig) {
+        closeableHttpClient = createHttpClient();
+        httpClientConfig = clientConfig;
+    }
+
+    @Override
+    public CompletableFuture<RestResult> send(RequestTemplate requestTemplate) {
+
+        HttpRequestBase httpRequest = null;
+        String httpMethod = requestTemplate.getHttpMethod();
+
+        httpRequest = createHttpUriRequest(httpMethod, requestTemplate);
+
+        if (httpRequest instanceof HttpEntityEnclosingRequest) {
+            ((HttpEntityEnclosingRequestBase) httpRequest)
+                    .setEntity(new ByteArrayEntity(requestTemplate.getSerializedBody()));
+        }
+
+        Map<String, Collection<String>> allHeaders = requestTemplate.getAllHeaders();
+
+        allHeaders.remove("Content-Length");
+        // header
+        for (String headerName : allHeaders.keySet()) {
+            Collection<String> headerValues = allHeaders.get(headerName);
+
+            for (String headerValue : headerValues) {
+                httpRequest.addHeader(headerName, headerValue);
+            }
+        }
+
+        httpRequest.setConfig(getRequestConfig(httpClientConfig));
+
+        CompletableFuture<RestResult> future = new CompletableFuture<>();
+        try {
+            CloseableHttpResponse response = closeableHttpClient.execute(httpRequest);
+            future.complete(new RestResult() {
+                @Override
+                public String getContentType() {
+                    Header header = response.getFirstHeader("Content-Type");
+                    return header == null ? null : header.getValue();
+                }
+
+                @Override
+                public byte[] getBody() throws IOException {
+                    if (response.getEntity() == null) {
+                        return new byte[0];
+                    }
+                    return IOUtils.toByteArray(response.getEntity().getContent());
+                }
+
+                @Override
+                public Map<String, List<String>> headers() {
+                    return Arrays.stream(response.getAllHeaders())
+                            .collect(Collectors.toMap(Header::getName, h -> Collections.singletonList(h.getValue())));
+                }
+
+                @Override
+                public byte[] getErrorResponse() throws IOException {
+                    return getBody();
+                }
+
+                @Override
+                public int getResponseCode() {
+                    return response.getStatusLine().getStatusCode();
+                }
+
+                @Override
+                public String getMessage() throws IOException {
+                    return appendErrorMessage(
+                            response.getStatusLine().getReasonPhrase(), new String(getErrorResponse()));
+                }
+            });
+        } catch (IOException e) {
+            future.completeExceptionally(e);
+        }
+        return future;
+    }
+
+    private RequestConfig getRequestConfig(HttpClientConfig clientConfig) {
+
+        // TODO config
+        return RequestConfig.custom().build();
+    }
+
+    @Override
+    public void close() {
+        try {
+            closeableHttpClient.close();
+        } catch (IOException e) {
+
+        }
+    }
+
+    @Override
+    public void close(int timeout) {}
+
+    @Override
+    public boolean isClosed() {
+        // TODO close judge
+        return true;
+    }
+
+    public CloseableHttpClient createHttpClient() {
+        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
+        return HttpClients.custom().setConnectionManager(connectionManager).build();
+    }
+
+    protected HttpRequestBase createHttpUriRequest(String httpMethod, RequestTemplate requestTemplate) {
+        String uri = requestTemplate.getURL();
+        HttpRequestBase httpUriRequest = null;
+        if (HttpGet.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpGet(uri);
+        } else if (HttpHead.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpHead(uri);
+        } else if (HttpPost.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpPost(uri);
+        } else if (HttpPut.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpPut(uri);
+        } else if (HttpPatch.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpPatch(uri);
+        } else if (HttpDelete.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpDelete(uri);
+        } else if (HttpOptions.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpOptions(uri);
+        } else if (HttpTrace.METHOD_NAME.equals(httpMethod)) {
+            httpUriRequest = new HttpTrace(uri);
+        } else {
+            throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
+        }
+        return httpUriRequest;
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java
new file mode 100644
index 0000000..ef0116e
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/OKHttpRestClient.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.restclient;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okhttp3.internal.http.HttpMethod;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.RestResult;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+// TODO add version 4.0 implements ,and default version is < 4.0,for dependency conflict
+public class OKHttpRestClient implements RestClient {
+    private final OkHttpClient okHttpClient;
+    private final HttpClientConfig httpClientConfig;
+
+    public OKHttpRestClient(HttpClientConfig clientConfig) {
+        this.okHttpClient = createHttpClient(clientConfig);
+        this.httpClientConfig = clientConfig;
+    }
+
+    @Override
+    public CompletableFuture<RestResult> send(RequestTemplate requestTemplate) {
+
+        Request.Builder builder = new Request.Builder();
+        // url
+        builder.url(requestTemplate.getURL());
+
+        Map<String, Collection<String>> allHeaders = requestTemplate.getAllHeaders();
+
+        boolean hasBody = false;
+        RequestBody requestBody = null;
+        // GET & HEAD body is forbidden
+        if (HttpMethod.permitsRequestBody(requestTemplate.getHttpMethod())) {
+            requestBody = RequestBody.create(null, requestTemplate.getSerializedBody());
+            hasBody = true;
+        }
+
+        // header
+        for (String headerName : allHeaders.keySet()) {
+            Collection<String> headerValues = allHeaders.get(headerName);
+            if (!hasBody && "Content-Length".equals(headerName)) {
+                continue;
+            }
+            for (String headerValue : headerValues) {
+
+                builder.addHeader(headerName, headerValue);
+            }
+        }
+
+        builder.method(requestTemplate.getHttpMethod(), requestBody);
+
+        CompletableFuture<RestResult> future = new CompletableFuture<>();
+
+        okHttpClient.newCall(builder.build()).enqueue(new Callback() {
+            @Override
+            public void onFailure(Call call, IOException e) {
+                future.completeExceptionally(e);
+            }
+
+            @Override
+            public void onResponse(Call call, Response response) throws IOException {
+                future.complete(new RestResult() {
+                    @Override
+                    public String getContentType() {
+                        return response.header("Content-Type");
+                    }
+
+                    @Override
+                    public byte[] getBody() throws IOException {
+                        ResponseBody body = response.body();
+                        return body == null ? null : body.bytes();
+                    }
+
+                    @Override
+                    public Map<String, List<String>> headers() {
+                        return response.headers().toMultimap();
+                    }
+
+                    @Override
+                    public byte[] getErrorResponse() throws IOException {
+                        return getBody();
+                    }
+
+                    @Override
+                    public int getResponseCode() throws IOException {
+                        return response.code();
+                    }
+
+                    @Override
+                    public String getMessage() throws IOException {
+                        return appendErrorMessage(response.message(), new String(getBody()));
+                    }
+                });
+            }
+        });
+
+        return future;
+    }
+
+    @Override
+    public void close() {
+        okHttpClient.connectionPool().evictAll();
+    }
+
+    @Override
+    public void close(int timeout) {}
+
+    @Override
+    public boolean isClosed() {
+        return okHttpClient.retryOnConnectionFailure();
+    }
+
+    public OkHttpClient createHttpClient(HttpClientConfig httpClientConfig) {
+        OkHttpClient client = new OkHttpClient.Builder()
+                .readTimeout(httpClientConfig.getReadTimeout(), TimeUnit.SECONDS)
+                .writeTimeout(httpClientConfig.getWriteTimeout(), TimeUnit.SECONDS)
+                .connectTimeout(httpClientConfig.getConnectTimeout(), TimeUnit.SECONDS)
+                .build();
+        return client;
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java
new file mode 100644
index 0000000..c4f6715
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/restclient/URLConnectionRestClient.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.restclient;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.RestResult;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class URLConnectionRestClient implements RestClient {
+    private final HttpClientConfig clientConfig;
+
+    public URLConnectionRestClient(HttpClientConfig clientConfig) {
+        this.clientConfig = clientConfig;
+    }
+
+    @Override
+    public CompletableFuture<RestResult> send(RequestTemplate requestTemplate) {
+
+        CompletableFuture<RestResult> future = new CompletableFuture<>();
+
+        try {
+            HttpURLConnection connection = (HttpURLConnection) new URL(requestTemplate.getURL()).openConnection();
+            connection.setConnectTimeout(clientConfig.getConnectTimeout());
+            connection.setReadTimeout(clientConfig.getReadTimeout());
+            connection.setRequestMethod(requestTemplate.getHttpMethod());
+
+            prepareConnection(connection, requestTemplate.getHttpMethod());
+
+            // writeHeaders
+
+            for (String field : requestTemplate.getAllHeaders().keySet()) {
+                for (String value : requestTemplate.getHeaders(field)) {
+                    connection.addRequestProperty(field, value);
+                }
+            }
+
+            // writeBody
+
+            boolean gzipEncodedRequest = requestTemplate.isGzipEncodedRequest();
+            boolean deflateEncodedRequest = requestTemplate.isDeflateEncodedRequest();
+            if (requestTemplate.isBodyEmpty()) {
+                future.complete(getRestResultFromConnection(connection));
+                return future;
+            }
+            Integer contentLength = requestTemplate.getContentLength();
+
+            if (contentLength != null) {
+                connection.setFixedLengthStreamingMode(contentLength);
+            } else {
+                connection.setChunkedStreamingMode(clientConfig.getChunkLength());
+            }
+
+            OutputStream out = connection.getOutputStream();
+            if (gzipEncodedRequest) {
+                out = new GZIPOutputStream(out);
+            } else if (deflateEncodedRequest) {
+                out = new DeflaterOutputStream(out);
+            }
+            try {
+                out.write(requestTemplate.getSerializedBody());
+            } finally {
+                try {
+                    out.close();
+                } catch (IOException suppressed) {
+                }
+            }
+
+            future.complete(getRestResultFromConnection(connection));
+        } catch (Exception e) {
+            future.completeExceptionally(e);
+        }
+
+        return future;
+    }
+
+    @Override
+    public void close() {}
+
+    @Override
+    public void close(int timeout) {}
+
+    @Override
+    public boolean isClosed() {
+        return true;
+    }
+
+    private RestResult getRestResultFromConnection(HttpURLConnection connection) {
+
+        return new RestResult() {
+            @Override
+            public String getContentType() {
+                return connection.getContentType();
+            }
+
+            @Override
+            public byte[] getBody() throws IOException {
+                return IOUtils.toByteArray(connection.getInputStream());
+            }
+
+            @Override
+            public Map<String, List<String>> headers() {
+                return connection.getHeaderFields();
+            }
+
+            @Override
+            public byte[] getErrorResponse() throws IOException {
+                return IOUtils.toByteArray(connection.getErrorStream());
+            }
+
+            @Override
+            public int getResponseCode() throws IOException {
+                return connection.getResponseCode();
+            }
+
+            @Override
+            public String getMessage() throws IOException {
+                return appendErrorMessage(connection.getResponseMessage(), new String(getErrorResponse()));
+            }
+        };
+    }
+
+    private void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
+
+        connection.setDoInput(true);
+
+        if ("GET".equals(httpMethod)) {
+            connection.setInstanceFollowRedirects(true);
+        } else {
+            connection.setInstanceFollowRedirects(false);
+        }
+
+        if ("POST".equals(httpMethod)
+                || "PUT".equals(httpMethod)
+                || "PATCH".equals(httpMethod)
+                || "DELETE".equals(httpMethod)) {
+            connection.setDoOutput(true);
+        } else {
+            connection.setDoOutput(false);
+        }
+
+        connection.setRequestMethod(httpMethod);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/BootstrapListener.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/BootstrapListener.java
new file mode 100644
index 0000000..0a902f2
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/BootstrapListener.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.servlet;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/**
+ * This class must be defined before something like spring's ContextLoaderListener in web.xml
+ */
+public class BootstrapListener implements ServletContextListener {
+
+    @Override
+    public void contextInitialized(ServletContextEvent servletContextEvent) {
+        ServletManager.getInstance()
+                .addServletContext(ServletManager.EXTERNAL_SERVER_PORT, servletContextEvent.getServletContext());
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent servletContextEvent) {
+        ServletManager.getInstance().removeServletContext(ServletManager.EXTERNAL_SERVER_PORT);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/DispatcherServlet.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/DispatcherServlet.java
new file mode 100644
index 0000000..d23ac6f
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/DispatcherServlet.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.dubbo.remoting.http.servlet;
+
+import org.apache.dubbo.remoting.http.HttpHandler;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Service dispatcher Servlet.
+ */
+public class DispatcherServlet extends HttpServlet {
+
+    private static final long serialVersionUID = 5766349180380479888L;
+    private static final Map<Integer, HttpHandler> HANDLERS = new ConcurrentHashMap<>();
+    private static DispatcherServlet INSTANCE;
+
+    public DispatcherServlet() {
+        DispatcherServlet.INSTANCE = this;
+    }
+
+    public static void addHttpHandler(int port, HttpHandler processor) {
+        HANDLERS.put(port, processor);
+    }
+
+    public static void removeHttpHandler(int port) {
+        HANDLERS.remove(port);
+    }
+
+    public static DispatcherServlet getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    protected void service(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
+        HttpHandler handler = HANDLERS.get(request.getLocalPort());
+        if (handler == null) { // service not found.
+            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Service not found.");
+        } else {
+            handler.handle(request, response);
+        }
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.java
new file mode 100644
index 0000000..33aecc5
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpBinder.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.dubbo.remoting.http.servlet;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.http.HttpBinder;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+/**
+ * ServletHttpTransporter
+ */
+public class ServletHttpBinder implements HttpBinder {
+
+    @Override
+    public HttpServer bind(URL url, HttpHandler handler) {
+        return new ServletHttpServer(url, handler);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpServer.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpServer.java
new file mode 100644
index 0000000..bf0b014
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletHttpServer.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.servlet;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.support.AbstractHttpServer;
+
+public class ServletHttpServer extends AbstractHttpServer {
+
+    public ServletHttpServer(URL url, HttpHandler handler) {
+        super(url, handler);
+        DispatcherServlet.addHttpHandler(url.getParameter(Constants.BIND_PORT_KEY, 8080), handler);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletManager.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletManager.java
new file mode 100644
index 0000000..1e19e3f
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/servlet/ServletManager.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.servlet;
+
+import javax.servlet.ServletContext;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * TODO this may not be a pretty elegant solution,
+ */
+public class ServletManager {
+
+    public static final int EXTERNAL_SERVER_PORT = -1234;
+
+    private static final ServletManager INSTANCE = new ServletManager();
+
+    private final Map<Integer, ServletContext> contextMap = new ConcurrentHashMap<>();
+
+    public static ServletManager getInstance() {
+        return INSTANCE;
+    }
+
+    public void addServletContext(int port, ServletContext servletContext) {
+        contextMap.put(port, servletContext);
+    }
+
+    public void removeServletContext(int port) {
+        contextMap.remove(port);
+    }
+
+    public ServletContext getServletContext(int port) {
+        return contextMap.get(port);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/support/AbstractHttpServer.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/support/AbstractHttpServer.java
new file mode 100644
index 0000000..cc01b1e
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/support/AbstractHttpServer.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.support;
+
+import org.apache.dubbo.common.Parameters;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.Channel;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+/**
+ * AbstractHttpServer
+ */
+public abstract class AbstractHttpServer implements HttpServer {
+
+    private final URL url;
+
+    private final HttpHandler handler;
+
+    private volatile boolean closed;
+
+    public AbstractHttpServer(URL url, HttpHandler handler) {
+        if (url == null) {
+            throw new IllegalArgumentException("url == null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("handler == null");
+        }
+        this.url = url;
+        this.handler = handler;
+    }
+
+    @Override
+    public HttpHandler getHttpHandler() {
+        return handler;
+    }
+
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public void reset(URL url) {}
+
+    @Override
+    public boolean isBound() {
+        return true;
+    }
+
+    @Override
+    public InetSocketAddress getLocalAddress() {
+        return url.toInetSocketAddress();
+    }
+
+    @Override
+    public void close() {
+        closed = true;
+    }
+
+    @Override
+    public void close(int timeout) {
+        close();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return closed;
+    }
+
+    /**
+     * Following methods are extended from RemotingServer, useless for http servers
+     */
+    @Override
+    public boolean canHandleIdle() {
+        return false;
+    }
+
+    @Override
+    public Collection<Channel> getChannels() {
+        return null;
+    }
+
+    @Override
+    public Channel getChannel(InetSocketAddress remoteAddress) {
+        return null;
+    }
+
+    @Override
+    public void reset(Parameters parameters) {}
+
+    @Override
+    public ChannelHandler getChannelHandler() {
+        return null;
+    }
+
+    @Override
+    public void send(Object message) throws RemotingException {}
+
+    @Override
+    public void send(Object message, boolean sent) throws RemotingException {}
+
+    @Override
+    public void startClose() {}
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinder.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinder.java
new file mode 100644
index 0000000..92b4bf1
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinder.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.tomcat;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.http.HttpBinder;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+
+public class TomcatHttpBinder implements HttpBinder {
+
+    @Override
+    public HttpServer bind(URL url, HttpHandler handler) {
+        return new TomcatHttpServer(url, handler);
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpServer.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpServer.java
new file mode 100644
index 0000000..ed0de49
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpServer.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.tomcat;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.servlet.DispatcherServlet;
+import org.apache.dubbo.remoting.http.servlet.ServletManager;
+import org.apache.dubbo.remoting.http.support.AbstractHttpServer;
+
+import java.io.File;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_THREADS;
+import static org.apache.dubbo.common.constants.CommonConstants.THREADS_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_FAILED_STOP_HTTP_SERVER;
+import static org.apache.dubbo.remoting.Constants.ACCEPTS_KEY;
+
+public class TomcatHttpServer extends AbstractHttpServer {
+
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(TomcatHttpServer.class);
+
+    private final Tomcat tomcat;
+
+    private final URL url;
+
+    public TomcatHttpServer(URL url, final HttpHandler handler) {
+        super(url, handler);
+
+        this.url = url;
+        DispatcherServlet.addHttpHandler(url.getPort(), handler);
+        String baseDir = new File(System.getProperty("java.io.tmpdir")).getAbsolutePath();
+        tomcat = new Tomcat();
+
+        Connector connector = tomcat.getConnector();
+        connector.setPort(url.getPort());
+        connector.setProperty("maxThreads", String.valueOf(url.getParameter(THREADS_KEY, DEFAULT_THREADS)));
+        connector.setProperty("maxConnections", String.valueOf(url.getParameter(ACCEPTS_KEY, -1)));
+        connector.setProperty("URIEncoding", "UTF-8");
+        connector.setProperty("connectionTimeout", "60000");
+        connector.setProperty("maxKeepAliveRequests", "-1");
+
+        tomcat.setBaseDir(baseDir);
+        tomcat.setPort(url.getPort());
+
+        Context context = tomcat.addContext("/", baseDir);
+        Tomcat.addServlet(context, "dispatcher", new DispatcherServlet());
+        // Issue : https://github.com/apache/dubbo/issues/6418
+        // addServletMapping method will be removed since Tomcat 9
+        // context.addServletMapping("/*", "dispatcher");
+        context.addServletMappingDecoded("/*", "dispatcher");
+        ServletManager.getInstance().addServletContext(url.getPort(), context.getServletContext());
+
+        // tell tomcat to fail on startup failures.
+        System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
+
+        try {
+            tomcat.start();
+        } catch (LifecycleException e) {
+            throw new IllegalStateException("Failed to start tomcat server at " + url.getAddress(), e);
+        }
+    }
+
+    @Override
+    public void close() {
+        super.close();
+
+        ServletManager.getInstance().removeServletContext(url.getPort());
+
+        try {
+            tomcat.stop();
+            // close port by destroy()
+            tomcat.destroy();
+        } catch (Exception e) {
+            logger.warn(COMMON_FAILED_STOP_HTTP_SERVER, "", "", e.getMessage(), e);
+        }
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder
new file mode 100644
index 0000000..845124b
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.HttpBinder
@@ -0,0 +1,3 @@
+servlet=org.apache.dubbo.remoting.http.servlet.ServletHttpBinder
+jetty=org.apache.dubbo.remoting.http.jetty.JettyHttpBinder
+tomcat=org.apache.dubbo.remoting.http.tomcat.TomcatHttpBinder
\ No newline at end of file
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory
new file mode 100644
index 0000000..95dd12e
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.remoting.http.factory.RestClientFactory
@@ -0,0 +1,4 @@
+ok-http=org.apache.dubbo.remoting.http.factory.impl.OkHttpClientFactory
+url-connection=org.apache.dubbo.remoting.http.factory.impl.URLConnectionClientFactory
+apache-http-client=org.apache.dubbo.remoting.http.factory.impl.ApacheHttpClientFactory
+
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java
new file mode 100644
index 0000000..095d91e
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyHttpBinderTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+import org.apache.http.client.fluent.Request;
+import org.junit.jupiter.api.Test;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+class JettyHttpBinderTest {
+    @Test
+    void shouldAbleHandleRequestForJettyBinder() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] {Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException {
+                response.getWriter().write("Jetty");
+            }
+        });
+
+        String response =
+                Request.Get(url.toJavaURL().toURI()).execute().returnContent().asString();
+
+        assertThat(response, is("Jetty"));
+
+        httpServer.close();
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java
new file mode 100644
index 0000000..74b4d05
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/jetty/JettyLoggerAdapterTest.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.jetty;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.support.FailsafeErrorTypeAwareLogger;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+import org.apache.http.client.fluent.Request;
+import org.eclipse.jetty.util.log.Log;
+import org.junit.jupiter.api.Test;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+
+class JettyLoggerAdapterTest {
+
+    @Test
+    void testJettyUseDubboLogger() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] {Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException {
+                response.getWriter().write("Jetty is using Dubbo's logger");
+            }
+        });
+        Request.Get(url.toJavaURL().toURI()).execute().returnContent().asString();
+
+        assertThat(Log.getLog().getClass().isAssignableFrom(JettyLoggerAdapter.class), is(true));
+
+        httpServer.close();
+    }
+
+    @Test
+    void testSuccessLogger() throws Exception {
+        Logger successLogger = mock(Logger.class);
+        Class<?> clazz = Class.forName("org.apache.dubbo.remoting.http.jetty.JettyLoggerAdapter");
+        JettyLoggerAdapter jettyLoggerAdapter =
+                (JettyLoggerAdapter) clazz.getDeclaredConstructor().newInstance();
+
+        Field loggerField = clazz.getDeclaredField("logger");
+        loggerField.setAccessible(true);
+        loggerField.set(jettyLoggerAdapter, new FailsafeErrorTypeAwareLogger(successLogger));
+        jettyLoggerAdapter.setDebugEnabled(true);
+
+        when(successLogger.isDebugEnabled()).thenReturn(true);
+        when(successLogger.isWarnEnabled()).thenReturn(true);
+        when(successLogger.isInfoEnabled()).thenReturn(true);
+
+        jettyLoggerAdapter.warn("warn");
+        jettyLoggerAdapter.info("info");
+        jettyLoggerAdapter.debug("debug");
+
+        verify(successLogger).warn(anyString());
+        verify(successLogger).info(anyString());
+        verify(successLogger).debug(anyString());
+
+        jettyLoggerAdapter.warn(new Exception("warn"));
+        jettyLoggerAdapter.info(new Exception("info"));
+        jettyLoggerAdapter.debug(new Exception("debug"));
+        jettyLoggerAdapter.ignore(new Exception("ignore"));
+
+        jettyLoggerAdapter.warn("warn", new Exception("warn"));
+        jettyLoggerAdapter.info("info", new Exception("info"));
+        jettyLoggerAdapter.debug("debug", new Exception("debug"));
+    }
+
+    @Test
+    void testNewLogger() {
+        JettyLoggerAdapter loggerAdapter = new JettyLoggerAdapter();
+        org.eclipse.jetty.util.log.Logger logger =
+                loggerAdapter.newLogger(this.getClass().getName());
+        assertThat(logger.getClass().isAssignableFrom(JettyLoggerAdapter.class), is(true));
+    }
+
+    @Test
+    void testDebugEnabled() {
+        JettyLoggerAdapter loggerAdapter = new JettyLoggerAdapter();
+        loggerAdapter.setDebugEnabled(true);
+        assertThat(loggerAdapter.isDebugEnabled(), is(true));
+    }
+
+    @Test
+    void testLoggerFormat() throws Exception {
+        Class<?> clazz = Class.forName("org.apache.dubbo.remoting.http.jetty.JettyLoggerAdapter");
+        Object newInstance = clazz.getDeclaredConstructor().newInstance();
+
+        Method method = clazz.getDeclaredMethod("format", String.class, Object[].class);
+        method.setAccessible(true);
+
+        String print = (String) method.invoke(newInstance, "Hello,{}! I'am {}", new String[] {"World", "Jetty"});
+
+        assertThat(print, is("Hello,World! I'am Jetty"));
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/rest/RestClientTest.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/rest/RestClientTest.java
new file mode 100644
index 0000000..b9523aa
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/rest/RestClientTest.java
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.RestResult;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+import org.apache.dubbo.remoting.http.jetty.JettyHttpServer;
+import org.apache.dubbo.remoting.http.restclient.HttpClientRestClient;
+import org.apache.dubbo.remoting.http.restclient.OKHttpRestClient;
+import org.apache.dubbo.remoting.http.restclient.URLConnectionRestClient;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class RestClientTest {
+
+    @Test
+    public void testRestClient() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] {Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException {
+                response.getWriter().write("Jetty");
+            }
+        });
+
+        RequestTemplate requestTemplate = new RequestTemplate(null, "POST", "localhost:" + port);
+
+        requestTemplate.addParam("p1", "value1");
+        requestTemplate.addParam("p2", "value2");
+
+        requestTemplate.addParams("p3", Arrays.asList("value3", "value3.1"));
+        requestTemplate.addHeader("test", "dubbo");
+        requestTemplate.addKeepAliveHeader(60);
+
+        requestTemplate.addHeaders("header", Arrays.asList("h1", "h2"));
+
+        requestTemplate.path("/test");
+        requestTemplate.serializeBody("test".getBytes(StandardCharsets.UTF_8));
+
+        RestClient restClient = new OKHttpRestClient(new HttpClientConfig());
+
+        CompletableFuture<RestResult> send = restClient.send(requestTemplate);
+
+        RestResult restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), is("Jetty"));
+
+        restClient = new HttpClientRestClient(new HttpClientConfig());
+
+        send = restClient.send(requestTemplate);
+
+        restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), is("Jetty"));
+
+        restClient = new URLConnectionRestClient(new HttpClientConfig());
+
+        send = restClient.send(requestTemplate);
+
+        restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), is("Jetty"));
+
+        httpServer.close();
+    }
+
+    @Test
+    public void testError() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] {Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException {
+                response.setStatus(500);
+                response.getWriter().write("server error");
+                response.addHeader("Content-Type", "text/html");
+            }
+        });
+
+        RequestTemplate requestTemplate = new RequestTemplate(null, null, null);
+
+        requestTemplate.httpMethod("POST");
+        requestTemplate.setAddress("localhost:" + port);
+        requestTemplate.setProtocol("http://");
+        requestTemplate.addHeader("test", "dubbo");
+        requestTemplate.path("/test");
+        requestTemplate.serializeBody("test".getBytes(StandardCharsets.UTF_8));
+
+        RestClient restClient = new OKHttpRestClient(new HttpClientConfig());
+
+        CompletableFuture<RestResult> send = restClient.send(requestTemplate);
+
+        String error = "Server Error\n" + " error info is: server error";
+        RestResult restResult = send.get();
+
+        String contentType = "text/html;charset=iso-8859-1";
+
+        Assertions.assertEquals(500, restResult.getResponseCode());
+        Assertions.assertEquals(error, restResult.getMessage());
+        Assertions.assertEquals(contentType, restResult.getContentType());
+
+        Map<String, List<String>> headers = restResult.headers();
+        restClient.close();
+
+        restClient = new HttpClientRestClient(new HttpClientConfig());
+        send = restClient.send(requestTemplate);
+        restResult = send.get();
+
+        Assertions.assertEquals(500, restResult.getResponseCode());
+        Assertions.assertEquals(error, restResult.getMessage());
+        Assertions.assertEquals(contentType, restResult.getContentType());
+
+        restClient.close();
+
+        restClient = new URLConnectionRestClient(new HttpClientConfig());
+        send = restClient.send(requestTemplate);
+        restResult = send.get();
+
+        Assertions.assertEquals(500, restResult.getResponseCode());
+        Assertions.assertEquals(error, restResult.getMessage());
+        Assertions.assertEquals(contentType, restResult.getContentType());
+        restClient.close();
+
+        httpServer.close();
+    }
+
+    @Test
+    public void testMethod() {
+
+        RequestTemplate requestTemplate = new RequestTemplate(null, null, null);
+
+        requestTemplate.body(new Object(), Object.class);
+
+        Assertions.assertEquals(requestTemplate.getBodyType(), Object.class);
+
+        requestTemplate.addHeader("Content-Length", 1);
+
+        Integer contentLength = requestTemplate.getContentLength();
+
+        Assertions.assertEquals(1, contentLength);
+
+        List<String> strings = Arrays.asList("h1", "h2");
+
+        requestTemplate.addHeaders("header", strings);
+
+        Assertions.assertArrayEquals(
+                strings.toArray(new String[0]),
+                requestTemplate.getHeaders("header").toArray(new String[0]));
+
+        strings = Arrays.asList("p1", "p2");
+
+        requestTemplate.addParams("param", strings);
+
+        Assertions.assertArrayEquals(
+                strings.toArray(new String[0]),
+                requestTemplate.getParam("param").toArray(new String[0]));
+    }
+
+    @Test
+    void testBuildURL() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] {Constants.BIND_PORT_KEY, String.valueOf(port)});
+        HttpServer httpServer = new JettyHttpServer(url, new HttpHandler<HttpServletRequest, HttpServletResponse>() {
+            @Override
+            public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException {
+                response.setCharacterEncoding("UTF-8");
+                response.getWriter().write(request.getQueryString());
+            }
+        });
+
+        RequestTemplate requestTemplate = new RequestTemplate(null, "POST", "localhost:" + port);
+
+        requestTemplate.addParam("name", "李强");
+        requestTemplate.addParam("age", "18");
+        requestTemplate.path("/hello/world");
+
+        // When using the OKHttpRestClient, parameters will be encoded with UTF-8 and appended to the URL
+        RestClient restClient = new OKHttpRestClient(new HttpClientConfig());
+
+        CompletableFuture<RestResult> send = restClient.send(requestTemplate);
+
+        RestResult restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), is("name=%E6%9D%8E%E5%BC%BA&age=18"));
+
+        // When using the HttpClientRestClient, parameters will be encoded with UTF-8 and appended to the URL
+        restClient = new HttpClientRestClient(new HttpClientConfig());
+
+        send = restClient.send(requestTemplate);
+
+        restResult = send.get();
+
+        assertThat(new String(restResult.getBody()), is("name=%E6%9D%8E%E5%BC%BA&age=18"));
+
+        // When using the URLConnectionRestClient, parameters won't be encoded and still appended to the URL
+        restClient = new URLConnectionRestClient(new HttpClientConfig());
+
+        send = restClient.send(requestTemplate);
+
+        restResult = send.get();
+
+        assertThat(new String(restResult.getBody(), StandardCharsets.UTF_8), is("name=李强&age=18"));
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java
new file mode 100644
index 0000000..cad26de
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/java/org/apache/dubbo/remoting/http/tomcat/TomcatHttpBinderTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.remoting.http.tomcat;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.url.component.ServiceConfigURL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.remoting.http.HttpServer;
+import org.apache.http.client.fluent.Request;
+import org.junit.jupiter.api.Test;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+class TomcatHttpBinderTest {
+    @Test
+    void shouldAbleHandleRequestForTomcatBinder() throws Exception {
+        int port = NetUtils.getAvailablePort();
+        URL url = new ServiceConfigURL(
+                "http", "localhost", port, new String[] {Constants.BIND_PORT_KEY, String.valueOf(port)});
+
+        HttpServer httpServer = new TomcatHttpBinder()
+                .bind(url, new HttpHandler<HttpServletRequest, HttpServletResponse>() {
+                    @Override
+                    public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException {
+                        response.getWriter().write("Tomcat");
+                    }
+                });
+
+        String response =
+                Request.Get(url.toJavaURL().toURI()).execute().returnContent().asString();
+
+        assertThat(response, is("Tomcat"));
+
+        httpServer.close();
+        assertThat(NetUtils.isPortInUsed(port), is(false));
+    }
+}
diff --git a/dubbo-remoting-extensions/dubbo-remoting-http/src/test/resources/log4j.xml b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/resources/log4j.xml
new file mode 100644
index 0000000..ef26f07
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-http/src/test/resources/log4j.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.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+    <!-- ===================================================================== -->
+    <!-- appender config -->
+    <!-- ===================================================================== -->
+    <appender name="dubbo" class="org.apache.dubbo.common.utils.DubboAppender">
+        <param name="File" value="${user.dir}/dubbo.log"/>
+        <param name="encoding" value="GBK"/>
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="%d %p [%c:%M] - %m%n"/>
+        </layout>
+    </appender>
+
+    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n"/>
+        </layout>
+    </appender>
+    <root>
+        <level value="INFO"/>
+        <appender-ref ref="dubbo"/>
+        <appender-ref ref="CONSOLE"/>
+    </root>
+</log4j:configuration>
diff --git a/dubbo-remoting-extensions/dubbo-remoting-mina/pom.xml b/dubbo-remoting-extensions/dubbo-remoting-mina/pom.xml
index de20a94..76a5c28 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-mina/pom.xml
+++ b/dubbo-remoting-extensions/dubbo-remoting-mina/pom.xml
@@ -23,7 +23,7 @@
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-remoting-mina</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
diff --git a/dubbo-remoting-extensions/dubbo-remoting-mina/src/main/java/org/apache/dubbo/remoting/transport/mina/MinaServer.java b/dubbo-remoting-extensions/dubbo-remoting-mina/src/main/java/org/apache/dubbo/remoting/transport/mina/MinaServer.java
index f88b967..4ab56ff 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-mina/src/main/java/org/apache/dubbo/remoting/transport/mina/MinaServer.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-mina/src/main/java/org/apache/dubbo/remoting/transport/mina/MinaServer.java
@@ -40,6 +40,7 @@
 import java.util.concurrent.Executors;
 
 import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY;
+import static org.apache.dubbo.config.Constants.SERVER_THREAD_POOL_NAME;
 import static org.apache.dubbo.remoting.Constants.DEFAULT_IO_THREADS;
 
 /**
@@ -82,6 +83,11 @@
     }
 
     @Override
+    protected int getChannelsSize() {
+        return getChannels().size();
+    }
+
+    @Override
     public Collection<Channel> getChannels() {
         Set<IoSession> sessions = acceptor.getManagedSessions(getBindAddress());
         Collection<Channel> channels = new HashSet<Channel>();
diff --git a/dubbo-remoting-extensions/dubbo-remoting-mina/src/test/java/org/apache/remoting/transport/mina/MinaClientToServerTest.java b/dubbo-remoting-extensions/dubbo-remoting-mina/src/test/java/org/apache/remoting/transport/mina/MinaClientToServerTest.java
index d1c0525..e32ce8f 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-mina/src/test/java/org/apache/remoting/transport/mina/MinaClientToServerTest.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-mina/src/test/java/org/apache/remoting/transport/mina/MinaClientToServerTest.java
@@ -22,10 +22,14 @@
 import org.apache.dubbo.remoting.exchange.ExchangeServer;
 import org.apache.dubbo.remoting.exchange.Exchangers;
 import org.apache.dubbo.remoting.exchange.support.Replier;
+import org.junit.jupiter.api.Disabled;
+
+import org.junit.jupiter.api.Disabled;
 
 /**
  * MinaServerClientTest
  */
+@Disabled
 public class MinaClientToServerTest extends ClientToServerTest {
 
     @Override
diff --git a/dubbo-remoting-extensions/dubbo-remoting-mina/src/test/resources/security/serialize.allowlist b/dubbo-remoting-extensions/dubbo-remoting-mina/src/test/resources/security/serialize.allowlist
new file mode 100644
index 0000000..cca5734
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-mina/src/test/resources/security/serialize.allowlist
@@ -0,0 +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
+#
+#       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.
+#
+#
+
+org.apache.remoting.transport.mina.World
+org.apache.remoting.transport.mina.Hello
diff --git a/dubbo-remoting-extensions/dubbo-remoting-p2p/pom.xml b/dubbo-remoting-extensions/dubbo-remoting-p2p/pom.xml
index 8961e35..75e82c5 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-p2p/pom.xml
+++ b/dubbo-remoting-extensions/dubbo-remoting-p2p/pom.xml
@@ -23,7 +23,7 @@
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-remoting-p2p</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
diff --git a/dubbo-remoting-extensions/dubbo-remoting-p2p/src/test/java/org/apache/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworkerTest.java b/dubbo-remoting-extensions/dubbo-remoting-p2p/src/test/java/org/apache/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworkerTest.java
index 573f245..18d3344 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-p2p/src/test/java/org/apache/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworkerTest.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-p2p/src/test/java/org/apache/dubbo/remoting/p2p/exchange/support/MulticastExchangeNetworkerTest.java
@@ -27,6 +27,7 @@
 import org.apache.dubbo.remoting.p2p.Networkers;
 import org.apache.dubbo.remoting.p2p.Peer;
 
+import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.junit.jupiter.api.Test;
 
 import java.util.concurrent.CompletableFuture;
@@ -48,7 +49,8 @@
 
         final CountDownLatch countDownLatch = new CountDownLatch(1);
         Peer peer1 = multicastExchangeNetworker.lookup(URL.valueOf(groupURL))
-                .join(URL.valueOf("exchange://0.0.0.0:" + NetUtils.getAvailablePort() + "?exchanger=header"), new ExchangeHandlerAdapter() {
+                .join(URL.valueOf("exchange://0.0.0.0:" + NetUtils.getAvailablePort() + "?exchanger=header"),
+                    new ExchangeHandlerAdapter(FrameworkModel.defaultModel()) {
                     @Override
                     public CompletableFuture<Object> reply(ExchangeChannel channel, Object msg) throws RemotingException {
                         countDownLatch.countDown();
diff --git a/dubbo-remoting-extensions/dubbo-remoting-p2p/src/test/java/org/apache/dubbo/remoting/p2p/support/FileNetworkerTest.java b/dubbo-remoting-extensions/dubbo-remoting-p2p/src/test/java/org/apache/dubbo/remoting/p2p/support/FileNetworkerTest.java
index d947761..24911f0 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-p2p/src/test/java/org/apache/dubbo/remoting/p2p/support/FileNetworkerTest.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-p2p/src/test/java/org/apache/dubbo/remoting/p2p/support/FileNetworkerTest.java
@@ -35,20 +35,23 @@
 
 import static org.mockito.Mockito.mock;
 
-public class FileNetworkerTest {
+class FileNetworkerTest {
+
+    @TempDir
+    Path folder;
 
     @BeforeEach
-    public void setUp(@TempDir Path folder) throws Exception {
+    public void setUp() throws Exception {
         folder.toFile().createNewFile();
     }
 
     @AfterEach
-    public void tearDown(@TempDir Path folder) {
+    public void tearDown() {
         folder.getFileName().toAbsolutePath().toFile().delete();
     }
 
     @Test
-    public void testJoin(@TempDir Path folder) throws RemotingException, InterruptedException {
+    void testJoin() throws RemotingException, InterruptedException {
         final String groupURL = "file:///" + folder.getFileName().toAbsolutePath();
 
         FileNetworker networker = new FileNetworker();
diff --git a/dubbo-remoting-extensions/dubbo-remoting-quic/README.md b/dubbo-remoting-extensions/dubbo-remoting-quic/README.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dubbo-remoting-extensions/dubbo-remoting-quic/README.md
diff --git a/dubbo-remoting-extensions/dubbo-remoting-quic/pom.xml b/dubbo-remoting-extensions/dubbo-remoting-quic/pom.xml
index 627e3d1..fe08542 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-quic/pom.xml
+++ b/dubbo-remoting-extensions/dubbo-remoting-quic/pom.xml
@@ -29,7 +29,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-remoting-quic</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
 
 
     <dependencies>
@@ -38,7 +38,6 @@
             <groupId>io.netty.incubator</groupId>
             <artifactId>netty-incubator-codec-quic</artifactId>
             <version>0.0.14.Final</version>
-            <classifier>${os.detected.classifier}</classifier>
         </dependency>
 
         <dependency>
@@ -46,7 +45,11 @@
             <artifactId>dubbo-remoting-api</artifactId>
             <optional>true</optional>
         </dependency>
-
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-all</artifactId>
diff --git a/dubbo-remoting-extensions/dubbo-remoting-quic/src/main/java/org/apache/dubbo/remoting/transport/quic/QuicNettyServer.java b/dubbo-remoting-extensions/dubbo-remoting-quic/src/main/java/org/apache/dubbo/remoting/transport/quic/QuicNettyServer.java
index d162046..ce3d187 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-quic/src/main/java/org/apache/dubbo/remoting/transport/quic/QuicNettyServer.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-quic/src/main/java/org/apache/dubbo/remoting/transport/quic/QuicNettyServer.java
@@ -56,6 +56,7 @@
 public class QuicNettyServer extends AbstractServer implements RemotingServer {
 
     private static final Logger logger = LoggerFactory.getLogger(QuicNettyServer.class);
+    private static final String SERVER_THREAD_POOL_NAME = "DubboServerHandler";
     /**
      * the cache for alive worker channel.
      * <ip:port, dubbo channel>
@@ -173,6 +174,11 @@
     }
 
     @Override
+    protected int getChannelsSize() {
+        return channels == null ? 0 : channels.size();
+    }
+
+    @Override
     public Collection<Channel> getChannels() {
         Collection<Channel> chs = new ArrayList<>(this.channels.size());
         // pick channels from NettyServerHandler ( needless to check connectivity )
diff --git a/dubbo-remoting-extensions/dubbo-remoting-redis/pom.xml b/dubbo-remoting-extensions/dubbo-remoting-redis/pom.xml
index f8c982b..26f76bf 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-redis/pom.xml
+++ b/dubbo-remoting-extensions/dubbo-remoting-redis/pom.xml
@@ -23,7 +23,7 @@
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-remoting-redis</artifactId>
 
     <packaging>jar</packaging>
diff --git a/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/ClusterRedisClient.java b/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/ClusterRedisClient.java
index fb5f4ff..87c48ee 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/ClusterRedisClient.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/ClusterRedisClient.java
@@ -75,7 +75,10 @@
         for (JedisPool jedisPool : poolMap.values()) {
             Jedis jedis = jedisPool.getResource();
             if (jedis.isConnected()) {
+                jedisPool.returnResource(jedis);
                 return true;
+            } else {
+                jedisPool.returnResource(jedis);
             }
         }
         return false;
@@ -98,7 +101,7 @@
         for (JedisPool jedisPool : nodes.values()) {
             Jedis jedis = jedisPool.getResource();
             result.addAll(scan(jedis, pattern));
-            jedis.close();
+            jedisPool.returnResource(jedis);
         }
         return result;
     }
diff --git a/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/MonoRedisClient.java b/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/MonoRedisClient.java
index 07cb9ed..64e4ebb 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/MonoRedisClient.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/MonoRedisClient.java
@@ -55,7 +55,7 @@
     public Long publish(String channel, String message) {
         Jedis jedis = jedisPool.getResource();
         Long result = jedis.publish(channel, message);
-        jedis.close();
+        jedisPool.returnResource(jedis);
         return result;
     }
 
@@ -63,7 +63,7 @@
     public boolean isConnected() {
         Jedis jedis = jedisPool.getResource();
         boolean connected = jedis.isConnected();
-        jedis.close();
+        jedisPool.returnResource(jedis);
         return connected;
     }
 
@@ -76,7 +76,7 @@
     public Long hdel(String key, String... fields) {
         Jedis jedis = jedisPool.getResource();
         Long result = jedis.hdel(key, fields);
-        jedis.close();
+        jedisPool.returnResource(jedis);
         return result;
     }
 
@@ -84,7 +84,7 @@
     public Set<String> scan(String pattern) {
         Jedis jedis = jedisPool.getResource();
         Set<String> result = super.scan(jedis, pattern);
-        jedis.close();
+        jedisPool.returnResource(jedis);
         return result;
     }
 
@@ -92,7 +92,7 @@
     public Map<String, String> hgetAll(String key) {
         Jedis jedis = jedisPool.getResource();
         Map<String, String> result = jedis.hgetAll(key);
-        jedis.close();
+        jedisPool.returnResource(jedis);
         return result;
     }
 
diff --git a/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/SentinelRedisClient.java b/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/SentinelRedisClient.java
index 137a379..3fb4afa 100644
--- a/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/SentinelRedisClient.java
+++ b/dubbo-remoting-extensions/dubbo-remoting-redis/src/main/java/org/apache/dubbo/remoting/redis/jedis/SentinelRedisClient.java
@@ -58,7 +58,7 @@
     public Long hset(String key, String field, String value) {
         Jedis jedis = sentinelPool.getResource();
         Long result = jedis.hset(key, field, value);
-        jedis.close();
+        sentinelPool.returnResource(jedis);
         return result;
     }
 
@@ -66,7 +66,7 @@
     public Long publish(String channel, String message) {
         Jedis jedis = sentinelPool.getResource();
         Long result = jedis.publish(channel, message);
-        jedis.close();
+        sentinelPool.returnResource(jedis);
         return result;
     }
 
@@ -74,7 +74,7 @@
     public boolean isConnected() {
         Jedis jedis = sentinelPool.getResource();
         boolean result = jedis.isConnected();
-        jedis.close();
+        sentinelPool.returnResource(jedis);
         return result;
     }
 
@@ -87,7 +87,7 @@
     public Long hdel(String key, String... fields) {
         Jedis jedis = sentinelPool.getResource();
         Long result = jedis.hdel(key, fields);
-        jedis.close();
+        sentinelPool.returnResource(jedis);
         return result;
     }
 
@@ -95,7 +95,7 @@
     public Set<String> scan(String pattern) {
         Jedis jedis = sentinelPool.getResource();
         Set<String> result = scan(jedis, pattern);
-        jedis.close();
+        sentinelPool.returnResource(jedis);
         return result;
     }
 
@@ -103,7 +103,7 @@
     public Map<String, String> hgetAll(String key) {
         Jedis jedis = sentinelPool.getResource();
         Map<String, String> result = jedis.hgetAll(key);
-        jedis.close();
+        sentinelPool.returnResource(jedis);
         return result;
     }
 
@@ -111,7 +111,7 @@
     public void psubscribe(JedisPubSub jedisPubSub, String... patterns) {
         Jedis jedis = sentinelPool.getResource();
         jedis.psubscribe(jedisPubSub, patterns);
-        jedis.close();
+        sentinelPool.returnResource(jedis);
     }
 
     @Override
diff --git a/dubbo-remoting-extensions/pom.xml b/dubbo-remoting-extensions/pom.xml
index 9b52ee1..ad8a953 100644
--- a/dubbo-remoting-extensions/pom.xml
+++ b/dubbo-remoting-extensions/pom.xml
@@ -37,6 +37,7 @@
         <module>dubbo-remoting-mina</module>
         <module>dubbo-remoting-p2p</module>
         <module>dubbo-remoting-redis</module>
+        <module>dubbo-remoting-http</module>
     </modules>
 
 
diff --git a/dubbo-rpc-extensions/dubbo-rpc-hessian/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-hessian/pom.xml
index b37c0d2..5e78ad2 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-hessian/pom.xml
+++ b/dubbo-rpc-extensions/dubbo-rpc-hessian/pom.xml
@@ -24,7 +24,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-rpc-hessian</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
 
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
@@ -64,7 +64,7 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-native-hession</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
diff --git a/dubbo-rpc-extensions/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocol.java b/dubbo-rpc-extensions/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocol.java
index c7b4fce..a8bf40d 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocol.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-hessian/src/main/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocol.java
@@ -171,11 +171,11 @@
         }
     }
 
-    private class HessianHandler implements HttpHandler {
+    private class HessianHandler implements HttpHandler<HttpServletRequest, HttpServletResponse> {
 
         @Override
         public void handle(HttpServletRequest request, HttpServletResponse response)
-                throws IOException, ServletException {
+            throws IOException {
             String uri = request.getRequestURI();
             HessianSkeleton skeleton = skeletonMap.get(uri);
             if (!"POST".equalsIgnoreCase(request.getMethod())) {
@@ -196,7 +196,7 @@
                 try {
                     skeleton.invoke(request.getInputStream(), response.getOutputStream(), Hessian2FactoryInitializer.getInstance().getSerializerFactory());
                 } catch (Throwable e) {
-                    throw new ServletException(e);
+                    throw new RuntimeException(new ServletException(e));
                 }
             }
         }
diff --git a/dubbo-rpc-extensions/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocolTest.java b/dubbo-rpc-extensions/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocolTest.java
index 8eebc75..16f3231 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocolTest.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-hessian/src/test/java/org/apache/dubbo/rpc/protocol/hessian/HessianProtocolTest.java
@@ -34,8 +34,8 @@
 import org.apache.dubbo.rpc.RpcException;
 import org.apache.dubbo.rpc.cluster.Cluster;
 import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
+import org.apache.dubbo.rpc.model.FrameworkModel;
 import org.apache.dubbo.rpc.service.GenericService;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -45,13 +45,14 @@
 import java.io.IOException;
 import java.util.Collections;
 
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
 import static org.junit.jupiter.api.Assertions.fail;
 
 /**
  * HessianProtocolTest
  */
 public class HessianProtocolTest {
-    
+
     @AfterEach
     public void after() {
         ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("hessian").destroy();
@@ -162,7 +163,7 @@
         invoker.destroy();
         exporter.unexport();
     }
-    
+
     @Test
     public void testOverload() {
         HessianServiceImpl server = new HessianServiceImpl();
@@ -181,7 +182,7 @@
         invoker.destroy();
         exporter.unexport();
     }
-    
+
     @Test
     public void testHttpClient() {
         HessianServiceImpl server = new HessianServiceImpl();
@@ -199,7 +200,7 @@
         invoker.destroy();
         exporter.unexport();
     }
-    
+
     @Test
     public void testTimeOut() {
         HessianServiceImpl server = new HessianServiceImpl();
@@ -219,9 +220,9 @@
             invoker.destroy();
             exporter.unexport();
         }
-        
+
     }
-    
+
     @Test
     public void testCustomException() {
         HessianServiceImpl server = new HessianServiceImpl();
@@ -236,20 +237,23 @@
             client.customException();
             fail();
         } catch (HessianServiceImpl.MyException expected) {
-        
+
         }
         invoker.destroy();
         exporter.unexport();
     }
-    
-    
+
+
     @Test
     public void testRemoteApplicationName() {
         HessianServiceImpl server = new HessianServiceImpl();
         ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
         Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
         int port = NetUtils.getAvailablePort();
-        URL url = URL.valueOf("hessian://127.0.0.1:" + port + "/" + HessianService.class.getName() + "?version=1.0.0&hessian.overload.method=true").addParameter("application", "consumer");
+        String url1 = "hessian://127.0.0.1:" + port + "/" + HessianService.class.getName() + "?version=1.0.0&hessian.overload.method=true&"
+            + INTERFACE_KEY + "=org.apache.dubbo.rpc.protocol.hessian.HessianService";
+        URL url = URL.valueOf(url1, FrameworkModel.defaultModel().defaultApplication().getDefaultModule()).addParameter("application", "consumer");
+
         Exporter<HessianService> exporter = protocol.export(proxyFactory.getInvoker(server, HessianService.class, url));
         Invoker<HessianService> invoker = protocol.refer(HessianService.class, url);
         Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-http/pom.xml
index 2aa4ca3..35a6ef9 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-http/pom.xml
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/pom.xml
@@ -26,7 +26,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-rpc-http</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
 
     <description>The JSON-RPC module of dubbo project</description>
 
diff --git a/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java
index 6bb5569..cc2c318 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-http/src/main/java/org/apache/dubbo/rpc/protocol/http/HttpProtocol.java
@@ -69,7 +69,7 @@
         return 80;
     }
 
-    private class InternalHandler implements HttpHandler {
+    private class InternalHandler implements HttpHandler<HttpServletRequest,HttpServletResponse> {
 
         private boolean cors;
 
@@ -78,8 +78,7 @@
         }
 
         @Override
-        public void handle(HttpServletRequest request, HttpServletResponse response)
-            throws ServletException {
+        public void handle(HttpServletRequest request, HttpServletResponse response) {
             String uri = request.getRequestURI();
             JsonRpcServer skeleton = skeletonMap.get(uri);
             if (cors) {
@@ -95,7 +94,7 @@
                 try {
                     skeleton.handle(request.getInputStream(), response.getOutputStream());
                 } catch (Throwable e) {
-                    throw new ServletException(e);
+                    throw new RuntimeException(new ServletException(e));
                 }
             } else {
                 response.setStatus(500);
diff --git a/dubbo-rpc-extensions/dubbo-rpc-memcached/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-memcached/pom.xml
index 0af66f7..2369ea9 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-memcached/pom.xml
+++ b/dubbo-rpc-extensions/dubbo-rpc-memcached/pom.xml
@@ -24,7 +24,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-rpc-memcached</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
     <description>The memcached rpc module of dubbo project</description>
diff --git a/dubbo-rpc-extensions/dubbo-rpc-native-thrift/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-native-thrift/pom.xml
index 42b39f1..1f6b0cf 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-native-thrift/pom.xml
+++ b/dubbo-rpc-extensions/dubbo-rpc-native-thrift/pom.xml
@@ -27,7 +27,7 @@
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
     <description>The thrift rpc module of dubbo project</description>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
     </properties>
diff --git a/dubbo-rpc-extensions/dubbo-rpc-redis/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-redis/pom.xml
index b0bff37..259a8ad 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-redis/pom.xml
+++ b/dubbo-rpc-extensions/dubbo-rpc-redis/pom.xml
@@ -24,7 +24,7 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-rpc-redis</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
     <description>The redis rpc module of dubbo project</description>
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-rest/pom.xml
new file mode 100644
index 0000000..e8704b7
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/pom.xml
@@ -0,0 +1,169 @@
+<?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>
+    <parent>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <artifactId>dubbo-rpc-extensions</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>dubbo-rpc-rest</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <description>The JAX-RS rpc module of dubbo project</description>
+    <version>${revision}</version>
+    <properties>
+        <skip_maven_deploy>false</skip_maven_deploy>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-metadata-rest</artifactId>
+            <version>${version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-cluster</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-remoting-http</artifactId>
+            <version>${version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-client</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
+        <!-- optional dependencies ==================== -->
+
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-netty4</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jdk-http</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jackson-provider</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxb-provider</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- swagger -->
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.ws.rs</groupId>
+                    <artifactId>jsr311-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-jaxrs</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.ws.rs</groupId>
+                    <artifactId>jsr311-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-serialization-jdk</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!--        to use utils -->
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metadata-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec-http</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport</artifactId>
+            <scope>compile</scope>
+        </dependency>
+
+        <!-- Spring Web MVC -->
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+
+</project>
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java
new file mode 100644
index 0000000..5aa4315
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/Constants.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+/**
+ * Constants definition.
+ */
+public interface Constants {
+    String KEEP_ALIVE_KEY = "keepalive";
+
+    boolean DEFAULT_KEEP_ALIVE = true;
+
+    String EXTENSION_KEY = "extension";
+
+    // http server
+
+    String NETTY_HTTP = "netty_http";
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyHttpRestServer.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyHttpRestServer.java
new file mode 100644
index 0000000..76595ba
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/NettyHttpRestServer.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelOption;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyServer;
+import org.apache.dubbo.rpc.protocol.rest.netty.RestHttpRequestDecoder;
+import org.apache.dubbo.rpc.protocol.rest.netty.UnSharedHandlerCreator;
+import org.apache.dubbo.rpc.protocol.rest.netty.ssl.SslServerTlsHandler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.apache.dubbo.common.constants.CommonConstants.BACKLOG_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.IO_THREADS_KEY;
+import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY;
+import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY;
+import static org.apache.dubbo.remoting.Constants.DEFAULT_IO_THREADS;
+
+/**
+ * netty http server
+ */
+public class NettyHttpRestServer implements RestProtocolServer {
+
+    private ServiceDeployer serviceDeployer = new ServiceDeployer();
+    private NettyServer server = getNettyServer();
+
+    /**
+     * for triple override
+     *
+     * @return
+     */
+    protected NettyServer getNettyServer() {
+        return new NettyServer();
+    }
+
+    private String address;
+
+    private final Map<String, Object> attributes = new ConcurrentHashMap<>();
+
+    @Override
+    public String getAddress() {
+        return address;
+    }
+
+    @Override
+    public void setAddress(String address) {
+
+        this.address = address;
+    }
+
+    @Override
+    public void close() {
+        server.stop();
+    }
+
+    @Override
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public void start(URL url) {
+
+        registerExtension(url);
+
+        String bindIp = url.getParameter(BIND_IP_KEY, url.getHost());
+        if (!url.isAnyHost() && NetUtils.isValidLocalHost(bindIp)) {
+            server.setHostname(bindIp);
+        }
+        server.setPort(url.getParameter(BIND_PORT_KEY, url.getPort()));
+
+        // child options
+        server.setChildChannelOptions(getChildChannelOptionMap(url));
+
+        // set options
+        server.setChannelOptions(getChannelOptionMap(url));
+        // set unshared callback
+        server.setUnSharedHandlerCallBack(getUnSharedHttpChannelHandlers());
+        // set channel handler  and @Shared
+        server.setChannelHandlers(getChannelHandlers(url));
+        server.setIoWorkerCount(url.getParameter(IO_THREADS_KEY, DEFAULT_IO_THREADS));
+        server.start(url);
+    }
+
+    private UnSharedHandlerCreator getUnSharedHttpChannelHandlers() {
+        return new UnSharedHandlerCreator() {
+            @Override
+            public List<ChannelHandler> getUnSharedHandlers(URL url) {
+                return Arrays.asList(
+                        //  add SslServerTlsHandler
+                        new SslServerTlsHandler(url),
+                        new HttpRequestDecoder(
+                                url.getParameter(
+                                        RestConstant.MAX_INITIAL_LINE_LENGTH_PARAM,
+                                        RestConstant.MAX_INITIAL_LINE_LENGTH),
+                                url.getParameter(RestConstant.MAX_HEADER_SIZE_PARAM, RestConstant.MAX_HEADER_SIZE),
+                                url.getParameter(RestConstant.MAX_CHUNK_SIZE_PARAM, RestConstant.MAX_CHUNK_SIZE)),
+                        new HttpObjectAggregator(
+                                url.getParameter(RestConstant.MAX_REQUEST_SIZE_PARAM, RestConstant.MAX_REQUEST_SIZE)),
+                        new HttpResponseEncoder(),
+                        new RestHttpRequestDecoder(url, serviceDeployer));
+            }
+        };
+    }
+
+    /**
+     * create child channel options map
+     *
+     * @param url
+     * @return
+     */
+    protected Map<ChannelOption, Object> getChildChannelOptionMap(URL url) {
+        Map<ChannelOption, Object> channelOption = new HashMap<>();
+        channelOption.put(
+                ChannelOption.SO_KEEPALIVE, url.getParameter(Constants.KEEP_ALIVE_KEY, Constants.DEFAULT_KEEP_ALIVE));
+        return channelOption;
+    }
+
+    /**
+     * create channel options map
+     *
+     * @param url
+     * @return
+     */
+    protected Map<ChannelOption, Object> getChannelOptionMap(URL url) {
+        Map<ChannelOption, Object> options = new HashMap<>();
+
+        options.put(ChannelOption.SO_REUSEADDR, Boolean.TRUE);
+        options.put(ChannelOption.TCP_NODELAY, Boolean.TRUE);
+        options.put(
+                ChannelOption.SO_BACKLOG,
+                url.getPositiveParameter(BACKLOG_KEY, org.apache.dubbo.remoting.Constants.DEFAULT_BACKLOG));
+        options.put(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
+        return options;
+    }
+
+    /**
+     * create channel handler
+     *
+     * @param url
+     * @return
+     */
+    protected List<ChannelHandler> getChannelHandlers(URL url) {
+        List<ChannelHandler> channelHandlers = new ArrayList<>();
+
+        return channelHandlers;
+    }
+
+    @Override
+    public void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker) {
+        serviceDeployer.deploy(serviceRestMetadata, invoker);
+    }
+
+    @Override
+    public void undeploy(ServiceRestMetadata serviceRestMetadata) {
+        serviceDeployer.undeploy(serviceRestMetadata);
+    }
+
+    private void registerExtension(URL url) {
+        serviceDeployer.registerExtension(url);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java
new file mode 100644
index 0000000..1cc9e6a
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/PathAndInvokerMapper.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.metadata.extension.rest.api.PathMatcher;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.protocol.rest.exception.DoublePathCheckException;
+import org.apache.dubbo.rpc.protocol.rest.pair.InvokerAndRestMethodMetadataPair;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * save the path & metadata info mapping
+ */
+public class PathAndInvokerMapper {
+    private static final ErrorTypeAwareLogger logger =
+            LoggerFactory.getErrorTypeAwareLogger(PathAndInvokerMapper.class);
+
+    private final Map<PathMatcher, InvokerAndRestMethodMetadataPair> pathToServiceMapContainPathVariable =
+            new ConcurrentHashMap<>();
+    private final Map<PathMatcher, InvokerAndRestMethodMetadataPair> pathToServiceMapNoPathVariable =
+            new ConcurrentHashMap<>();
+
+    // for http method compare 405
+    private final Map<PathMatcher, Set<String>> pathMatcherToHttpMethodMap = new HashMap<>();
+
+    /**
+     * deploy path metadata
+     *
+     * @param metadataMap
+     * @param invoker
+     */
+    public void addPathAndInvoker(Map<PathMatcher, RestMethodMetadata> metadataMap, Invoker invoker) {
+
+        metadataMap.entrySet().stream().forEach(entry -> {
+            PathMatcher pathMatcher = entry.getKey();
+            if (pathMatcher.hasPathVariable()) {
+                addPathMatcherToPathMap(
+                        pathMatcher,
+                        pathToServiceMapContainPathVariable,
+                        InvokerAndRestMethodMetadataPair.pair(invoker, entry.getValue()));
+            } else {
+                addPathMatcherToPathMap(
+                        pathMatcher,
+                        pathToServiceMapNoPathVariable,
+                        InvokerAndRestMethodMetadataPair.pair(invoker, entry.getValue()));
+            }
+        });
+    }
+
+    /**
+     * get rest method metadata by path matcher
+     *
+     * @param pathMatcher
+     * @return
+     */
+    public InvokerAndRestMethodMetadataPair getRestMethodMetadata(PathMatcher pathMatcher) {
+
+        // first search from pathToServiceMapNoPathVariable
+        if (pathToServiceMapNoPathVariable.containsKey(pathMatcher)) {
+            return pathToServiceMapNoPathVariable.get(pathMatcher);
+        }
+
+        // second search from pathToServiceMapContainPathVariable
+        if (pathToServiceMapContainPathVariable.containsKey(pathMatcher)) {
+            return pathToServiceMapContainPathVariable.get(pathMatcher);
+        }
+
+        return null;
+    }
+
+    /**
+     * undeploy path metadata
+     *
+     * @param pathMatcher
+     */
+    public void removePath(PathMatcher pathMatcher) {
+
+        InvokerAndRestMethodMetadataPair containPathVariablePair =
+                pathToServiceMapContainPathVariable.remove(pathMatcher);
+
+        InvokerAndRestMethodMetadataPair unContainPathVariablePair = pathToServiceMapNoPathVariable.remove(pathMatcher);
+        logger.info("dubbo rest undeploy pathMatcher:" + pathMatcher
+                + ", and path variable method is :"
+                + (containPathVariablePair == null
+                        ? null
+                        : containPathVariablePair.getRestMethodMetadata().getReflectMethod())
+                + ", and no path variable  method is :"
+                + (unContainPathVariablePair == null
+                        ? null
+                        : unContainPathVariablePair.getRestMethodMetadata().getReflectMethod()));
+    }
+
+    public void addPathMatcherToPathMap(
+            PathMatcher pathMatcher,
+            Map<PathMatcher, InvokerAndRestMethodMetadataPair> pathMatcherPairMap,
+            InvokerAndRestMethodMetadataPair invokerRestMethodMetadataPair) {
+
+        if (pathMatcherPairMap.containsKey(pathMatcher)) {
+
+            // cover the old service metadata when  current interface is old interface & current method desc equals
+            // old`s method desc,else ,throw double check exception
+
+            InvokerAndRestMethodMetadataPair beforeMetadata = pathMatcherPairMap.get(pathMatcher);
+            // true when reExport
+            if (!invokerRestMethodMetadataPair.compareServiceMethod(beforeMetadata)) {
+                throw new DoublePathCheckException("dubbo rest double path check error, current path is: " + pathMatcher
+                        + " ,and service method is: "
+                        + invokerRestMethodMetadataPair.getRestMethodMetadata().getReflectMethod()
+                        + "before service  method is: "
+                        + beforeMetadata.getRestMethodMetadata().getReflectMethod());
+            }
+        }
+
+        pathMatcherPairMap.put(pathMatcher, invokerRestMethodMetadataPair);
+
+        addPathMatcherToHttpMethodsMap(pathMatcher);
+
+        logger.info("dubbo rest deploy pathMatcher:" + pathMatcher + ", and service method is :"
+                + invokerRestMethodMetadataPair.getRestMethodMetadata().getReflectMethod());
+    }
+
+    private void addPathMatcherToHttpMethodsMap(PathMatcher pathMatcher) {
+
+        PathMatcher newPathMatcher = PathMatcher.convertPathMatcher(pathMatcher);
+
+        if (!pathMatcherToHttpMethodMap.containsKey(newPathMatcher)) {
+            HashSet<String> httpMethods = new HashSet<>();
+
+            httpMethods.add(pathMatcher.getHttpMethod());
+
+            pathMatcherToHttpMethodMap.put(newPathMatcher, httpMethods);
+        }
+
+        Set<String> httpMethods = pathMatcherToHttpMethodMap.get(newPathMatcher);
+
+        httpMethods.add(newPathMatcher.getHttpMethod());
+    }
+
+    public boolean isHttpMethodAllowed(PathMatcher pathMatcher) {
+
+        PathMatcher newPathMatcher = PathMatcher.convertPathMatcher(pathMatcher);
+        if (!pathMatcherToHttpMethodMap.containsKey(newPathMatcher)) {
+            return false;
+        }
+
+        Set<String> httpMethods = pathMatcherToHttpMethodMap.get(newPathMatcher);
+
+        return httpMethods.contains(newPathMatcher.getHttpMethod());
+    }
+
+    public String pathHttpMethods(PathMatcher pathMatcher) {
+
+        PathMatcher newPathMatcher = PathMatcher.convertPathMatcher(pathMatcher);
+        if (!pathMatcherToHttpMethodMap.containsKey(newPathMatcher)) {
+            return null;
+        }
+
+        Set<String> httpMethods = pathMatcherToHttpMethodMap.get(newPathMatcher);
+
+        return httpMethods.toString();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ReferenceCountedClient.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ReferenceCountedClient.java
new file mode 100644
index 0000000..b7dbaa0
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ReferenceCountedClient.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.reference.ReferenceCountedResource;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.factory.RestClientFactory;
+
+import java.util.concurrent.ConcurrentMap;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT;
+
+public class ReferenceCountedClient<T extends RestClient> extends ReferenceCountedResource {
+    private static final ErrorTypeAwareLogger logger =
+            LoggerFactory.getErrorTypeAwareLogger(ReferenceCountedClient.class);
+
+    private ConcurrentMap<String, ReferenceCountedClient<? extends RestClient>> clients;
+    private URL url;
+    private RestClientFactory clientFactory;
+
+    private T client;
+
+    public ReferenceCountedClient(
+            T client,
+            ConcurrentMap<String, ReferenceCountedClient<? extends RestClient>> clients,
+            RestClientFactory clientFactory,
+            URL url) {
+        this.client = client;
+        this.clients = clients;
+        this.clientFactory = clientFactory;
+        this.url = url;
+    }
+
+    public T getClient() {
+
+        // for client destroy and create right now, only  lock current client
+        synchronized (this) {
+            ReferenceCountedClient<? extends RestClient> referenceCountedClient = clients.get(url.getAddress());
+
+            // for double check
+            if (referenceCountedClient.isDestroyed()) {
+                synchronized (this) {
+                    referenceCountedClient = clients.get(url.getAddress());
+                    if (referenceCountedClient.isDestroyed()) {
+                        RestClient restClient = clientFactory.createRestClient(url);
+                        clients.put(
+                                url.getAddress(), new ReferenceCountedClient(restClient, clients, clientFactory, url));
+                        return (T) restClient;
+                    } else {
+                        return (T) referenceCountedClient.client;
+                    }
+                }
+
+            } else {
+                return client;
+            }
+        }
+    }
+
+    public boolean isDestroyed() {
+        return client.isClosed();
+    }
+
+    @Override
+    protected void destroy() {
+        try {
+            client.close();
+        } catch (Exception e) {
+            logger.error(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", "Close resteasy client error", e);
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestConstraintViolation.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestConstraintViolation.java
new file mode 100644
index 0000000..8ec6454
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestConstraintViolation.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+
+@XmlRootElement(name = "constraintViolation")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RestConstraintViolation implements Serializable {
+
+    private static final long serialVersionUID = -23497234978L;
+
+    private String path;
+    private String message;
+    private String value;
+
+    public RestConstraintViolation(String path, String message, String value) {
+        this.path = path;
+        this.message = message;
+        this.value = value;
+    }
+
+    public RestConstraintViolation() {}
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestHeaderEnum.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestHeaderEnum.java
new file mode 100644
index 0000000..7eab6db
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestHeaderEnum.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+
+public enum RestHeaderEnum {
+    CONTENT_TYPE(RestConstant.CONTENT_TYPE),
+    ACCEPT(RestConstant.ACCEPT),
+    GROUP(RestConstant.REST_HEADER_PREFIX + RestConstant.GROUP),
+    VERSION(RestConstant.REST_HEADER_PREFIX + RestConstant.VERSION),
+    PATH(RestConstant.REST_HEADER_PREFIX + RestConstant.PATH),
+    KEEP_ALIVE_HEADER(RestConstant.KEEP_ALIVE_HEADER),
+    CONNECTION(RestConstant.CONNECTION),
+    REST_HEADER_PREFIX(RestConstant.REST_HEADER_PREFIX),
+    TOKEN_KEY(RestConstant.REST_HEADER_PREFIX + RestConstant.TOKEN_KEY),
+    ;
+    private final String header;
+
+    RestHeaderEnum(String header) {
+        this.header = header;
+    }
+
+    public String getHeader() {
+        return header;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestInvoker.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestInvoker.java
new file mode 100644
index 0000000..ddeaf9d
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestInvoker.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.ParameterTypesComparator;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.RestResult;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.AsyncRpcResult;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.protocol.AbstractInvoker;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
+import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException;
+import org.apache.dubbo.rpc.protocol.rest.exception.PathNoFoundException;
+import org.apache.dubbo.rpc.protocol.rest.exception.RemoteServerInternalException;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager;
+import org.apache.dubbo.rpc.protocol.rest.util.HttpHeaderUtil;
+import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+public class RestInvoker<T> extends AbstractInvoker<T> {
+    private final ServiceRestMetadata serviceRestMetadata;
+    private final ReferenceCountedClient<? extends RestClient> referenceCountedClient;
+    private final Set<HttpConnectionPreBuildIntercept> httpConnectionPreBuildIntercepts;
+
+    public RestInvoker(
+            Class type,
+            URL url,
+            ReferenceCountedClient<? extends RestClient> referenceCountedClient,
+            Set<HttpConnectionPreBuildIntercept> httpConnectionPreBuildIntercepts,
+            ServiceRestMetadata serviceRestMetadata) {
+        super(type, url);
+        this.serviceRestMetadata = serviceRestMetadata;
+        this.referenceCountedClient = referenceCountedClient;
+        this.httpConnectionPreBuildIntercepts = httpConnectionPreBuildIntercepts;
+    }
+
+    @Override
+    protected Result doInvoke(Invocation invocation) {
+        try {
+
+            Map<String, Map<ParameterTypesComparator, RestMethodMetadata>> metadataMap =
+                    serviceRestMetadata.getMethodToServiceMap();
+            //  get metadata
+            RestMethodMetadata restMethodMetadata = metadataMap
+                    .get(invocation.getMethodName())
+                    .get(ParameterTypesComparator.getInstance(invocation.getParameterTypes()));
+
+            // create requestTemplate
+            RequestTemplate requestTemplate = new RequestTemplate(
+                    invocation, restMethodMetadata.getRequest().getMethod(), getUrl().getAddress());
+
+            HttpConnectionCreateContext httpConnectionCreateContext = createHttpConnectionCreateContext(
+                    invocation, serviceRestMetadata, restMethodMetadata, requestTemplate);
+
+            // fill real  data
+            for (HttpConnectionPreBuildIntercept intercept : httpConnectionPreBuildIntercepts) {
+                intercept.intercept(httpConnectionCreateContext);
+            }
+
+            // TODO check rest client cannot be reused
+            CompletableFuture<RestResult> future =
+                    referenceCountedClient.getClient().send(requestTemplate);
+            CompletableFuture<AppResponse> responseFuture = new CompletableFuture<>();
+            AsyncRpcResult asyncRpcResult = new AsyncRpcResult(responseFuture, invocation);
+            future.whenComplete((r, t) -> {
+                if (t != null) {
+                    responseFuture.completeExceptionally(t);
+                } else {
+                    AppResponse appResponse = new AppResponse();
+                    try {
+                        int responseCode = r.getResponseCode();
+                        MediaType mediaType = MediaType.TEXT_PLAIN;
+
+                        if (responseCode == 404) {
+                            responseFuture.completeExceptionally(new PathNoFoundException(r.getMessage()));
+                        } else if (400 <= responseCode && responseCode < 500) {
+                            responseFuture.completeExceptionally(new ParamParseException(r.getMessage()));
+                            // TODO add Exception Mapper
+                        } else if (responseCode >= 500) {
+                            responseFuture.completeExceptionally(new RemoteServerInternalException(r.getMessage()));
+                        } else if (responseCode < 400) {
+                            Method reflectMethod = restMethodMetadata.getReflectMethod();
+                            mediaType =
+                                    MediaTypeUtil.convertMediaType(reflectMethod.getReturnType(), r.getContentType());
+                            Object value = HttpMessageCodecManager.httpMessageDecode(
+                                    r.getBody(),
+                                    reflectMethod.getReturnType(),
+                                    reflectMethod.getGenericReturnType(),
+                                    mediaType);
+                            appResponse.setValue(value);
+                            // resolve response attribute & attachment
+                            HttpHeaderUtil.parseResponseHeader(appResponse, r);
+                            responseFuture.complete(appResponse);
+                        }
+                    } catch (Exception e) {
+                        responseFuture.completeExceptionally(e);
+                    }
+                }
+            });
+            return asyncRpcResult;
+        } catch (RpcException e) {
+            throw e;
+        }
+    }
+
+    /**
+     * create intercept context
+     *
+     * @param invocation
+     * @param serviceRestMetadata
+     * @param restMethodMetadata
+     * @param requestTemplate
+     * @return
+     */
+    private HttpConnectionCreateContext createHttpConnectionCreateContext(
+            Invocation invocation,
+            ServiceRestMetadata serviceRestMetadata,
+            RestMethodMetadata restMethodMetadata,
+            RequestTemplate requestTemplate) {
+        HttpConnectionCreateContext httpConnectionCreateContext = new HttpConnectionCreateContext();
+        httpConnectionCreateContext.setRequestTemplate(requestTemplate);
+        httpConnectionCreateContext.setRestMethodMetadata(restMethodMetadata);
+        httpConnectionCreateContext.setServiceRestMetadata(serviceRestMetadata);
+        httpConnectionCreateContext.setInvocation(invocation);
+        httpConnectionCreateContext.setUrl(getUrl());
+        return httpConnectionCreateContext;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java
new file mode 100644
index 0000000..a1c6765
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocol.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.remoting.http.RestClient;
+import org.apache.dubbo.remoting.http.factory.RestClientFactory;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ProtocolServer;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.AbstractExporter;
+import org.apache.dubbo.rpc.protocol.AbstractProtocol;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
+import org.apache.dubbo.rpc.protocol.rest.annotation.metadata.MetadataResolver;
+
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT;
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_SERVER;
+import static org.apache.dubbo.remoting.Constants.SERVER_KEY;
+import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.PATH_SEPARATOR;
+
+public class RestProtocol extends AbstractProtocol {
+
+    private static final int DEFAULT_PORT = 80;
+    private static final String DEFAULT_SERVER = Constants.NETTY_HTTP;
+
+    private final RestServerFactory serverFactory = new RestServerFactory();
+
+    private final ConcurrentMap<String, ReferenceCountedClient<? extends RestClient>> clients =
+            new ConcurrentHashMap<>();
+
+    private final RestClientFactory clientFactory;
+
+    private final Set<HttpConnectionPreBuildIntercept> httpConnectionPreBuildIntercepts;
+
+    public RestProtocol(FrameworkModel frameworkModel) {
+        this.clientFactory =
+                frameworkModel.getExtensionLoader(RestClientFactory.class).getAdaptiveExtension();
+        this.httpConnectionPreBuildIntercepts = new LinkedHashSet<>(frameworkModel
+                .getExtensionLoader(HttpConnectionPreBuildIntercept.class)
+                .getActivateExtensions());
+    }
+
+    @Override
+    public int getDefaultPort() {
+        return DEFAULT_PORT;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> Exporter<T> export(final Invoker<T> invoker) throws RpcException {
+        URL url = invoker.getUrl();
+        final String uri = serviceKey(url);
+        Exporter<T> exporter = (Exporter<T>) exporterMap.get(uri);
+        if (exporter != null) {
+            // When modifying the configuration through override, you need to re-expose the newly modified service.
+            if (Objects.equals(exporter.getInvoker().getUrl(), invoker.getUrl())) {
+                return exporter;
+            }
+        }
+
+        // resolve metadata
+        ServiceRestMetadata serviceRestMetadata = MetadataResolver.resolveProviderServiceMetadata(
+                url.getServiceModel().getProxyObject().getClass(), url, getContextPath(url));
+
+        // TODO add Extension filter
+        // create rest server
+        RestProtocolServer server = (RestProtocolServer) ConcurrentHashMapUtils.computeIfAbsent(
+                (ConcurrentMap<? super String, ? super RestProtocolServer>) serverMap, getAddr(url), restServer -> {
+                    RestProtocolServer s = serverFactory.createServer(url.getParameter(SERVER_KEY, DEFAULT_SERVER));
+                    s.setAddress(url.getAddress());
+                    s.start(url);
+                    return s;
+                });
+
+        server.deploy(serviceRestMetadata, invoker);
+
+        exporter = new AbstractExporter<T>(invoker) {
+            @Override
+            public void afterUnExport() {
+                destroyInternal(url);
+                exporterMap.remove(uri);
+                server.undeploy(serviceRestMetadata);
+            }
+        };
+        exporterMap.put(uri, exporter);
+        return exporter;
+    }
+
+    @Override
+    protected <T> Invoker<T> protocolBindingRefer(final Class<T> type, final URL url) throws RpcException {
+
+        ReferenceCountedClient<? extends RestClient> refClient = clients.get(url.getAddress());
+        if (refClient == null || refClient.isDestroyed()) {
+            synchronized (clients) {
+                refClient = clients.get(url.getAddress());
+                if (refClient == null || refClient.isDestroyed()) {
+                    refClient = ConcurrentHashMapUtils.computeIfAbsent(
+                            clients, url.getAddress(), _key -> createReferenceCountedClient(url));
+                }
+            }
+        }
+        refClient.retain();
+
+        String contextPathFromUrl = getContextPath(url);
+
+        // resolve metadata
+        ServiceRestMetadata serviceRestMetadata =
+                MetadataResolver.resolveConsumerServiceMetadata(type, url, contextPathFromUrl);
+
+        Invoker<T> invoker =
+                new RestInvoker<>(type, url, refClient, httpConnectionPreBuildIntercepts, serviceRestMetadata);
+
+        invokers.add(invoker);
+        return invoker;
+    }
+
+    /**
+     * create rest ReferenceCountedClient
+     *
+     * @param url
+     * @return
+     * @throws RpcException
+     */
+    private ReferenceCountedClient<? extends RestClient> createReferenceCountedClient(URL url) throws RpcException {
+
+        // url -> RestClient
+        RestClient restClient = clientFactory.createRestClient(url);
+
+        return new ReferenceCountedClient<>(restClient, clients, clientFactory, url);
+    }
+
+    @Override
+    public void destroy() {
+        if (logger.isInfoEnabled()) {
+            logger.info("Destroying protocol [" + this.getClass().getSimpleName() + "] ...");
+        }
+        super.destroy();
+
+        for (Map.Entry<String, ProtocolServer> entry : serverMap.entrySet()) {
+            try {
+                if (logger.isInfoEnabled()) {
+                    logger.info("Closing the rest server at " + entry.getKey());
+                }
+                entry.getValue().close();
+            } catch (Throwable t) {
+                logger.warn(PROTOCOL_ERROR_CLOSE_SERVER, "", "", "Error closing rest server", t);
+            }
+        }
+        serverMap.clear();
+
+        if (logger.isInfoEnabled()) {
+            logger.info("Closing rest clients");
+        }
+        for (ReferenceCountedClient<?> client : clients.values()) {
+            try {
+                // destroy directly regardless of the current reference count.
+                client.destroy();
+            } catch (Throwable t) {
+                logger.warn(PROTOCOL_ERROR_CLOSE_CLIENT, "", "", "Error closing rest client", t);
+            }
+        }
+        clients.clear();
+    }
+
+    /**
+     * getPath() will return: [contextpath + "/" +] path
+     * 1. contextpath is empty if user does not set through ProtocolConfig or ProviderConfig
+     * 2. path will never be empty, its default value is the interface name.
+     *
+     * @return return path only if user has explicitly gave then a value.
+     */
+    private String getContextPath(URL url) {
+        String contextPath = url.getPath();
+        if (contextPath != null) {
+            if (contextPath.equalsIgnoreCase(url.getParameter(INTERFACE_KEY))) {
+                return "";
+            }
+            if (contextPath.endsWith(url.getParameter(INTERFACE_KEY))) {
+                contextPath = contextPath.substring(0, contextPath.lastIndexOf(url.getParameter(INTERFACE_KEY)));
+            }
+            return contextPath.endsWith(PATH_SEPARATOR)
+                    ? contextPath.substring(0, contextPath.length() - 1)
+                    : contextPath;
+        } else {
+            return "";
+        }
+    }
+
+    private void destroyInternal(URL url) {
+        try {
+            ReferenceCountedClient<?> referenceCountedClient = clients.get(url.getAddress());
+            if (referenceCountedClient != null && referenceCountedClient.release()) {
+                clients.remove(url.getAddress());
+            }
+        } catch (Exception e) {
+            logger.warn(
+                    PROTOCOL_ERROR_CLOSE_CLIENT,
+                    "",
+                    "",
+                    "Failed to close unused resources in rest protocol. interfaceName [" + url.getServiceInterface()
+                            + "]",
+                    e);
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolServer.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolServer.java
new file mode 100644
index 0000000..f534b75
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestProtocolServer.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.ProtocolServer;
+
+public interface RestProtocolServer extends ProtocolServer {
+
+    void start(URL url);
+
+    void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker);
+
+    void undeploy(ServiceRestMetadata serviceRestMetadata);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java
new file mode 100644
index 0000000..9607449
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestRPCInvocationUtil.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.BaseServiceMetadata;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.PathMatcher;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParserManager;
+import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.ProviderParseContext;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException;
+import org.apache.dubbo.rpc.protocol.rest.pair.InvokerAndRestMethodMetadataPair;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.apache.dubbo.rpc.protocol.rest.util.HttpHeaderUtil;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+public class RestRPCInvocationUtil {
+
+    private static final ErrorTypeAwareLogger logger =
+            LoggerFactory.getErrorTypeAwareLogger(RestRPCInvocationUtil.class);
+
+    /**
+     * service method real args parse
+     *
+     * @param rpcInvocation
+     * @param request
+     * @param servletRequest
+     * @param servletResponse
+     * @param restMethodMetadata
+     */
+    public static void parseMethodArgs(
+            RpcInvocation rpcInvocation,
+            RequestFacade request,
+            Object servletRequest,
+            Object servletResponse,
+            RestMethodMetadata restMethodMetadata) {
+
+        try {
+            ProviderParseContext parseContext =
+                    createParseContext(request, servletRequest, servletResponse, restMethodMetadata);
+            Object[] args = ParamParserManager.providerParamParse(parseContext);
+
+            List<ArgInfo> argInfos = parseContext.getArgInfos();
+
+            for (ArgInfo argInfo : argInfos) {
+                // TODO set default value
+                if (argInfo.getParamType().isPrimitive() && args[argInfo.getIndex()] == null) {
+                    throw new ParamParseException("\n dubbo provider primitive arg not exist in request, method is: "
+                            + restMethodMetadata.getReflectMethod() + "\n type is: " + argInfo.getParamType()
+                            + " \n and arg index is: " + argInfo.getIndex());
+                }
+            }
+
+            rpcInvocation.setArguments(args);
+        } catch (Exception e) {
+            logger.error("", e.getMessage(), "", "dubbo rest provider method args parse error: ", e);
+            throw new ParamParseException(e.getMessage());
+        }
+    }
+
+    /**
+     * create parseMethodArgs context
+     *
+     * @param request
+     * @param originRequest
+     * @param originResponse
+     * @param restMethodMetadata
+     * @return
+     */
+    private static ProviderParseContext createParseContext(
+            RequestFacade request, Object originRequest, Object originResponse, RestMethodMetadata restMethodMetadata) {
+        ProviderParseContext parseContext = new ProviderParseContext(request);
+        parseContext.setResponse(originResponse);
+        parseContext.setRequest(originRequest);
+
+        Object[] objects = new Object[restMethodMetadata.getArgInfos().size()];
+        parseContext.setArgs(Arrays.asList(objects));
+        parseContext.setArgInfos(restMethodMetadata.getArgInfos());
+
+        return parseContext;
+    }
+
+    /**
+     * build RpcInvocation
+     *
+     * @param request
+     * @param restMethodMetadata
+     * @return
+     */
+    public static RpcInvocation createBaseRpcInvocation(RequestFacade request, RestMethodMetadata restMethodMetadata) {
+        RpcInvocation rpcInvocation = new RpcInvocation();
+
+        rpcInvocation.setParameterTypes(restMethodMetadata.getReflectMethod().getParameterTypes());
+        rpcInvocation.setReturnType(restMethodMetadata.getReflectMethod().getReturnType());
+        rpcInvocation.setMethodName(restMethodMetadata.getMethod().getName());
+
+        // TODO set   protocolServiceKey ,but no set method
+        //
+
+        HttpHeaderUtil.parseRequest(rpcInvocation, request);
+
+        String serviceKey = BaseServiceMetadata.buildServiceKey(
+                request.getHeader(RestHeaderEnum.PATH.getHeader()),
+                request.getHeader(RestHeaderEnum.GROUP.getHeader()),
+                request.getHeader(RestHeaderEnum.VERSION.getHeader()));
+        rpcInvocation.setTargetServiceUniqueName(serviceKey);
+
+        return rpcInvocation;
+    }
+
+    /**
+     * get InvokerAndRestMethodMetadataPair by path matcher
+     *
+     * @param pathMatcher
+     * @return
+     */
+    public static InvokerAndRestMethodMetadataPair getRestMethodMetadataAndInvokerPair(
+            PathMatcher pathMatcher, ServiceDeployer serviceDeployer) {
+
+        return serviceDeployer.getPathAndInvokerMapper().getRestMethodMetadata(pathMatcher);
+    }
+
+    /**
+     * get  InvokerAndRestMethodMetadataPair from rpc context
+     *
+     * @param request
+     * @return
+     */
+    public static InvokerAndRestMethodMetadataPair getRestMethodMetadataAndInvokerPair(RequestFacade request) {
+
+        PathMatcher pathMather = createPathMatcher(request);
+
+        return getRestMethodMetadataAndInvokerPair(pathMather, request.getServiceDeployer());
+    }
+
+    /**
+     * get  invoker by request
+     *
+     * @param request
+     * @return
+     */
+    public static Invoker getInvokerByRequest(RequestFacade request) {
+
+        PathMatcher pathMatcher = createPathMatcher(request);
+
+        return getInvoker(pathMatcher, request.getServiceDeployer());
+    }
+
+    /**
+     * get invoker by service method
+     * <p>
+     * compare method`s name,param types
+     *
+     * @param serviceMethod
+     * @return
+     */
+    public static Invoker getInvokerByServiceInvokeMethod(Method serviceMethod, ServiceDeployer serviceDeployer) {
+
+        if (serviceMethod == null) {
+            return null;
+        }
+
+        PathMatcher pathMatcher = PathMatcher.getInvokeCreatePathMatcher(serviceMethod);
+
+        InvokerAndRestMethodMetadataPair pair = getRestMethodMetadataAndInvokerPair(pathMatcher, serviceDeployer);
+
+        if (pair == null) {
+            return null;
+        }
+
+        return pair.getInvoker();
+    }
+
+    /**
+     * get invoker by path matcher
+     *
+     * @param pathMatcher
+     * @return
+     */
+    public static Invoker getInvoker(PathMatcher pathMatcher, ServiceDeployer serviceDeployer) {
+        InvokerAndRestMethodMetadataPair pair = getRestMethodMetadataAndInvokerPair(pathMatcher, serviceDeployer);
+
+        if (pair == null) {
+            return null;
+        }
+
+        return pair.getInvoker();
+    }
+
+    /**
+     * create path matcher by request
+     *
+     * @param request
+     * @return
+     */
+    public static PathMatcher createPathMatcher(RequestFacade request) {
+        String path = request.getPath();
+        String version = request.getHeader(RestHeaderEnum.VERSION.getHeader());
+        String group = request.getHeader(RestHeaderEnum.GROUP.getHeader());
+        String method = request.getMethod();
+
+        return PathMatcher.getInvokeCreatePathMatcher(path, version, group, null, method);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java
new file mode 100644
index 0000000..97e0d55
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RestServerFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+/**
+ * Only the server that implements servlet container
+ * could support something like @Context injection of servlet objects.
+ */
+public class RestServerFactory {
+
+    public RestProtocolServer createServer(String name) {
+        return new NettyHttpRestServer();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java
new file mode 100644
index 0000000..6336f1c
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapper.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.utils.ClassUtils;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler;
+import org.apache.dubbo.rpc.protocol.rest.util.ConstraintViolationExceptionConvert;
+
+public class RpcExceptionMapper implements ExceptionHandler<RpcException> {
+
+    @Override
+    public Object result(RpcException e) {
+
+        // javax dependency judge
+        if (violationDependency()) {
+            //  ConstraintViolationException judge
+            if (ConstraintViolationExceptionConvert.needConvert(e)) {
+                return ConstraintViolationExceptionConvert.handleConstraintViolationException(e);
+            }
+        }
+
+        return "Internal server error: " + e.getMessage();
+    }
+
+    private boolean violationDependency() {
+        return ClassUtils.isPresent(
+                "javax.validation.ConstraintViolationException", RpcExceptionMapper.class.getClassLoader());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ViolationReport.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ViolationReport.java
new file mode 100644
index 0000000..3adc26e
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/ViolationReport.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+import java.util.LinkedList;
+import java.util.List;
+
+@XmlRootElement(name = "violationReport")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ViolationReport implements Serializable {
+
+    private static final long serialVersionUID = -130498234L;
+
+    private List<RestConstraintViolation> constraintViolations;
+
+    public List<RestConstraintViolation> getConstraintViolations() {
+        return constraintViolations;
+    }
+
+    public void setConstraintViolations(List<RestConstraintViolation> constraintViolations) {
+        this.constraintViolations = constraintViolations;
+    }
+
+    public void addConstraintViolation(RestConstraintViolation constraintViolation) {
+        if (constraintViolations == null) {
+            constraintViolations = new LinkedList<>();
+        }
+        constraintViolations.add(constraintViolation);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/BaseParseContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/BaseParseContext.java
new file mode 100644
index 0000000..1f61842
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/BaseParseContext.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+
+import java.util.List;
+
+public class BaseParseContext {
+
+    protected List<Object> args;
+
+    protected List<ArgInfo> argInfos;
+
+    public List<Object> getArgs() {
+        return args;
+    }
+
+    public void setArgs(List<Object> args) {
+        this.args = args;
+    }
+
+    public List<ArgInfo> getArgInfos() {
+        return argInfos;
+    }
+
+    public void setArgInfos(List<ArgInfo> argInfos) {
+        this.argInfos = argInfos;
+    }
+
+    public ArgInfo getArgInfoByIndex(int index) {
+        return getArgInfos().get(index);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParser.java
new file mode 100644
index 0000000..0c51f19
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParser.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+
+public interface ParamParser<T> {
+    void parse(T parseContext, ArgInfo argInfo);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java
new file mode 100644
index 0000000..e713ee3
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/ParamParserManager.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation;
+
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser;
+import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.ConsumerParseContext;
+import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BaseProviderParamParser;
+import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.ProviderParseContext;
+
+import java.util.List;
+import java.util.Set;
+
+public class ParamParserManager {
+
+    private static final Set<BaseConsumerParamParser> consumerParamParsers = FrameworkModel.defaultModel()
+            .getExtensionLoader(BaseConsumerParamParser.class)
+            .getSupportedExtensionInstances();
+
+    private static final Set<BaseProviderParamParser> providerParamParsers = FrameworkModel.defaultModel()
+            .getExtensionLoader(BaseProviderParamParser.class)
+            .getSupportedExtensionInstances();
+
+    /**
+     * provider  Design Description:
+     * <p>
+     * Object[] args=new Object[0];
+     * List<Object> argsList=new ArrayList<>;</>
+     * <p>
+     * setValueByIndex(int index,Object value);
+     * <p>
+     * args=toArray(new Object[0]);
+     */
+    public static Object[] providerParamParse(ProviderParseContext parseContext) {
+
+        List<ArgInfo> args = parseContext.getArgInfos();
+
+        for (int i = 0; i < args.size(); i++) {
+            for (ParamParser paramParser : providerParamParsers) {
+
+                paramParser.parse(parseContext, args.get(i));
+            }
+        }
+        // TODO add param require or default & body arg size pre judge
+        return parseContext.getArgs().toArray(new Object[0]);
+    }
+
+    /**
+     * consumer  Design Description:
+     * <p>
+     * Object[] args=new Object[0];
+     * List<Object> argsList=new ArrayList<>;</>
+     * <p>
+     * setValueByIndex(int index,Object value);
+     * <p>
+     * args=toArray(new Object[0]);
+     */
+    public static void consumerParamParse(ConsumerParseContext parseContext) {
+
+        List<ArgInfo> argInfos = parseContext.getArgInfos();
+
+        for (int i = 0; i < argInfos.size(); i++) {
+            for (BaseConsumerParamParser paramParser : consumerParamParsers) {
+                ArgInfo argInfoByIndex = parseContext.getArgInfoByIndex(i);
+
+                if (!paramParser.paramTypeMatch(argInfoByIndex)) {
+                    continue;
+                }
+
+                paramParser.parse(parseContext, argInfoByIndex);
+            }
+        }
+
+        // TODO add param require or default
+
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java
new file mode 100644
index 0000000..8aaa20a
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionCreateContext.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.consumer;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.rpc.Invocation;
+
+public class HttpConnectionCreateContext {
+
+    private RequestTemplate requestTemplate;
+    private RestMethodMetadata restMethodMetadata;
+    private ServiceRestMetadata serviceRestMetadata;
+    private Invocation invocation;
+    private URL url;
+
+    public HttpConnectionCreateContext() {}
+
+    public void setRequestTemplate(RequestTemplate requestTemplate) {
+        this.requestTemplate = requestTemplate;
+    }
+
+    public RequestTemplate getRequestTemplate() {
+        return requestTemplate;
+    }
+
+    public ServiceRestMetadata getServiceRestMetadata() {
+        return serviceRestMetadata;
+    }
+
+    public RestMethodMetadata getRestMethodMetadata() {
+        return restMethodMetadata;
+    }
+
+    public void setRestMethodMetadata(RestMethodMetadata restMethodMetadata) {
+        this.restMethodMetadata = restMethodMetadata;
+    }
+
+    public Invocation getInvocation() {
+        return invocation;
+    }
+
+    public void setInvocation(Invocation invocation) {
+        this.invocation = invocation;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    public void setUrl(URL url) {
+        this.url = url;
+    }
+
+    public void setServiceRestMetadata(ServiceRestMetadata serviceRestMetadata) {
+        this.serviceRestMetadata = serviceRestMetadata;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java
new file mode 100644
index 0000000..a25466e
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/HttpConnectionPreBuildIntercept.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.consumer;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+
+/**
+ * http  request build intercept
+ */
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface HttpConnectionPreBuildIntercept {
+    void intercept(HttpConnectionCreateContext connectionCreateContext);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.java
new file mode 100644
index 0000000..612e8a9
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AddMustAttachmentIntercept.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 org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+
+/**
+ * add some must attachment
+ */
+@Activate(value = RestConstant.ADD_MUST_ATTTACHMENT, order = 1)
+public class AddMustAttachmentIntercept implements HttpConnectionPreBuildIntercept {
+
+    @Override
+    public void intercept(HttpConnectionCreateContext connectionCreateContext) {
+
+        RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate();
+        ServiceRestMetadata serviceRestMetadata = connectionCreateContext.getServiceRestMetadata();
+
+        requestTemplate.addHeader(RestHeaderEnum.GROUP.getHeader(), serviceRestMetadata.getGroup());
+        requestTemplate.addHeader(RestHeaderEnum.VERSION.getHeader(), serviceRestMetadata.getVersion());
+        requestTemplate.addHeader(RestHeaderEnum.PATH.getHeader(), serviceRestMetadata.getServiceInterface());
+        requestTemplate.addHeader(
+                RestHeaderEnum.TOKEN_KEY.getHeader(),
+                connectionCreateContext.getUrl().getParameter(RestConstant.TOKEN_KEY));
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java
new file mode 100644
index 0000000..c6a4a26
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/AttachmentIntercept.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.util.HttpHeaderUtil;
+
+/**
+ *  add client rpc context to request geader
+ */
+@Activate(value = RestConstant.RPCCONTEXT_INTERCEPT, order = 3)
+public class AttachmentIntercept implements HttpConnectionPreBuildIntercept {
+
+    @Override
+    public void intercept(HttpConnectionCreateContext connectionCreateContext) {
+
+        RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate();
+
+        HttpHeaderUtil.addRequestAttachments(
+                requestTemplate, connectionCreateContext.getInvocation().getObjectAttachments());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java
new file mode 100644
index 0000000..7de1066
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/ParamParseIntercept.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParserManager;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
+import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.ConsumerParseContext;
+
+import java.util.Arrays;
+
+/**
+ *  resolve method args  by args info
+ */
+@Activate(value = "paramparse", order = 5)
+public class ParamParseIntercept implements HttpConnectionPreBuildIntercept {
+
+    @Override
+    public void intercept(HttpConnectionCreateContext connectionCreateContext) {
+
+        ConsumerParseContext consumerParseContext =
+                new ConsumerParseContext(connectionCreateContext.getRequestTemplate());
+        consumerParseContext.setArgInfos(
+                connectionCreateContext.getRestMethodMetadata().getArgInfos());
+        consumerParseContext.setArgs(
+                Arrays.asList(connectionCreateContext.getInvocation().getArguments()));
+        ParamParserManager.consumerParamParse(consumerParseContext);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java
new file mode 100644
index 0000000..76802db
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/PathVariableIntercept.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.PathUtil;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ *  resolve method args from path
+ */
+@Activate(value = RestConstant.PATH_INTERCEPT, order = 4)
+public class PathVariableIntercept implements HttpConnectionPreBuildIntercept {
+
+    @Override
+    public void intercept(HttpConnectionCreateContext connectionCreateContext) {
+
+        RestMethodMetadata restMethodMetadata = connectionCreateContext.getRestMethodMetadata();
+        RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate();
+
+        List<ArgInfo> argInfos = restMethodMetadata.getArgInfos();
+
+        // path variable parse
+        String path = PathUtil.resolvePathVariable(
+                restMethodMetadata.getRequest().getPath(),
+                argInfos,
+                Arrays.asList(connectionCreateContext.getInvocation().getArguments()));
+        requestTemplate.path(path);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java
new file mode 100644
index 0000000..18c79c5
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/RequestHeaderIntercept.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * resolve method args from header
+ */
+@Activate(value = RestConstant.REQUEST_HEADER_INTERCEPT, order = Integer.MAX_VALUE - 1)
+public class RequestHeaderIntercept implements HttpConnectionPreBuildIntercept {
+
+    @Override
+    public void intercept(HttpConnectionCreateContext connectionCreateContext) {
+
+        RestMethodMetadata restMethodMetadata = connectionCreateContext.getRestMethodMetadata();
+        RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate();
+
+        Set<String> consumes = restMethodMetadata.getRequest().getConsumes();
+
+        requestTemplate.addHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), consumes);
+
+        Collection<String> produces = restMethodMetadata.getRequest().getProduces();
+        if (produces == null || produces.isEmpty()) {
+            requestTemplate.addHeader(RestHeaderEnum.ACCEPT.getHeader(), RestConstant.DEFAULT_ACCEPT);
+        } else {
+            requestTemplate.addHeaders(RestHeaderEnum.ACCEPT.getHeader(), produces);
+        }
+
+        //        URL url = connectionCreateContext.getUrl();
+
+        //
+        // requestTemplate.addKeepAliveHeader(url.getParameter(RestConstant.KEEP_ALIVE_TIMEOUT_PARAM,RestConstant.KEEP_ALIVE_TIMEOUT));
+
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java
new file mode 100644
index 0000000..12825e8
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/consumer/inercept/SerializeBodyIntercept.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.LoggerCodeConstants;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext;
+import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager;
+import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Collection;
+
+/**
+ * for request body Serialize
+ */
+@Activate(value = RestConstant.SERIALIZE_INTERCEPT, order = Integer.MAX_VALUE)
+public class SerializeBodyIntercept implements HttpConnectionPreBuildIntercept {
+
+    private static final ErrorTypeAwareLogger logger =
+            LoggerFactory.getErrorTypeAwareLogger(SerializeBodyIntercept.class);
+
+    @Override
+    public void intercept(HttpConnectionCreateContext connectionCreateContext) {
+        RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate();
+
+        if (requestTemplate.isBodyEmpty()) {
+            return;
+        }
+
+        try {
+            Object unSerializedBody = requestTemplate.getUnSerializedBody();
+            URL url = connectionCreateContext.getUrl();
+            // TODO pool
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            Collection<String> headers = requestTemplate.getHeaders(RestConstant.CONTENT_TYPE);
+            MediaType mediaType =
+                    MediaTypeUtil.convertMediaType(requestTemplate.getBodyType(), headers.toArray(new String[0]));
+
+            // add mediaType by targetClass serialize
+            if (mediaType != null && !mediaType.equals(MediaType.ALL_VALUE)) {
+                headers.clear();
+                headers.add(mediaType.value);
+            }
+            HttpMessageCodecManager.httpMessageEncode(
+                    outputStream, unSerializedBody, url, mediaType, requestTemplate.getBodyType());
+            requestTemplate.serializeBody(outputStream.toByteArray());
+            outputStream.close();
+        } catch (Exception e) {
+            logger.error(
+                    LoggerCodeConstants.PROTOCOL_ERROR_DESERIALIZE,
+                    "",
+                    "",
+                    "Rest SerializeBodyIntercept serialize error: {}",
+                    e);
+            throw new RpcException(e);
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java
new file mode 100644
index 0000000..b101b9b
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/metadata/MetadataResolver.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.metadata;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadataResolver;
+import org.apache.dubbo.rpc.protocol.rest.exception.CodeStyleNotSupportException;
+
+public class MetadataResolver {
+    private MetadataResolver() {}
+
+    /**
+     * for consumer
+     *
+     * @param targetClass target service class
+     * @param url         consumer url
+     * @return rest metadata
+     * @throws CodeStyleNotSupportException not support type
+     */
+    public static ServiceRestMetadata resolveConsumerServiceMetadata(
+            Class<?> targetClass, URL url, String contextPathFromUrl) {
+        ExtensionLoader<ServiceRestMetadataResolver> extensionLoader =
+                url.getOrDefaultApplicationModel().getExtensionLoader(ServiceRestMetadataResolver.class);
+
+        for (ServiceRestMetadataResolver serviceRestMetadataResolver :
+                extensionLoader.getSupportedExtensionInstances()) {
+            if (serviceRestMetadataResolver.supports(targetClass, true)) {
+                ServiceRestMetadata serviceRestMetadata =
+                        new ServiceRestMetadata(url.getServiceInterface(), url.getVersion(), url.getGroup(), true);
+                serviceRestMetadata.setContextPathFromUrl(contextPathFromUrl);
+                ServiceRestMetadata resolve = serviceRestMetadataResolver.resolve(targetClass, serviceRestMetadata);
+                return resolve;
+            }
+        }
+
+        // TODO support Dubbo style service
+        throw new CodeStyleNotSupportException("service is: " + targetClass + ", only support "
+                + extensionLoader.getSupportedExtensions() + " annotation");
+    }
+
+    public static ServiceRestMetadata resolveProviderServiceMetadata(
+            Class serviceImpl, URL url, String contextPathFromUrl) {
+        ExtensionLoader<ServiceRestMetadataResolver> extensionLoader =
+                url.getOrDefaultApplicationModel().getExtensionLoader(ServiceRestMetadataResolver.class);
+
+        for (ServiceRestMetadataResolver serviceRestMetadataResolver :
+                extensionLoader.getSupportedExtensionInstances()) {
+            boolean supports = serviceRestMetadataResolver.supports(serviceImpl);
+            if (supports) {
+                ServiceRestMetadata serviceRestMetadata =
+                        new ServiceRestMetadata(url.getServiceInterface(), url.getVersion(), url.getGroup(), false);
+                serviceRestMetadata.setContextPathFromUrl(contextPathFromUrl);
+                ServiceRestMetadata resolve = serviceRestMetadataResolver.resolve(serviceImpl, serviceRestMetadata);
+                return resolve;
+            }
+        }
+        throw new CodeStyleNotSupportException(
+                "service is:" + serviceImpl + ",just support rest or spring-web annotation");
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BaseConsumerParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BaseConsumerParamParser.java
new file mode 100644
index 0000000..87b1430
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BaseConsumerParamParser.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParser;
+
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface BaseConsumerParamParser extends ParamParser<ConsumerParseContext> {
+
+    boolean paramTypeMatch(ArgInfo argInfo);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.java
new file mode 100644
index 0000000..e9d1e48
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/BodyConsumerParamParser.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 org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.ParamType;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+
+import java.util.List;
+
+@Activate("consumer-body")
+public class BodyConsumerParamParser implements BaseConsumerParamParser {
+    @Override
+    public void parse(ConsumerParseContext parseContext, ArgInfo argInfo) {
+
+        List<Object> args = parseContext.getArgs();
+
+        RequestTemplate requestTemplate = parseContext.getRequestTemplate();
+
+        requestTemplate.body(args.get(argInfo.getIndex()), argInfo.getParamType());
+    }
+
+    @Override
+    public boolean paramTypeMatch(ArgInfo argInfo) {
+        return ParamType.BODY.supportAnno(argInfo.getParamAnnotationType());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ConsumerParseContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ConsumerParseContext.java
new file mode 100644
index 0000000..c6833f4
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ConsumerParseContext.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer;
+
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.rpc.protocol.rest.annotation.BaseParseContext;
+
+public class ConsumerParseContext extends BaseParseContext {
+    private RequestTemplate requestTemplate;
+
+    public ConsumerParseContext(RequestTemplate requestTemplate) {
+        this.requestTemplate = requestTemplate;
+    }
+
+    public RequestTemplate getRequestTemplate() {
+        return requestTemplate;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java
new file mode 100644
index 0000000..b4d9901
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/FormConsumerParamParser.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.ReflectUtils;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.ParamType;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Activate("consumer-form")
+public class FormConsumerParamParser implements BaseConsumerParamParser {
+    @Override
+    public void parse(ConsumerParseContext parseContext, ArgInfo argInfo) {
+
+        List<Object> args = parseContext.getArgs();
+
+        RequestTemplate requestTemplate = parseContext.getRequestTemplate();
+        Object value = args.get(argInfo.getIndex());
+
+        if (value == null) {
+            return;
+        }
+
+        Map<String, List<String>> tmp = new HashMap<>();
+        if (DataParseUtils.isTextType(value.getClass())) {
+            tmp.put(argInfo.getAnnotationNameAttribute(), Arrays.asList(String.valueOf(value)));
+            requestTemplate.body(tmp, Map.class);
+        } else if (value instanceof Map) {
+            requestTemplate.body(value, Map.class);
+        } else {
+            Set<String> allFieldNames = ReflectUtils.getAllFieldNames(value.getClass());
+
+            allFieldNames.stream().forEach(entry -> {
+                Object fieldValue = ReflectUtils.getFieldValue(value, entry);
+                tmp.put(String.valueOf(entry), Arrays.asList(String.valueOf(fieldValue)));
+            });
+
+            requestTemplate.body(tmp, Map.class);
+        }
+
+        Collection<String> headers = requestTemplate.getHeaders(RestConstant.CONTENT_TYPE);
+        if (CollectionUtils.isEmpty(headers)) {
+            requestTemplate.addHeader(RestConstant.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE.value);
+        }
+    }
+
+    @Override
+    public boolean paramTypeMatch(ArgInfo argInfo) {
+        return ParamType.FORM.supportAnno(argInfo.getParamAnnotationType());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java
new file mode 100644
index 0000000..1852bc7
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/HeaderConsumerParamParser.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.ParamType;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+
+import java.util.List;
+import java.util.Map;
+
+@Activate("consumer-header")
+public class HeaderConsumerParamParser implements BaseConsumerParamParser {
+    @Override
+    public void parse(ConsumerParseContext parseContext, ArgInfo argInfo) {
+        List<Object> args = parseContext.getArgs();
+
+        RequestTemplate requestTemplate = parseContext.getRequestTemplate();
+
+        Object headerValue = args.get(argInfo.getIndex());
+
+        if (headerValue == null) {
+            return;
+        }
+
+        // Map<String,String>
+        if (Map.class.isAssignableFrom(argInfo.getParamType())) {
+            Map headerValues = (Map) headerValue;
+            for (Object name : headerValues.keySet()) {
+                requestTemplate.addHeader(String.valueOf(name), headerValues.get(name));
+            }
+        } else {
+            // others
+            requestTemplate.addHeader(argInfo.getAnnotationNameAttribute(), headerValue);
+        }
+    }
+
+    @Override
+    public boolean paramTypeMatch(ArgInfo argInfo) {
+        return ParamType.HEADER.supportAnno(argInfo.getParamAnnotationType());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java
new file mode 100644
index 0000000..a1c3ba9
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/consumer/ParameterConsumerParamParser.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.ParamType;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+
+import java.util.List;
+import java.util.Map;
+
+@Activate("consumer-parameter")
+public class ParameterConsumerParamParser implements BaseConsumerParamParser {
+    @Override
+    public void parse(ConsumerParseContext parseContext, ArgInfo argInfo) {
+        List<Object> args = parseContext.getArgs();
+
+        RequestTemplate requestTemplate = parseContext.getRequestTemplate();
+
+        Object paramValue = args.get(argInfo.getIndex());
+
+        if (paramValue == null) {
+            return;
+        }
+
+        if (Map.class.isAssignableFrom(argInfo.getParamType())) {
+            Map paramValues = (Map) paramValue;
+            for (Object name : paramValues.keySet()) {
+                requestTemplate.addParam(String.valueOf(name), paramValues.get(name));
+            }
+        } else {
+            requestTemplate.addParam(argInfo.getAnnotationNameAttribute(), paramValue);
+        }
+    }
+
+    @Override
+    public boolean paramTypeMatch(ArgInfo argInfo) {
+        return ParamType.PARAM.supportAnno(argInfo.getParamAnnotationType());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BaseProviderParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BaseProviderParamParser.java
new file mode 100644
index 0000000..3b31d70
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BaseProviderParamParser.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParser;
+
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface BaseProviderParamParser extends ParamParser<ProviderParseContext> {}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BodyProviderParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BodyProviderParamParser.java
new file mode 100644
index 0000000..ddd6314
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/BodyProviderParamParser.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.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.ParamType;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil;
+
+/**
+ * body param parse
+ */
+@Activate(value = RestConstant.PROVIDER_BODY_PARSE)
+public class BodyProviderParamParser extends ProviderParamParser {
+
+    @Override
+    protected void doParse(ProviderParseContext parseContext, ArgInfo argInfo) {
+
+        RequestFacade request = parseContext.getRequestFacade();
+
+        try {
+            String contentType = parseContext.getRequestFacade().getHeader(RestHeaderEnum.CONTENT_TYPE.getHeader());
+            MediaType mediaType = MediaTypeUtil.convertMediaType(argInfo.getParamType(), contentType);
+            Object param = HttpMessageCodecManager.httpMessageDecode(
+                    request.getInputStream(), argInfo.getParamType(), argInfo.actualReflectType(), mediaType);
+            parseContext.setValueByIndex(argInfo.getIndex(), param);
+        } catch (Throwable e) {
+            throw new ParamParseException("dubbo rest protocol provider body param parser  error: " + e.getMessage());
+        }
+    }
+
+    @Override
+    protected ParamType getParamType() {
+        return ParamType.PROVIDER_BODY;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/HeaderProviderParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/HeaderProviderParamParser.java
new file mode 100644
index 0000000..7cec447
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/HeaderProviderParamParser.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.ParamType;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * header param parse
+ */
+@Activate(value = RestConstant.PROVIDER_HEADER_PARSE)
+public class HeaderProviderParamParser extends ProviderParamParser {
+    @Override
+    protected void doParse(ProviderParseContext parseContext, ArgInfo argInfo) {
+
+        // TODO MAP<String,String> convert
+        RequestFacade request = parseContext.getRequestFacade();
+        if (Map.class.isAssignableFrom(argInfo.getParamType())) {
+
+            Map<String, String> headerMap = new LinkedHashMap<>();
+            Enumeration<String> headerNames = request.getHeaderNames();
+
+            while (headerNames.hasMoreElements()) {
+                String name = headerNames.nextElement();
+                headerMap.put(name, request.getHeader(name));
+            }
+            parseContext.setValueByIndex(argInfo.getIndex(), headerMap);
+            return;
+        }
+
+        String header = request.getHeader(argInfo.getAnnotationNameAttribute());
+        Object headerValue = paramTypeConvert(argInfo.getParamType(), header);
+
+        parseContext.setValueByIndex(argInfo.getIndex(), headerValue);
+    }
+
+    @Override
+    protected ParamType getParamType() {
+        return ParamType.HEADER;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ParamProviderParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ParamProviderParamParser.java
new file mode 100644
index 0000000..6b31d6b
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ParamProviderParamParser.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.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.ParamType;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Http Parameter param parse
+ */
+@Activate(value = RestConstant.PROVIDER_PARAM_PARSE)
+public class ParamProviderParamParser extends ProviderParamParser {
+    @Override
+    protected void doParse(ProviderParseContext parseContext, ArgInfo argInfo) {
+
+        // TODO MAP<String,String> convert
+        RequestFacade request = parseContext.getRequestFacade();
+
+        if (Map.class.isAssignableFrom(argInfo.getParamType())) {
+
+            Map<String, String> paramMap = new LinkedHashMap<>();
+            Enumeration<String> parameterNames = request.getParameterNames();
+
+            while (parameterNames.hasMoreElements()) {
+                String name = parameterNames.nextElement();
+                paramMap.put(name, request.getParameter(name));
+            }
+            parseContext.setValueByIndex(argInfo.getIndex(), paramMap);
+            return;
+        }
+
+        String param = request.getParameter(argInfo.getAnnotationNameAttribute());
+
+        Object paramValue = paramTypeConvert(argInfo.getParamType(), param);
+        parseContext.setValueByIndex(argInfo.getIndex(), paramValue);
+    }
+
+    @Override
+    protected ParamType getParamType() {
+        return ParamType.PARAM;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/PathProviderParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/PathProviderParamParser.java
new file mode 100644
index 0000000..7b33839
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/PathProviderParamParser.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.ParamType;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+
+/**
+ * path param parse
+ */
+@Activate(value = RestConstant.PROVIDER_PATH_PARSE)
+public class PathProviderParamParser extends ProviderParamParser {
+    @Override
+    protected void doParse(ProviderParseContext parseContext, ArgInfo argInfo) {
+
+        String pathVariable = parseContext.getPathVariable(argInfo.getUrlSplitIndex());
+
+        Object pathVariableValue = paramTypeConvert(argInfo.getParamType(), pathVariable);
+
+        parseContext.setValueByIndex(argInfo.getIndex(), pathVariableValue);
+    }
+
+    @Override
+    protected ParamType getParamType() {
+        return ParamType.PATH;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParamParser.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParamParser.java
new file mode 100644
index 0000000..e1bfede
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParamParser.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
+
+import org.apache.dubbo.metadata.extension.rest.api.ArgInfo;
+import org.apache.dubbo.metadata.extension.rest.api.ParamType;
+import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
+
+public abstract class ProviderParamParser implements BaseProviderParamParser {
+
+    public void parse(ProviderParseContext parseContext, ArgInfo argInfo) {
+
+        if (!matchParseType(argInfo.getParamAnnotationType())) {
+            return;
+        }
+
+        doParse(parseContext, argInfo);
+    }
+
+    protected abstract void doParse(ProviderParseContext parseContext, ArgInfo argInfo);
+
+    public boolean matchParseType(Class paramAnno) {
+
+        ParamType paramAnnotType = getParamType();
+        return paramAnnotType.supportAnno(paramAnno);
+    }
+
+    protected abstract ParamType getParamType();
+
+    protected Object paramTypeConvert(Class targetType, String value) {
+
+        return DataParseUtils.stringTypeConvert(targetType, value);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParseContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParseContext.java
new file mode 100644
index 0000000..be247cd
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/annotation/param/parse/provider/ProviderParseContext.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.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
+
+import org.apache.dubbo.rpc.protocol.rest.annotation.BaseParseContext;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+
+public class ProviderParseContext extends BaseParseContext {
+
+    private RequestFacade requestFacade;
+    private Object response;
+    private Object request;
+
+    public ProviderParseContext(RequestFacade request) {
+        this.requestFacade = request;
+    }
+
+    public RequestFacade getRequestFacade() {
+        return requestFacade;
+    }
+
+    public void setValueByIndex(int index, Object value) {
+
+        this.args.set(index, value);
+    }
+
+    public Object getResponse() {
+        return response;
+    }
+
+    public void setResponse(Object response) {
+        this.response = response;
+    }
+
+    public Object getRequest() {
+        return request;
+    }
+
+    public void setRequest(Object request) {
+        this.request = request;
+    }
+
+    public String getPathVariable(int urlSplitIndex) {
+
+        String[] split = getRequestFacade().getRequestURI().split("/");
+
+        return split[urlSplitIndex];
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java
new file mode 100644
index 0000000..a8d5bbc
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/constans/RestConstant.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.constans;
+
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.rpc.Constants;
+
+public interface RestConstant {
+    String VERSION = CommonConstants.VERSION_KEY;
+    String GROUP = CommonConstants.GROUP_KEY;
+    String PATH = CommonConstants.PATH_KEY;
+    String TOKEN_KEY = Constants.TOKEN_KEY;
+    String LOCAL_ADDR = "LOCAL_ADDR";
+    String REMOTE_ADDR = "REMOTE_ADDR";
+    String LOCAL_PORT = "LOCAL_PORT";
+    String REMOTE_PORT = "REMOTE_PORT";
+    String PROVIDER_BODY_PARSE = "body";
+    String PROVIDER_PARAM_PARSE = "param";
+    String PROVIDER_HEADER_PARSE = "header";
+    String PROVIDER_PATH_PARSE = "path";
+
+    String ADD_MUST_ATTTACHMENT = "must-intercept";
+    String RPCCONTEXT_INTERCEPT = "rpc-context";
+    String SERIALIZE_INTERCEPT = "serialize";
+    String PATH_SEPARATOR = "/";
+    String REQUEST_HEADER_INTERCEPT = "header";
+    String PATH_INTERCEPT = "path";
+    String KEEP_ALIVE_HEADER = "Keep-Alive";
+    String CONNECTION = "Connection";
+    String CONTENT_TYPE = "Content-Type";
+    String TEXT_PLAIN = "text/plain";
+    String ACCEPT_CHARSET = "Accept-Charset";
+    String WEIGHT_IDENTIFIER = ";q=";
+    String ACCEPT = "Accept";
+    String DEFAULT_ACCEPT = "*/*";
+    String REST_HEADER_PREFIX = "rest-service-";
+
+    // http
+    String MAX_INITIAL_LINE_LENGTH_PARAM = "max.initial.line.length";
+    String MAX_HEADER_SIZE_PARAM = "max.header.size";
+    String MAX_CHUNK_SIZE_PARAM = "max.chunk.size";
+    String MAX_REQUEST_SIZE_PARAM = "max.request.size";
+    String IDLE_TIMEOUT_PARAM = "idle.timeout";
+    String KEEP_ALIVE_TIMEOUT_PARAM = "keep.alive.timeout";
+    String DEFAULT_CHARSET = "UTF-8";
+
+    int MAX_REQUEST_SIZE = 1024 * 1024 * 10;
+    int MAX_INITIAL_LINE_LENGTH = 4096;
+    int MAX_HEADER_SIZE = 8192;
+    int MAX_CHUNK_SIZE = 8192;
+    int IDLE_TIMEOUT = -1;
+    int KEEP_ALIVE_TIMEOUT = 60;
+
+    /**
+     * ServerAttachment  pathAndInvokerMapper key
+     */
+    String PATH_AND_INVOKER_MAPPER = "pathAndInvokerMapper";
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/deploy/ServiceDeployer.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/deploy/ServiceDeployer.java
new file mode 100644
index 0000000..2712da0
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/deploy/ServiceDeployer.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.deploy;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.ClassUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.extension.rest.api.PathMatcher;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.protocol.rest.Constants;
+import org.apache.dubbo.rpc.protocol.rest.PathAndInvokerMapper;
+import org.apache.dubbo.rpc.protocol.rest.RpcExceptionMapper;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.RestEasyExceptionMapper;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
+
+public class ServiceDeployer {
+
+    private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+
+    private final PathAndInvokerMapper pathAndInvokerMapper = new PathAndInvokerMapper();
+    private final ExceptionMapper exceptionMapper = createExceptionMapper();
+
+    private final Set<Object> extensions = new HashSet<>();
+
+    public void deploy(ServiceRestMetadata serviceRestMetadata, Invoker invoker) {
+        Map<PathMatcher, RestMethodMetadata> pathToServiceMapContainPathVariable =
+                serviceRestMetadata.getPathContainPathVariableToServiceMap();
+        pathAndInvokerMapper.addPathAndInvoker(pathToServiceMapContainPathVariable, invoker);
+
+        Map<PathMatcher, RestMethodMetadata> pathToServiceMapUnContainPathVariable =
+                serviceRestMetadata.getPathUnContainPathVariableToServiceMap();
+        pathAndInvokerMapper.addPathAndInvoker(pathToServiceMapUnContainPathVariable, invoker);
+    }
+
+    public void undeploy(ServiceRestMetadata serviceRestMetadata) {
+        Map<PathMatcher, RestMethodMetadata> pathToServiceMapContainPathVariable =
+                serviceRestMetadata.getPathContainPathVariableToServiceMap();
+        pathToServiceMapContainPathVariable.keySet().stream().forEach(pathAndInvokerMapper::removePath);
+
+        Map<PathMatcher, RestMethodMetadata> pathToServiceMapUnContainPathVariable =
+                serviceRestMetadata.getPathUnContainPathVariableToServiceMap();
+        pathToServiceMapUnContainPathVariable.keySet().stream().forEach(pathAndInvokerMapper::removePath);
+    }
+
+    public void registerExtension(URL url) {
+
+        for (String clazz : COMMA_SPLIT_PATTERN.split(
+                url.getParameter(Constants.EXTENSION_KEY, RpcExceptionMapper.class.getName()))) {
+
+            if (StringUtils.isEmpty(clazz)) {
+                continue;
+            }
+            try {
+                Class<?> aClass = ClassUtils.forName(clazz);
+
+                // exception handler
+                if (ExceptionMapper.isSupport(aClass)) {
+                    exceptionMapper.registerMapper(clazz);
+                } else {
+
+                    extensions.add(aClass.newInstance());
+                }
+
+            } catch (Exception e) {
+                logger.warn("", "", "dubbo rest registerExtension error: ", e.getMessage(), e);
+            }
+        }
+    }
+
+    public PathAndInvokerMapper getPathAndInvokerMapper() {
+        return pathAndInvokerMapper;
+    }
+
+    public ExceptionMapper getExceptionMapper() {
+        return exceptionMapper;
+    }
+
+    public Set<Object> getExtensions() {
+        return extensions;
+    }
+
+    /**
+     * get extensions by type
+     *
+     * @param extensionClass
+     * @param <T>
+     * @return
+     */
+    // TODO add  javax.annotation.Priority sort
+    public <T> List<T> getExtensions(Class<T> extensionClass) {
+
+        ArrayList<T> exts = new ArrayList<>();
+        if (extensions.isEmpty()) {
+            return exts;
+        }
+
+        for (Object extension : extensions) {
+            if (extensionClass.isAssignableFrom(extension.getClass())) {
+                exts.add((T) extension);
+            }
+        }
+
+        return exts;
+    }
+
+    private ExceptionMapper createExceptionMapper() {
+        if (ClassUtils.isPresent(
+                "javax.ws.rs.ext.ExceptionMapper", Thread.currentThread().getContextClassLoader())) {
+            return new RestEasyExceptionMapper();
+        }
+        return new ExceptionMapper();
+    }
+
+    public boolean isMethodAllowed(PathMatcher pathMatcher) {
+        return pathAndInvokerMapper.isHttpMethodAllowed(pathMatcher);
+    }
+
+    public boolean hashRestMethod(PathMatcher pathMatcher) {
+        return pathAndInvokerMapper.getRestMethodMetadata(pathMatcher) != null;
+    }
+
+    public String pathHttpMethods(PathMatcher pathMatcher) {
+        return pathAndInvokerMapper.pathHttpMethods(pathMatcher);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java
new file mode 100644
index 0000000..9799947
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/CodeStyleNotSupportException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception;
+
+/**
+ *  only support spring mvc & jaxrs annotation
+ */
+public class CodeStyleNotSupportException extends RestException {
+
+    public CodeStyleNotSupportException(String message) {
+        super(message);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/DoublePathCheckException.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/DoublePathCheckException.java
new file mode 100644
index 0000000..450f7a2
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/DoublePathCheckException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception;
+
+/**
+ *  path mapper contains current path will throw
+ */
+public class DoublePathCheckException extends RuntimeException {
+
+    public DoublePathCheckException(String message) {
+        super(message);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/MediaTypeUnSupportException.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/MediaTypeUnSupportException.java
new file mode 100644
index 0000000..29af0d7
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/MediaTypeUnSupportException.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception;
+
+public class MediaTypeUnSupportException extends RestException {
+    public MediaTypeUnSupportException(String message) {
+        super(message);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/ParamParseException.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/ParamParseException.java
new file mode 100644
index 0000000..b0cd3d7
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/ParamParseException.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception;
+
+public class ParamParseException extends RestException {
+
+    public ParamParseException(String message) {
+        super(message);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/PathNoFoundException.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/PathNoFoundException.java
new file mode 100644
index 0000000..e3ced44
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/PathNoFoundException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception;
+
+/**
+ *  response code : 404 path no found exception
+ */
+public class PathNoFoundException extends RestException {
+
+    public PathNoFoundException(String message) {
+        super(message);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java
new file mode 100644
index 0000000..fce0964
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RemoteServerInternalException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception;
+
+/**
+ *  response status code : 500
+ */
+public class RemoteServerInternalException extends RestException {
+
+    public RemoteServerInternalException(String message) {
+        super("dubbo http rest protocol remote error :" + message);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RestException.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RestException.java
new file mode 100644
index 0000000..9956eb8
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/RestException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception;
+
+/**
+ *  rest exception super
+ */
+public class RestException extends RuntimeException {
+
+    public RestException(String message) {
+        super(message);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.java
new file mode 100644
index 0000000..dda391d
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/UnSupportContentTypeException.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 org.apache.dubbo.rpc.protocol.rest.exception;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+
+public class UnSupportContentTypeException extends MediaTypeUnSupportException {
+
+    public UnSupportContentTypeException(String message) {
+
+        super("Current Support content type: " + MediaType.getAllContentType() + "; Do not support  content type"
+                + message);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionHandler.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionHandler.java
new file mode 100644
index 0000000..0c6f9fd
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionHandler.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception.mapper;
+
+public interface ExceptionHandler<E extends Throwable> {
+
+    Object result(E exception);
+
+    default int status() {
+        return 200;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionHandlerResult.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionHandlerResult.java
new file mode 100644
index 0000000..7c5109c
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionHandlerResult.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception.mapper;
+
+public class ExceptionHandlerResult {
+    private int status;
+    private Object entity;
+
+    public ExceptionHandlerResult() {}
+
+    public ExceptionHandlerResult setStatus(int status) {
+        this.status = status;
+        return this;
+    }
+
+    public ExceptionHandlerResult setEntity(Object entity) {
+        this.entity = entity;
+        return this;
+    }
+
+    public static ExceptionHandlerResult build() {
+        return new ExceptionHandlerResult();
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public Object getEntity() {
+        return entity;
+    }
+
+    @Override
+    public String toString() {
+        return "ExceptionHandlerResult{" + "status=" + status + ", entity=" + entity + '}';
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionMapper.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionMapper.java
new file mode 100644
index 0000000..c220499
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/ExceptionMapper.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception.mapper;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.protocol.rest.util.ReflectUtils;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ExceptionMapper {
+    private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+
+    private final Map<Class<?>, ExceptionHandler> exceptionHandlerMap = new ConcurrentHashMap<>();
+
+    private final Map allExceptionHandlers = new ConcurrentHashMap<>();
+
+    public ExceptionHandlerResult exceptionToResult(Object throwable) {
+        ExceptionHandler exceptionHandler = (ExceptionHandler) getExceptionHandler(throwable.getClass());
+
+        Object result = exceptionHandler.result((Throwable) throwable);
+
+        return ExceptionHandlerResult.build().setEntity(result).setStatus(exceptionHandler.status());
+    }
+
+    public Object getExceptionHandler(Class causeClass) {
+
+        return getExceptionHandler(allExceptionHandlers, causeClass);
+    }
+
+    public Object getExceptionHandler(Map exceptionHandlerMap, Class causeClass) {
+        Object exceptionHandler = null;
+        while (causeClass != null) {
+            exceptionHandler = exceptionHandlerMap.get(causeClass);
+            if (exceptionHandler != null) {
+                break;
+            }
+            // When the exception handling class cannot be obtained, it should recursively search the base class
+            causeClass = causeClass.getSuperclass();
+        }
+        return exceptionHandler;
+    }
+
+    public boolean hasExceptionMapper(Object throwable) {
+        if (throwable == null) {
+            return false;
+        }
+        return allExceptionHandlers.containsKey(throwable.getClass());
+    }
+
+    public void registerMapper(Class<?> exceptionHandler) {
+
+        try {
+            List<Method> methods = getExceptionHandlerMethods(exceptionHandler);
+
+            if (methods == null || methods.isEmpty()) {
+                return;
+            }
+
+            Set<Class<?>> exceptions = new HashSet<>();
+
+            for (Method method : methods) {
+                Class<?> parameterType = method.getParameterTypes()[0];
+
+                // param type isAssignableFrom throwable
+                if (!Throwable.class.isAssignableFrom(parameterType)) {
+                    continue;
+                }
+
+                exceptions.add(parameterType);
+            }
+
+            ArrayList<Class<?>> classes = new ArrayList<>(exceptions);
+
+            // if size==1 so ,exception handler for Throwable
+            if (classes.size() != 1) {
+                // else remove throwable
+                exceptions.remove(Throwable.class);
+            }
+
+            List<Constructor<?>> constructors = ReflectUtils.getConstructList(exceptionHandler);
+
+            if (constructors.isEmpty()) {
+                throw new RuntimeException(
+                        "dubbo rest exception mapper register mapper need exception handler exist no  construct declare, current class is: "
+                                + exceptionHandler);
+            }
+
+            // if exceptionHandler is inner class , no arg construct don`t appear , so  newInstance don`t use
+            // noArgConstruct
+            Object handler = constructors
+                    .get(0)
+                    .newInstance(new Object[constructors.get(0).getParameterCount()]);
+
+            putExtensionToMap(exceptions, handler);
+
+        } catch (Exception e) {
+            throw new RuntimeException("dubbo rest protocol exception mapper register error ", e);
+        }
+    }
+
+    protected void putExtensionToMap(Set<Class<?>> exceptions, Object handler) {
+
+        Map exceptionHandlerMaps = getExceptionHandlerMap(handler);
+
+        for (Class<?> exception : exceptions) {
+            // put to instance map
+            exceptionHandlerMaps.put(exception, handler);
+            // put to all map
+            allExceptionHandlers.put(exception, handler);
+        }
+    }
+
+    protected Map getExceptionHandlerMap(Object handler) {
+        return exceptionHandlerMap;
+    }
+
+    protected List<Method> getExceptionHandlerMethods(Class<?> exceptionHandler) {
+        if (!ExceptionHandler.class.isAssignableFrom(exceptionHandler)) {
+            return null;
+        }
+        // resolve Java_Zulu_jdk/17.0.6-10/x64 param is not throwable
+        List<Method> methods = ReflectUtils.getMethodByNameList(exceptionHandler, "result");
+        return methods;
+    }
+
+    public void registerMapper(String exceptionMapper) {
+        try {
+            registerMapper(ReflectUtils.findClass(exceptionMapper));
+        } catch (Exception e) {
+            logger.warn(
+                    "",
+                    e.getMessage(),
+                    "",
+                    "dubbo rest protocol exception mapper register error ,and current exception mapper is  :"
+                            + exceptionMapper);
+        }
+    }
+
+    public void unRegisterMapper(Class<?> exception) {
+        exceptionHandlerMap.remove(exception);
+    }
+
+    public static boolean isSupport(Class<?> exceptionHandler) {
+        try {
+            return ExceptionHandler.class.isAssignableFrom(exceptionHandler)
+                    || ReflectUtils.findClassTryException("javax.ws.rs.ext.ExceptionMapper")
+                            .isAssignableFrom(exceptionHandler);
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/RestEasyExceptionMapper.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/RestEasyExceptionMapper.java
new file mode 100644
index 0000000..263dd76
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/exception/mapper/RestEasyExceptionMapper.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception.mapper;
+
+import org.apache.dubbo.rpc.protocol.rest.util.ReflectUtils;
+
+import javax.ws.rs.core.Response;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * for rest easy  exception mapper extension
+ */
+public class RestEasyExceptionMapper extends ExceptionMapper {
+
+    private final Map<Class<?>, javax.ws.rs.ext.ExceptionMapper> exceptionMappers = new ConcurrentHashMap<>();
+
+    protected List<Method> getExceptionHandlerMethods(Class<?> exceptionHandler) {
+        if (!javax.ws.rs.ext.ExceptionMapper.class.isAssignableFrom(exceptionHandler)) {
+            return super.getExceptionHandlerMethods(exceptionHandler);
+        }
+        // resolve Java_Zulu_jdk/17.0.6-10/x64 param is not throwable
+        List<Method> methods = ReflectUtils.getMethodByNameList(exceptionHandler, "toResponse");
+        return methods;
+    }
+
+    protected Map getExceptionHandlerMap(Object handler) {
+        if (handler instanceof ExceptionHandler) {
+            return super.getExceptionHandlerMap(handler);
+        }
+        return exceptionMappers;
+    }
+
+    public ExceptionHandlerResult exceptionToResult(Object throwable) {
+        Object exceptionMapper = getExceptionHandler(throwable.getClass());
+        if (exceptionMapper == null || exceptionMapper instanceof ExceptionHandler) {
+            return super.exceptionToResult(throwable);
+        }
+
+        Response response = ((javax.ws.rs.ext.ExceptionMapper) exceptionMapper).toResponse((Throwable) throwable);
+
+        return ExceptionHandlerResult.build().setStatus(response.getStatus()).setEntity(response.getEntity());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/ResteasyContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/ResteasyContext.java
new file mode 100644
index 0000000..b76c7cf
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/ResteasyContext.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.handler.codec.http.HttpContent;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpRequest;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboContainerResponseContextImpl;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboPreMatchContainerRequestContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.ServiceInvokeRestFilter;
+import org.apache.dubbo.rpc.protocol.rest.netty.ChunkOutputStream;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil;
+import org.jboss.resteasy.core.interception.ResponseContainerRequestContext;
+import org.jboss.resteasy.plugins.server.netty.NettyHttpRequest;
+import org.jboss.resteasy.plugins.server.netty.NettyUtil;
+import org.jboss.resteasy.specimpl.BuiltResponse;
+import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
+import org.jboss.resteasy.spi.HttpResponse;
+import org.jboss.resteasy.spi.ResteasyUriInfo;
+
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MultivaluedMap;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+public interface ResteasyContext {
+    String HTTP_PROTOCOL = "http://";
+    String HTTP = "http";
+    String HTTPS_PROTOCOL = "https://";
+
+    /**
+     * return extensions that are  filtered by  extension type
+     *
+     * @param extension
+     * @param <T>
+     * @return
+     */
+    default <T> List<T> getExtension(ServiceDeployer serviceDeployer, Class<T> extension) {
+
+        return serviceDeployer.getExtensions(extension);
+    }
+
+    default DubboPreMatchContainerRequestContext convertHttpRequestToContainerRequestContext(
+            RequestFacade requestFacade, ContainerRequestFilter[] requestFilters) {
+
+        NettyRequestFacade nettyRequestFacade = (NettyRequestFacade) requestFacade;
+        HttpRequest request = (HttpRequest) requestFacade.getRequest();
+
+        NettyHttpRequest nettyRequest = createNettyHttpRequest(nettyRequestFacade, request);
+
+        if (request instanceof HttpContent) {
+
+            try {
+                byte[] inputStream = requestFacade.getInputStream();
+                ByteBuf buffer =
+                        nettyRequestFacade.getNettyChannelContext().alloc().buffer();
+                buffer.writeBytes(inputStream);
+                nettyRequest.setContentBuffer(buffer);
+            } catch (IOException e) {
+            }
+        }
+
+        return new DubboPreMatchContainerRequestContext(nettyRequest, requestFilters, null);
+    }
+
+    default ResteasyUriInfo extractUriInfo(HttpRequest request) {
+        String host = HttpHeaders.getHost(request, "unknown");
+        if ("".equals(host)) {
+            host = "unknown";
+        }
+        String uri = request.getUri();
+
+        String uriString;
+
+        // If we appear to have an absolute URL, don't try to recreate it from the host and request line.
+        if (uri.startsWith(HTTP_PROTOCOL) || uri.startsWith(HTTPS_PROTOCOL)) {
+            uriString = uri;
+        } else {
+            uriString = HTTP + "://" + host + uri;
+        }
+
+        URI absoluteURI = URI.create(uriString);
+        return new ResteasyUriInfo(uriString, absoluteURI.getRawQuery(), "");
+    }
+
+    default NettyHttpRequest createNettyHttpRequest(NettyRequestFacade nettyRequestFacade, HttpRequest request) {
+        ResteasyHttpHeaders headers = NettyUtil.extractHttpHeaders(request);
+        ResteasyUriInfo uriInfo = extractUriInfo(request);
+        NettyHttpRequest nettyRequest = new NettyHttpRequest(
+                nettyRequestFacade.getNettyChannelContext(),
+                headers,
+                uriInfo,
+                request.getMethod().name(),
+                null,
+                null,
+                HttpHeaders.is100ContinueExpected(request));
+
+        return nettyRequest;
+    }
+
+    default NettyHttpRequest createNettyHttpRequest(RequestFacade requestFacade) {
+        NettyRequestFacade nettyRequestFacade = (NettyRequestFacade) requestFacade;
+        HttpRequest request = (HttpRequest) requestFacade.getRequest();
+
+        ResteasyHttpHeaders headers = NettyUtil.extractHttpHeaders(request);
+        ResteasyUriInfo uriInfo = extractUriInfo(request);
+        NettyHttpRequest nettyRequest = new NettyHttpRequest(
+                nettyRequestFacade.getNettyChannelContext(),
+                headers,
+                uriInfo,
+                request.getMethod().name(),
+                null,
+                null,
+                HttpHeaders.is100ContinueExpected(request));
+
+        return nettyRequest;
+    }
+
+    default void writeResteasyResponse(
+            URL url, RequestFacade requestFacade, NettyHttpResponse response, BuiltResponse restResponse)
+            throws Exception {
+        if (restResponse.getMediaType() != null) {
+            MediaType mediaType = MediaTypeUtil.convertMediaType(
+                    restResponse.getEntityClass(), restResponse.getMediaType().toString());
+            ServiceInvokeRestFilter.writeResult(
+                    response, url, restResponse.getEntity(), restResponse.getEntityClass(), mediaType);
+        } else {
+            ServiceInvokeRestFilter.writeResult(
+                    response, requestFacade, url, restResponse.getEntity(), restResponse.getEntityClass());
+        }
+    }
+
+    default MediaType getAcceptMediaType(RequestFacade request, Class<?> returnType) {
+
+        return ServiceInvokeRestFilter.getAcceptMediaType(request, returnType);
+    }
+
+    default void addResponseHeaders(NettyHttpResponse response, MultivaluedMap<String, Object> headers) {
+        if (headers == null || headers.isEmpty()) {
+
+            return;
+        }
+        for (Map.Entry<String, List<Object>> entry : headers.entrySet()) {
+
+            String key = entry.getKey();
+            List<Object> value = entry.getValue();
+            if (value == null || value.isEmpty()) {
+                continue;
+            }
+            for (Object tmp : value) {
+                response.addOutputHeaders(key, tmp.toString());
+            }
+        }
+    }
+
+    default DubboContainerResponseContextImpl createContainerResponseContext(
+            Object originRequest,
+            RequestFacade request,
+            HttpResponse httpResponse,
+            BuiltResponse jaxrsResponse,
+            ContainerResponseFilter[] responseFilters) {
+
+        NettyHttpRequest nettyHttpRequest =
+                originRequest == null ? createNettyHttpRequest(request) : (NettyHttpRequest) originRequest;
+
+        ResponseContainerRequestContext requestContext = new ResponseContainerRequestContext(nettyHttpRequest);
+        DubboContainerResponseContextImpl responseContext = new DubboContainerResponseContextImpl(
+                nettyHttpRequest, httpResponse, jaxrsResponse, requestContext, responseFilters, null, null);
+
+        return responseContext;
+    }
+
+    default void restOutputStream(NettyHttpResponse response) throws IOException {
+        ChunkOutputStream outputStream = (ChunkOutputStream) response.getOutputStream();
+        outputStream.reset();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/DubboBuiltResponse.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/DubboBuiltResponse.java
new file mode 100644
index 0000000..30af535
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/DubboBuiltResponse.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter;
+
+import org.jboss.resteasy.specimpl.BuiltResponse;
+
+/**
+ * wrapper resteasy  BuiltResponse
+ */
+public class DubboBuiltResponse extends BuiltResponse {
+
+    // user reset entity
+    private boolean resetEntity;
+
+    public DubboBuiltResponse(Object entity, int status, Class<?> entityClass) {
+
+        this.entity = entity;
+        this.entityClass = entityClass;
+        this.status = status;
+    }
+
+    @Override
+    public void setEntity(Object entity) {
+        if (entity == null) {
+            return;
+        }
+
+        if (entity.equals(this.entity)) {
+            return;
+        }
+        //  reset entity true
+        this.resetEntity = true;
+        super.setEntity(entity);
+    }
+
+    public boolean isResetEntity() {
+        return resetEntity;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/DubboContainerResponseContextImpl.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/DubboContainerResponseContextImpl.java
new file mode 100644
index 0000000..4c2f9de
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/DubboContainerResponseContextImpl.java
@@ -0,0 +1,391 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.jboss.resteasy.core.Dispatcher;
+import org.jboss.resteasy.core.ServerResponseWriter;
+import org.jboss.resteasy.core.SynchronousDispatcher;
+import org.jboss.resteasy.core.interception.ResponseContainerRequestContext;
+import org.jboss.resteasy.core.interception.jaxrs.SuspendableContainerResponseContext;
+import org.jboss.resteasy.specimpl.BuiltResponse;
+import org.jboss.resteasy.spi.ApplicationException;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.HttpResponse;
+import org.jboss.resteasy.spi.ResteasyAsynchronousResponse;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class DubboContainerResponseContextImpl implements SuspendableContainerResponseContext {
+    private static final ErrorTypeAwareLogger logger =
+            LoggerFactory.getErrorTypeAwareLogger(DubboContainerResponseContextImpl.class);
+
+    protected final HttpRequest request;
+    protected final HttpResponse httpResponse;
+    protected final BuiltResponse jaxrsResponse;
+    private ResponseContainerRequestContext requestContext;
+    private ContainerResponseFilter[] responseFilters;
+    private ServerResponseWriter.RunnableWithIOException continuation;
+    private int currentFilter;
+    private boolean suspended;
+    private boolean filterReturnIsMeaningful = true;
+    private Map<Class<?>, Object> contextDataMap;
+    private boolean inFilter;
+    private Throwable throwable;
+    private Consumer<Throwable> onComplete;
+    private boolean weSuspended;
+
+    public DubboContainerResponseContextImpl(
+            final HttpRequest request,
+            final HttpResponse httpResponse,
+            final BuiltResponse serverResponse,
+            final ResponseContainerRequestContext requestContext,
+            final ContainerResponseFilter[] responseFilters,
+            final Consumer<Throwable> onComplete,
+            final ServerResponseWriter.RunnableWithIOException continuation) {
+        this.request = request;
+        this.httpResponse = httpResponse;
+        this.jaxrsResponse = serverResponse;
+        this.requestContext = requestContext;
+        this.responseFilters = responseFilters;
+        this.continuation = continuation;
+        this.onComplete = onComplete;
+        contextDataMap = ResteasyProviderFactory.getContextDataMap();
+    }
+
+    public BuiltResponse getJaxrsResponse() {
+        return jaxrsResponse;
+    }
+
+    public HttpResponse getHttpResponse() {
+        return httpResponse;
+    }
+
+    @Override
+    public int getStatus() {
+        return jaxrsResponse.getStatus();
+    }
+
+    @Override
+    public void setStatus(int code) {
+        httpResponse.setStatus(code);
+        jaxrsResponse.setStatus(code);
+    }
+
+    @Override
+    public Response.StatusType getStatusInfo() {
+        return jaxrsResponse.getStatusInfo();
+    }
+
+    @Override
+    public void setStatusInfo(Response.StatusType statusInfo) {
+        httpResponse.setStatus(statusInfo.getStatusCode());
+        jaxrsResponse.setStatus(statusInfo.getStatusCode());
+    }
+
+    @Override
+    public Class<?> getEntityClass() {
+        return jaxrsResponse.getEntityClass();
+    }
+
+    @Override
+    public Type getEntityType() {
+        return jaxrsResponse.getGenericType();
+    }
+
+    @Override
+    public void setEntity(Object entity) {
+        if (entity != null && jaxrsResponse.getEntity() != null) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Dubbo container response context filter set entity ,before entity is: "
+                        + jaxrsResponse.getEntity() + "and after entity is: " + entity);
+            }
+        }
+        jaxrsResponse.setEntity(entity);
+        // it resets the entity in a response filter which results
+        // in a bad content-length being sent back to the client
+        // so, we'll remove any content-length setting
+        getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
+    }
+
+    @Override
+    public void setEntity(Object entity, Annotation[] annotations, MediaType mediaType) {
+        if (entity != null && jaxrsResponse.getEntity() != null) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Dubbo container response context filter set entity ,before entity is: "
+                        + jaxrsResponse.getEntity() + "and after entity is: " + entity);
+            }
+        }
+        jaxrsResponse.setEntity(entity);
+        jaxrsResponse.setAnnotations(annotations);
+        jaxrsResponse.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, mediaType);
+        // it resets the entity in a response filter which results
+        // in a bad content-length being sent back to the client
+        // so, we'll remove any content-length setting
+        getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
+    }
+
+    @Override
+    public MultivaluedMap<String, Object> getHeaders() {
+        return jaxrsResponse.getMetadata();
+    }
+
+    @Override
+    public Set<String> getAllowedMethods() {
+        return jaxrsResponse.getAllowedMethods();
+    }
+
+    @Override
+    public Date getDate() {
+        return jaxrsResponse.getDate();
+    }
+
+    @Override
+    public Locale getLanguage() {
+        return jaxrsResponse.getLanguage();
+    }
+
+    @Override
+    public int getLength() {
+        return jaxrsResponse.getLength();
+    }
+
+    @Override
+    public MediaType getMediaType() {
+        return jaxrsResponse.getMediaType();
+    }
+
+    @Override
+    public Map<String, NewCookie> getCookies() {
+        return jaxrsResponse.getCookies();
+    }
+
+    @Override
+    public EntityTag getEntityTag() {
+        return jaxrsResponse.getEntityTag();
+    }
+
+    @Override
+    public Date getLastModified() {
+        return jaxrsResponse.getLastModified();
+    }
+
+    @Override
+    public URI getLocation() {
+        return jaxrsResponse.getLocation();
+    }
+
+    @Override
+    public Set<Link> getLinks() {
+        return jaxrsResponse.getLinks();
+    }
+
+    @Override
+    public boolean hasLink(String relation) {
+        return jaxrsResponse.hasLink(relation);
+    }
+
+    @Override
+    public Link getLink(String relation) {
+        return jaxrsResponse.getLink(relation);
+    }
+
+    @Override
+    public Link.Builder getLinkBuilder(String relation) {
+        return jaxrsResponse.getLinkBuilder(relation);
+    }
+
+    @Override
+    public boolean hasEntity() {
+        return !jaxrsResponse.isClosed() && jaxrsResponse.hasEntity();
+    }
+
+    @Override
+    public Object getEntity() {
+        return !jaxrsResponse.isClosed() ? jaxrsResponse.getEntity() : null;
+    }
+
+    @Override
+    public OutputStream getEntityStream() {
+        try {
+            return httpResponse.getOutputStream();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void setEntityStream(OutputStream entityStream) {
+        httpResponse.setOutputStream(entityStream);
+    }
+
+    @Override
+    public Annotation[] getEntityAnnotations() {
+        return jaxrsResponse.getAnnotations();
+    }
+
+    @Override
+    public MultivaluedMap<String, String> getStringHeaders() {
+        return jaxrsResponse.getStringHeaders();
+    }
+
+    @Override
+    public String getHeaderString(String name) {
+        return jaxrsResponse.getHeaderString(name);
+    }
+
+    @Override
+    public synchronized void suspend() {
+        if (continuation == null) throw new RuntimeException("Suspend not supported yet");
+        suspended = true;
+    }
+
+    @Override
+    public synchronized void resume() {
+        if (!suspended) throw new RuntimeException("Cannot resume: not suspended");
+        if (inFilter) {
+            // suspend/resume within filter, same thread: just ignore and move on
+            suspended = false;
+            return;
+        }
+
+        // go on, but with proper exception handling
+        try (ResteasyProviderFactory.CloseableContext c =
+                ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+            filter();
+        } catch (Throwable t) {
+            // don't throw to client
+            writeException(t);
+        }
+    }
+
+    @Override
+    public synchronized void resume(Throwable t) {
+        if (!suspended) throw new RuntimeException("Cannot resume: not suspended");
+        if (inFilter) {
+            // not suspended, or suspend/abortWith within filter, same thread: collect and move on
+            throwable = t;
+            suspended = false;
+        } else {
+            try (ResteasyProviderFactory.CloseableContext c =
+                    ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+                writeException(t);
+            }
+        }
+    }
+
+    private void writeException(Throwable t) {
+        /*
+         * Here we cannot call AsyncResponse.resume(t) because that would invoke the response filters
+         * and we should not invoke them because we're already in them.
+         */
+        HttpResponse httpResponse = (HttpResponse) contextDataMap.get(HttpResponse.class);
+        SynchronousDispatcher dispatcher = (SynchronousDispatcher) contextDataMap.get(Dispatcher.class);
+        ResteasyAsynchronousResponse asyncResponse = request.getAsyncContext().getAsyncResponse();
+
+        dispatcher.unhandledAsynchronousException(httpResponse, t);
+        onComplete.accept(t);
+        asyncResponse.complete();
+        asyncResponse.completionCallbacks(t);
+    }
+
+    public synchronized void filter() throws IOException {
+        while (currentFilter < responseFilters.length) {
+            ContainerResponseFilter filter = responseFilters[currentFilter++];
+            try {
+                suspended = false;
+                throwable = null;
+                inFilter = true;
+                filter.filter(requestContext, this);
+            } catch (IOException e) {
+                throw new ApplicationException(e);
+            } finally {
+                inFilter = false;
+            }
+            if (suspended) {
+                if (!request.getAsyncContext().isSuspended()) {
+                    request.getAsyncContext().suspend();
+                    weSuspended = true;
+                }
+                // ignore any abort request until we are resumed
+                filterReturnIsMeaningful = false;
+                return;
+            }
+            if (throwable != null) {
+                // handle the case where we've been suspended by a previous filter
+                if (filterReturnIsMeaningful) SynchronousDispatcher.rethrow(throwable);
+                else {
+                    writeException(throwable);
+                    return;
+                }
+            }
+        }
+        // here it means we reached the last filter
+
+        // some frameworks don't support async request filters, in which case suspend() is forbidden
+        // so if we get here we're still synchronous and don't have a continuation, which must be in
+        // the caller
+        if (continuation == null) return;
+
+        // if we've never been suspended, the caller is valid so let it handle any exception
+        if (filterReturnIsMeaningful) {
+            continuation.run();
+            onComplete.accept(null);
+            return;
+        }
+        // if we've been suspended then the caller is a filter and have to invoke our continuation
+        // try to write it out
+        try {
+            continuation.run();
+            onComplete.accept(null);
+            if (weSuspended) {
+                // if we're the ones who turned the request async, nobody will call complete() for us, so we have to
+                HttpServletRequest httpServletRequest =
+                        (HttpServletRequest) contextDataMap.get(HttpServletRequest.class);
+                httpServletRequest.getAsyncContext().complete();
+            }
+        } catch (IOException e) {
+            logger.error(
+                    "",
+                    "Dubbo container response context filter error",
+                    "request method is: " + request.getHttpMethod() + "and request uri is:"
+                            + request.getUri().getPath(),
+                    "",
+                    e);
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/DubboPreMatchContainerRequestContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/DubboPreMatchContainerRequestContext.java
new file mode 100644
index 0000000..1490ace
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/DubboPreMatchContainerRequestContext.java
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter;
+
+import org.jboss.resteasy.core.interception.jaxrs.SuspendableContainerRequestContext;
+import org.jboss.resteasy.plugins.server.netty.NettyHttpRequest;
+import org.jboss.resteasy.specimpl.BuiltResponse;
+import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
+import org.jboss.resteasy.spi.ApplicationException;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public class DubboPreMatchContainerRequestContext implements SuspendableContainerRequestContext {
+    protected final NettyHttpRequest httpRequest;
+    protected Response response;
+    private ContainerRequestFilter[] requestFilters;
+    private int currentFilter;
+    private boolean suspended;
+    private boolean filterReturnIsMeaningful = true;
+    private Supplier<BuiltResponse> continuation;
+    private Map<Class<?>, Object> contextDataMap;
+    private boolean inFilter;
+    private Throwable throwable;
+    private boolean startedContinuation;
+
+    public DubboPreMatchContainerRequestContext(
+            final NettyHttpRequest request,
+            final ContainerRequestFilter[] requestFilters,
+            final Supplier<BuiltResponse> continuation) {
+        this.httpRequest = request;
+        this.requestFilters = requestFilters;
+        this.continuation = continuation;
+        contextDataMap = ResteasyProviderFactory.getContextDataMap();
+    }
+
+    public NettyHttpRequest getHttpRequest() {
+        return httpRequest;
+    }
+
+    public Response getResponseAbortedWith() {
+        return response;
+    }
+
+    @Override
+    public Object getProperty(String name) {
+        return httpRequest.getAttribute(name);
+    }
+
+    @Override
+    public Collection<String> getPropertyNames() {
+        ArrayList<String> names = new ArrayList<>();
+        Enumeration<String> enames = httpRequest.getAttributeNames();
+        while (enames.hasMoreElements()) {
+            names.add(enames.nextElement());
+        }
+        return names;
+    }
+
+    @Override
+    public void setProperty(String name, Object object) {
+        httpRequest.setAttribute(name, object);
+    }
+
+    @Override
+    public void removeProperty(String name) {
+        httpRequest.removeAttribute(name);
+    }
+
+    @Override
+    public UriInfo getUriInfo() {
+        return httpRequest.getUri();
+    }
+
+    @Override
+    public void setRequestUri(URI requestUri) throws IllegalStateException {
+        httpRequest.setRequestUri(requestUri);
+    }
+
+    @Override
+    public void setRequestUri(URI baseUri, URI requestUri) throws IllegalStateException {
+        httpRequest.setRequestUri(baseUri, requestUri);
+    }
+
+    @Override
+    public String getMethod() {
+        return httpRequest.getHttpMethod();
+    }
+
+    @Override
+    public void setMethod(String method) {
+        httpRequest.setHttpMethod(method);
+    }
+
+    @Override
+    public MultivaluedMap<String, String> getHeaders() {
+        return ((ResteasyHttpHeaders) httpRequest.getHttpHeaders()).getMutableHeaders();
+    }
+
+    @Override
+    public Date getDate() {
+        return httpRequest.getHttpHeaders().getDate();
+    }
+
+    @Override
+    public Locale getLanguage() {
+        return httpRequest.getHttpHeaders().getLanguage();
+    }
+
+    @Override
+    public int getLength() {
+        return httpRequest.getHttpHeaders().getLength();
+    }
+
+    @Override
+    public MediaType getMediaType() {
+        return httpRequest.getHttpHeaders().getMediaType();
+    }
+
+    @Override
+    public List<MediaType> getAcceptableMediaTypes() {
+        return httpRequest.getHttpHeaders().getAcceptableMediaTypes();
+    }
+
+    @Override
+    public List<Locale> getAcceptableLanguages() {
+        return httpRequest.getHttpHeaders().getAcceptableLanguages();
+    }
+
+    @Override
+    public Map<String, Cookie> getCookies() {
+        return httpRequest.getHttpHeaders().getCookies();
+    }
+
+    @Override
+    public boolean hasEntity() {
+        return getMediaType() != null;
+    }
+
+    @Override
+    public InputStream getEntityStream() {
+        return httpRequest.getInputStream();
+    }
+
+    @Override
+    public void setEntityStream(InputStream entityStream) {
+        httpRequest.setInputStream(entityStream);
+    }
+
+    @Override
+    public SecurityContext getSecurityContext() {
+        return ResteasyProviderFactory.getContextData(SecurityContext.class);
+    }
+
+    @Override
+    public void setSecurityContext(SecurityContext context) {
+        ResteasyProviderFactory.pushContext(SecurityContext.class, context);
+    }
+
+    @Override
+    public Request getRequest() {
+        return ResteasyProviderFactory.getContextData(Request.class);
+    }
+
+    @Override
+    public String getHeaderString(String name) {
+        return httpRequest.getHttpHeaders().getHeaderString(name);
+    }
+
+    @Override
+    public synchronized void suspend() {
+        if (continuation == null) throw new RuntimeException("Suspend not supported yet");
+        suspended = true;
+    }
+
+    @Override
+    public synchronized void abortWith(Response response) {
+        if (suspended && !inFilter) {
+            try (ResteasyProviderFactory.CloseableContext c =
+                    ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+                httpRequest.getAsyncContext().getAsyncResponse().resume(response);
+            }
+        } else {
+            // not suspended, or suspend/abortWith within filter, same thread: collect and move on
+            this.response = response;
+            suspended = false;
+        }
+    }
+
+    @Override
+    public synchronized void resume() {
+        if (!suspended) throw new RuntimeException("Cannot resume: not suspended");
+        if (inFilter) {
+            // suspend/resume within filter, same thread: just ignore and move on
+            suspended = false;
+            return;
+        }
+
+        // go on, but with proper exception handling
+        try (ResteasyProviderFactory.CloseableContext c =
+                ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+            filter();
+        } catch (Throwable t) {
+            // don't throw to client
+            writeException(t);
+        }
+    }
+
+    @Override
+    public synchronized void resume(Throwable t) {
+        if (!suspended) throw new RuntimeException("Cannot resume: not suspended");
+        if (inFilter) {
+            // not suspended, or suspend/abortWith within filter, same thread: collect and move on
+            throwable = t;
+            suspended = false;
+        } else {
+            try (ResteasyProviderFactory.CloseableContext c =
+                    ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+                writeException(t);
+            }
+        }
+    }
+
+    private void writeException(Throwable t) {
+        /*
+         * Here, contrary to ContainerResponseContextImpl.writeException, we can use the async response
+         * to write the exception, because it calls the right response filters, complete() and callbacks
+         */
+        httpRequest.getAsyncContext().getAsyncResponse().resume(t);
+    }
+
+    public synchronized BuiltResponse filter() throws Throwable {
+        while (currentFilter < requestFilters.length) {
+            ContainerRequestFilter filter = requestFilters[currentFilter++];
+            try {
+                suspended = false;
+                response = null;
+                throwable = null;
+                inFilter = true;
+                filter.filter(this);
+            } catch (IOException e) {
+                throw new ApplicationException(e);
+            } finally {
+                inFilter = false;
+            }
+            if (suspended) {
+                if (!httpRequest.getAsyncContext().isSuspended())
+                    // ignore any abort request until we are resumed
+                    filterReturnIsMeaningful = false;
+                response = null;
+                return null;
+            }
+            BuiltResponse serverResponse = (BuiltResponse) getResponseAbortedWith();
+            if (serverResponse != null) {
+                // handle the case where we've been suspended by a previous filter
+                return serverResponse;
+            }
+
+            if (throwable != null) {
+                // handle the case where we've been suspended by a previous filter
+                throw throwable;
+            }
+        }
+        // here it means we reached the last filter
+        // some frameworks don't support async request filters, in which case suspend() is forbidden
+        // so if we get here we're still synchronous and don't have a continuation, which must be in
+        // the caller
+        startedContinuation = true;
+        if (continuation == null) return null;
+        // in any case, return the continuation: sync will use it, and async will ignore it
+        return continuation.get();
+    }
+
+    public boolean startedContinuation() {
+        return startedContinuation;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/ResteasyNettyHttpResponse.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/ResteasyNettyHttpResponse.java
new file mode 100644
index 0000000..2948217
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/ResteasyNettyHttpResponse.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter;
+
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.jboss.resteasy.spi.HttpResponse;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+public class ResteasyNettyHttpResponse implements HttpResponse {
+
+    private NettyHttpResponse response;
+
+    private MultivaluedMap<String, Object> multivaluedMap = new MultivaluedMapImpl<>();
+
+    public ResteasyNettyHttpResponse(NettyHttpResponse response) {
+        this.response = response;
+        Map<String, List<String>> outputHeaders = response.getOutputHeaders();
+
+        for (Map.Entry<String, List<String>> headers : outputHeaders.entrySet()) {
+            String key = headers.getKey();
+            List<String> value = headers.getValue();
+
+            if (value == null || value.isEmpty()) {
+                continue;
+            }
+
+            for (String val : value) {
+                multivaluedMap.add(key, val);
+            }
+        }
+    }
+
+    @Override
+    public int getStatus() {
+        return response.getStatus();
+    }
+
+    @Override
+    public void setStatus(int status) {
+
+        response.setStatus(status);
+    }
+
+    @Override
+    public MultivaluedMap<String, Object> getOutputHeaders() {
+        return multivaluedMap;
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        return response.getOutputStream();
+    }
+
+    @Override
+    public void setOutputStream(OutputStream os) {
+        response.setOutputStream(os);
+    }
+
+    @Override
+    public void addNewCookie(NewCookie cookie) {}
+
+    @Override
+    public void sendError(int status) throws IOException {
+
+        response.sendError(status);
+    }
+
+    @Override
+    public void sendError(int status, String message) throws IOException {
+        response.sendError(status, message);
+    }
+
+    @Override
+    public boolean isCommitted() {
+        return false;
+    }
+
+    @Override
+    public void reset() {
+
+        response.reset();
+    }
+
+    @Override
+    public void flushBuffer() throws IOException {}
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/ResteasyRequestContainerFilterAdapter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/ResteasyRequestContainerFilterAdapter.java
new file mode 100644
index 0000000..5ede5b4
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/ResteasyRequestContainerFilterAdapter.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestRequestFilter;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.jboss.resteasy.specimpl.BuiltResponse;
+
+import javax.ws.rs.container.ContainerRequestFilter;
+import java.util.List;
+
+@Activate(
+        value = "resteasy",
+        onClass = {
+            "javax.ws.rs.container.ContainerRequestFilter",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpRequest",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"
+        },
+        order = Integer.MAX_VALUE - 1)
+public class ResteasyRequestContainerFilterAdapter implements RestRequestFilter, ResteasyContext {
+
+    @Override
+    public void filter(RestFilterContext restFilterContext) throws Exception {
+
+        ServiceDeployer serviceDeployer = restFilterContext.getServiceDeployer();
+        RequestFacade requestFacade = restFilterContext.getRequestFacade();
+        URL url = restFilterContext.getUrl();
+        NettyHttpResponse response = restFilterContext.getResponse();
+
+        List<ContainerRequestFilter> containerRequestFilters =
+                getExtension(serviceDeployer, ContainerRequestFilter.class);
+
+        if (containerRequestFilters.isEmpty()) {
+
+            return;
+        }
+
+        DubboPreMatchContainerRequestContext containerRequestContext = convertHttpRequestToContainerRequestContext(
+                requestFacade, containerRequestFilters.toArray(new ContainerRequestFilter[0]));
+
+        // set resteasy request for save user`s custom  request attribute
+        restFilterContext.setOriginRequest(containerRequestContext.getHttpRequest());
+
+        try {
+            BuiltResponse restResponse = containerRequestContext.filter();
+
+            if (restResponse == null) {
+                return;
+            }
+
+            addResponseHeaders(response, restResponse.getHeaders());
+            writeResteasyResponse(url, requestFacade, response, restResponse);
+            // completed
+            restFilterContext.setComplete(true);
+        } catch (Throwable e) {
+            throw new RuntimeException("dubbo rest resteasy ContainerRequestFilter write response encode error", e);
+        } finally {
+            containerRequestContext.getHttpRequest().releaseContentBuffer();
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/ResteasyResponseContainerFilterAdapter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/ResteasyResponseContainerFilterAdapter.java
new file mode 100644
index 0000000..c91623c
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/filter/ResteasyResponseContainerFilterAdapter.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestResponseFilter;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.jboss.resteasy.spi.HttpResponse;
+
+import javax.ws.rs.container.ContainerResponseFilter;
+import java.util.List;
+
+@Activate(
+        value = "resteasy",
+        order = Integer.MAX_VALUE - 1000,
+        onClass = {
+            "org.jboss.resteasy.specimpl.BuiltResponse",
+            "javax.ws.rs.container.ContainerResponseFilter",
+            "org.jboss.resteasy.spi.HttpResponse",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"
+        })
+public class ResteasyResponseContainerFilterAdapter implements RestResponseFilter, ResteasyContext {
+    @Override
+    public void filter(RestFilterContext restFilterContext) throws Exception {
+
+        ServiceDeployer serviceDeployer = restFilterContext.getServiceDeployer();
+        RequestFacade requestFacade = restFilterContext.getRequestFacade();
+        NettyHttpResponse response = restFilterContext.getResponse();
+        URL url = restFilterContext.getUrl();
+        List<ContainerResponseFilter> containerRequestFilters =
+                getExtension(serviceDeployer, ContainerResponseFilter.class);
+
+        if (containerRequestFilters.isEmpty()) {
+            return;
+        }
+
+        // response filter entity first
+
+        // build jaxrsResponse from rest netty response
+        DubboBuiltResponse dubboBuiltResponse =
+                new DubboBuiltResponse(response.getResponseBody(), response.getStatus(), response.getEntityClass());
+        // NettyHttpResponse wrapper
+        HttpResponse httpResponse = new ResteasyNettyHttpResponse(response);
+        DubboContainerResponseContextImpl containerResponseContext = createContainerResponseContext(
+                restFilterContext.getOriginRequest(),
+                requestFacade,
+                httpResponse,
+                dubboBuiltResponse,
+                containerRequestFilters.toArray(new ContainerResponseFilter[0]));
+        containerResponseContext.filter();
+
+        // user reset entity
+        if (dubboBuiltResponse.hasEntity() && dubboBuiltResponse.isResetEntity()) {
+            // clean  output stream data
+            restOutputStream(response);
+            writeResteasyResponse(url, requestFacade, response, dubboBuiltResponse);
+        }
+        addResponseHeaders(response, httpResponse.getOutputHeaders());
+
+        restFilterContext.setComplete(true);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/intercept/DubboServerWriterInterceptorContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/intercept/DubboServerWriterInterceptorContext.java
new file mode 100644
index 0000000..57a22a0
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/intercept/DubboServerWriterInterceptorContext.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy.intercept;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.jboss.resteasy.core.interception.ServerWriterInterceptorContext;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.WriterInterceptor;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+public class DubboServerWriterInterceptorContext extends ServerWriterInterceptorContext {
+    private static final ErrorTypeAwareLogger logger =
+            LoggerFactory.getErrorTypeAwareLogger(DubboServerWriterInterceptorContext.class);
+
+    public DubboServerWriterInterceptorContext(
+            WriterInterceptor[] interceptors,
+            ResteasyProviderFactory providerFactory,
+            Object entity,
+            Class type,
+            Type genericType,
+            Annotation[] annotations,
+            MediaType mediaType,
+            MultivaluedMap<String, Object> headers,
+            OutputStream outputStream,
+            HttpRequest request) {
+        super(
+                interceptors,
+                providerFactory,
+                entity,
+                type,
+                genericType,
+                annotations,
+                mediaType,
+                headers,
+                outputStream,
+                request);
+    }
+
+    @Override
+    public void proceed() throws IOException, WebApplicationException {
+        logger.debug("Dubbo server writer intercept  context: " + getClass().getName() + "  Method : proceed");
+
+        if (interceptors == null || index >= interceptors.length) {
+            return;
+        } else {
+
+            logger.debug("Dubbo server writer intercept  context WriterInterceptor: "
+                    + interceptors[index].getClass().getName());
+            interceptors[index++].aroundWriteTo(this);
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/intercept/ResteasyStatusCodeInterceptor.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/intercept/ResteasyStatusCodeInterceptor.java
new file mode 100644
index 0000000..0f1aa46
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/intercept/ResteasyStatusCodeInterceptor.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy.intercept;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestInterceptContext;
+import org.jboss.resteasy.specimpl.AbstractBuiltResponse;
+
+@Activate(
+        value = "resteasy-resStatus",
+        onClass = {
+            "javax.ws.rs.ext.WriterInterceptorContext",
+            "org.jboss.resteasy.specimpl.BuiltResponse",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpRequest",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"
+        },
+        order = Integer.MAX_VALUE)
+public class ResteasyStatusCodeInterceptor implements RestResponseInterceptor, ResteasyContext {
+
+    @Override
+    public void intercept(RestInterceptContext restResponseInterceptor) throws Exception {
+        Object result = restResponseInterceptor.getResult();
+
+        if (result == null || (!(result instanceof AbstractBuiltResponse))) {
+            return;
+        }
+
+        AbstractBuiltResponse abstractBuiltResponse = (AbstractBuiltResponse) result;
+
+        restResponseInterceptor.getResponse().setStatus(abstractBuiltResponse.getStatus());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/intercept/ResteasyWriterInterceptorAdapter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/intercept/ResteasyWriterInterceptorAdapter.java
new file mode 100644
index 0000000..27b290f
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/extension/resteasy/intercept/ResteasyWriterInterceptorAdapter.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.extension.resteasy.intercept;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestInterceptContext;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext;
+import org.jboss.resteasy.plugins.server.netty.NettyHttpRequest;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.WriterInterceptor;
+import java.io.ByteArrayOutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+
+@Activate(
+        value = "resteasy",
+        onClass = {
+            "javax.ws.rs.ext.WriterInterceptorContext",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpRequest",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"
+        })
+public class ResteasyWriterInterceptorAdapter implements RestResponseInterceptor, ResteasyContext {
+
+    private ResteasyProviderFactory resteasyProviderFactory = getResteasyProviderFactory();
+
+    @Override
+    public void intercept(RestInterceptContext restResponseInterceptor) throws Exception {
+
+        RpcInvocation rpcInvocation = restResponseInterceptor.getRpcInvocation();
+        ServiceDeployer serviceDeployer = restResponseInterceptor.getServiceDeployer();
+        RequestFacade request = restResponseInterceptor.getRequestFacade();
+        NettyHttpResponse response = restResponseInterceptor.getResponse();
+        Object result = restResponseInterceptor.getResult();
+
+        Class<?> type = rpcInvocation.getReturnType();
+
+        List<WriterInterceptor> extension = serviceDeployer.getExtensions(WriterInterceptor.class);
+
+        if (extension.isEmpty()) {
+            return;
+        }
+
+        NettyHttpRequest nettyHttpRequest = (NettyHttpRequest) restResponseInterceptor.getOriginRequest();
+
+        HttpRequest restRequest = nettyHttpRequest == null ? createNettyHttpRequest(request) : nettyHttpRequest;
+
+        MultivaluedMap<String, Object> headers = new MultivaluedMapImpl();
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+        try {
+
+            // get content-type
+            String value = getAcceptMediaType(request, type).value;
+
+            MediaType mediaType = MediaType.valueOf(value);
+
+            AbstractWriterInterceptorContext writerContext = getAbstractWriterInterceptorContext(
+                    restRequest, extension, result, type, type, mediaType, os, headers);
+
+            writerContext.proceed();
+            ByteArrayOutputStream outputStream = (ByteArrayOutputStream) writerContext.getOutputStream();
+
+            addResponseHeaders(response, writerContext.getHeaders());
+
+            if (outputStream.size() <= 0) {
+                return;
+            }
+
+            // intercept response  first
+            restOutputStream(response);
+
+            byte[] bytes = outputStream.toByteArray();
+            response.getOutputStream().write(bytes);
+            response.addOutputHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), value);
+
+            restResponseInterceptor.setComplete(true);
+        } finally {
+            IOUtils.close(os);
+        }
+    }
+
+    private AbstractWriterInterceptorContext getAbstractWriterInterceptorContext(
+            HttpRequest request,
+            List<WriterInterceptor> extension,
+            Object entity,
+            Class type,
+            Type genericType,
+            MediaType mediaType,
+            ByteArrayOutputStream os,
+            MultivaluedMap<String, Object> headers) {
+        AbstractWriterInterceptorContext writerContext = new DubboServerWriterInterceptorContext(
+                extension.toArray(new WriterInterceptor[0]),
+                resteasyProviderFactory,
+                entity,
+                type,
+                genericType,
+                new Annotation[0],
+                mediaType,
+                headers,
+                os,
+                request);
+        return writerContext;
+    }
+
+    protected ResteasyProviderFactory getResteasyProviderFactory() {
+        return new ResteasyProviderFactory();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestFilter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestFilter.java
new file mode 100644
index 0000000..dc7608d
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestFilter.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter;
+
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
+
+/**
+ * Rest filter is extended by rest request & response filter
+ */
+public interface RestFilter {
+
+    void filter(RestFilterContext restFilterContext) throws Exception;
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestRequestFilter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestRequestFilter.java
new file mode 100644
index 0000000..da5a2c9
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestRequestFilter.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+
+/**
+ * Rest filter will be invoked before http handler
+ */
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface RestRequestFilter extends RestFilter {}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestResponseFilter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestResponseFilter.java
new file mode 100644
index 0000000..97fdf00
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestResponseFilter.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+
+/**
+ * Rest  response filter will be invoked when  response is written to channel
+ */
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface RestResponseFilter extends RestFilter {}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestResponseInterceptor.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestResponseInterceptor.java
new file mode 100644
index 0000000..8e82430
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/RestResponseInterceptor.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestInterceptContext;
+
+/**
+ * RestResponseInterceptorChain will take effect before result is written to response
+ */
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface RestResponseInterceptor {
+
+    void intercept(RestInterceptContext restResponseInterceptor) throws Exception;
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/ServiceInvokeRestFilter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/ServiceInvokeRestFilter.java
new file mode 100644
index 0000000..4e7ce21
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/ServiceInvokeRestFilter.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter;
+
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpRequest;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.metadata.extension.rest.api.PathMatcher;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
+import org.apache.dubbo.rpc.protocol.rest.RestRPCInvocationUtil;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.exception.PathNoFoundException;
+import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandlerResult;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestInterceptContext;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.pair.InvokerAndRestMethodMetadataPair;
+import org.apache.dubbo.rpc.protocol.rest.pair.MessageCodecResultPair;
+import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil;
+
+import java.util.List;
+import java.util.Objects;
+
+@Activate(value = "invoke", order = Integer.MAX_VALUE)
+public class ServiceInvokeRestFilter implements RestRequestFilter {
+    private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+
+    private final List<RestResponseInterceptor> restResponseInterceptors;
+
+    public ServiceInvokeRestFilter(FrameworkModel frameworkModel) {
+        restResponseInterceptors =
+                frameworkModel.getExtensionLoader(RestResponseInterceptor.class).getActivateExtensions();
+    }
+
+    @Override
+    public void filter(RestFilterContext restFilterContext) throws Exception {
+        NettyRequestFacade nettyRequestFacade = (NettyRequestFacade) restFilterContext.getRequestFacade();
+
+        FullHttpRequest nettyHttpRequest = nettyRequestFacade.getRequest();
+
+        doHandler(
+                nettyHttpRequest,
+                restFilterContext.getResponse(),
+                restFilterContext.getRequestFacade(),
+                restFilterContext.getUrl(),
+                restFilterContext.getOriginRequest(),
+                restFilterContext.getServiceDeployer());
+    }
+
+    private void doHandler(
+            HttpRequest nettyHttpRequest,
+            NettyHttpResponse nettyHttpResponse,
+            RequestFacade request,
+            URL url,
+            Object originRequest, // resteasy  request
+            ServiceDeployer serviceDeployer)
+            throws Exception {
+        PathMatcher pathMatcher = RestRPCInvocationUtil.createPathMatcher(request);
+
+        // path NoFound 404
+        if (!serviceDeployer.hashRestMethod(pathMatcher)) {
+            throw new PathNoFoundException("rest service Path no found, current path info:" + pathMatcher);
+        }
+
+        // method disallowed
+        if (!serviceDeployer.isMethodAllowed(pathMatcher)) {
+            nettyHttpResponse.sendError(
+                    405,
+                    "service require request method is : "
+                            + serviceDeployer.pathHttpMethods(pathMatcher)
+                            + ", but current request method is: " + request.getMethod());
+            return;
+        }
+        // compare http method and  acquire metadata by request
+        InvokerAndRestMethodMetadataPair restMethodMetadataPair =
+                RestRPCInvocationUtil.getRestMethodMetadataAndInvokerPair(
+                        pathMatcher.compareHttpMethod(true), serviceDeployer);
+
+        Invoker invoker = restMethodMetadataPair.getInvoker();
+
+        RestMethodMetadata restMethodMetadata = restMethodMetadataPair.getRestMethodMetadata();
+
+        // content-type  support judge,throw unSupportException
+        acceptSupportJudge(request, restMethodMetadata.getReflectMethod().getReturnType());
+
+        // build RpcInvocation
+        RpcInvocation rpcInvocation = RestRPCInvocationUtil.createBaseRpcInvocation(request, restMethodMetadata);
+
+        // parse method real args
+        RestRPCInvocationUtil.parseMethodArgs(
+                rpcInvocation, request, nettyHttpRequest, nettyHttpResponse, restMethodMetadata);
+
+        // execute business  method invoke
+        Result result = invoker.invoke(rpcInvocation);
+
+        // set raw response
+        nettyHttpResponse.setResponseBody(result.getValue());
+
+        if (result.hasException()) {
+            Throwable exception = result.getException();
+            logger.error(
+                    "", exception.getMessage(), "", "dubbo rest protocol provider Invoker invoke error", exception);
+
+            if (serviceDeployer.getExceptionMapper().hasExceptionMapper(exception)) {
+                ExceptionHandlerResult exceptionToResult =
+                        serviceDeployer.getExceptionMapper().exceptionToResult(result.getException());
+                writeResult(
+                        nettyHttpResponse, request, url, exceptionToResult.getEntity(), rpcInvocation.getReturnType());
+                nettyHttpResponse.setStatus(exceptionToResult.getStatus());
+            } else {
+                nettyHttpResponse.sendError(
+                        500,
+                        "\n dubbo rest business exception, error cause is: "
+                                + result.getException().getCause()
+                                + "\n message is: " + result.getException().getMessage()
+                                + "\n stacktrace is: " + stackTraceToString(exception));
+            }
+        }
+
+        try {
+            RestInterceptContext restFilterContext = new RestInterceptContext(
+                    url, request, nettyHttpResponse, serviceDeployer, result.getValue(), rpcInvocation);
+            // set filter request
+            restFilterContext.setOriginRequest(originRequest);
+
+            // invoke the intercept chain before Result  write to  response
+            executeResponseIntercepts(restFilterContext);
+        } catch (Exception exception) {
+            logger.error(
+                    "", exception.getMessage(), "", "dubbo rest protocol execute ResponseIntercepts error", exception);
+            throw exception;
+        }
+    }
+
+    /**
+     * write return value by accept
+     *
+     * @param nettyHttpResponse
+     * @param request
+     * @param value
+     * @param returnType
+     * @throws Exception
+     */
+    public static void writeResult(
+            NettyHttpResponse nettyHttpResponse, RequestFacade<?> request, URL url, Object value, Class<?> returnType)
+            throws Exception {
+        MediaType mediaType = getAcceptMediaType(request, returnType);
+        writeResult(nettyHttpResponse, url, value, returnType, mediaType);
+    }
+
+    public static void writeResult(
+            NettyHttpResponse nettyHttpResponse, URL url, Object value, Class<?> returnType, MediaType mediaType)
+            throws Exception {
+        MessageCodecResultPair booleanMediaTypePair = HttpMessageCodecManager.httpMessageEncode(
+                nettyHttpResponse.getOutputStream(), value, url, mediaType, returnType);
+        // reset raw response result
+        nettyHttpResponse.setResponseBody(value);
+        nettyHttpResponse.addOutputHeaders(
+                RestHeaderEnum.CONTENT_TYPE.getHeader(), booleanMediaTypePair.getMediaType().value);
+    }
+
+    /**
+     * return first match , if any multiple content-type
+     *
+     * @param request
+     * @return
+     */
+    public static MediaType getAcceptMediaType(RequestFacade request, Class<?> returnType) {
+        String accept = request.getHeader(RestHeaderEnum.ACCEPT.getHeader());
+        accept = Objects.isNull(accept) ? MediaType.ALL_VALUE.value : accept;
+        MediaType mediaType = MediaTypeUtil.convertMediaType(returnType, accept);
+        return mediaType;
+    }
+
+    /**
+     * accept can not support will throw UnSupportAcceptException
+     *
+     * @param requestFacade
+     */
+    private void acceptSupportJudge(RequestFacade requestFacade, Class<?> returnType) {
+        try {
+            // media type judge
+            getAcceptMediaType(requestFacade, returnType);
+        } catch (UnSupportContentTypeException e) {
+            // return type judge
+            MediaType mediaType = HttpMessageCodecManager.typeSupport(returnType);
+
+            String accept = requestFacade.getHeader(RestHeaderEnum.ACCEPT.getHeader());
+            if (mediaType == null || accept == null) {
+                throw e;
+            }
+
+            if (!accept.contains(mediaType.value)) {
+
+                throw e;
+            }
+        }
+    }
+
+    public static String stackTraceToString(Throwable throwable) {
+        StackTraceElement[] stackTrace = throwable.getStackTrace();
+
+        StringBuilder stringBuilder = new StringBuilder("\n");
+        for (StackTraceElement traceElement : stackTrace) {
+            stringBuilder.append("\tat " + traceElement).append("\n");
+        }
+
+        return stringBuilder.toString();
+    }
+
+    /**
+     * execute response Intercepts
+     *
+     * @param restFilterContext
+     * @throws Exception
+     */
+    public void executeResponseIntercepts(RestInterceptContext restFilterContext) throws Exception {
+
+        for (RestResponseInterceptor restResponseInterceptor : restResponseInterceptors) {
+
+            restResponseInterceptor.intercept(restFilterContext);
+
+            if (restFilterContext.complete()) {
+                break;
+            }
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/ServiceInvokeRestResponseInterceptor.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/ServiceInvokeRestResponseInterceptor.java
new file mode 100644
index 0000000..db3c3d8
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/ServiceInvokeRestResponseInterceptor.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestInterceptContext;
+
+import static org.apache.dubbo.rpc.protocol.rest.filter.ServiceInvokeRestFilter.writeResult;
+
+/**
+ * default RestResponseInterceptor
+ */
+@Activate(value = "invoke", order = Integer.MAX_VALUE)
+public class ServiceInvokeRestResponseInterceptor implements RestResponseInterceptor {
+
+    @Override
+    public void intercept(RestInterceptContext restInterceptContext) throws Exception {
+
+        writeResult(
+                restInterceptContext.getResponse(),
+                restInterceptContext.getRequestFacade(),
+                restInterceptContext.getUrl(),
+                restInterceptContext.getResult(),
+                restInterceptContext.getRpcInvocation().getReturnType());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/context/FilterContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/context/FilterContext.java
new file mode 100644
index 0000000..c9544f4
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/context/FilterContext.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter.context;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.netty.HttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+
+public interface FilterContext {
+
+    URL getUrl();
+
+    RequestFacade getRequestFacade();
+
+    HttpResponse getResponse();
+
+    ServiceDeployer getServiceDeployer();
+
+    boolean complete();
+
+    void setComplete(boolean complete);
+
+    Object getOriginRequest();
+
+    Object getOriginResponse();
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/context/RestFilterContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/context/RestFilterContext.java
new file mode 100644
index 0000000..dd3e2c5
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/context/RestFilterContext.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter.context;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+
+public class RestFilterContext implements FilterContext {
+    protected URL url;
+    protected RequestFacade requestFacade;
+    protected NettyHttpResponse response;
+    protected ServiceDeployer serviceDeployer;
+    protected boolean completed;
+    protected Object originRequest;
+    protected Object originResponse;
+
+    public RestFilterContext(
+            URL url, RequestFacade requestFacade, NettyHttpResponse response, ServiceDeployer serviceDeployer) {
+        this.url = url;
+        this.requestFacade = requestFacade;
+        this.response = response;
+        this.serviceDeployer = serviceDeployer;
+    }
+
+    @Override
+    public URL getUrl() {
+        return url;
+    }
+
+    @Override
+    public RequestFacade getRequestFacade() {
+        return requestFacade;
+    }
+
+    @Override
+    public NettyHttpResponse getResponse() {
+        return response;
+    }
+
+    @Override
+    public ServiceDeployer getServiceDeployer() {
+        return serviceDeployer;
+    }
+
+    @Override
+    public boolean complete() {
+        return completed;
+    }
+
+    @Override
+    public void setComplete(boolean complete) {
+        this.completed = complete;
+    }
+
+    @Override
+    public Object getOriginRequest() {
+        return originRequest;
+    }
+
+    @Override
+    public Object getOriginResponse() {
+        return originResponse;
+    }
+
+    public void setOriginRequest(Object originRequest) {
+        if (this.originRequest != null) {
+            return;
+        }
+        this.originRequest = originRequest;
+    }
+
+    public void setOriginResponse(Object originResponse) {
+        this.originResponse = originResponse;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/context/RestInterceptContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/context/RestInterceptContext.java
new file mode 100644
index 0000000..9ba91ae
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/filter/context/RestInterceptContext.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter.context;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+
+public class RestInterceptContext extends RestFilterContext {
+
+    private Object result;
+    private RpcInvocation rpcInvocation;
+
+    public RestInterceptContext(
+            URL url,
+            RequestFacade requestFacade,
+            NettyHttpResponse response,
+            ServiceDeployer serviceDeployer,
+            Object result,
+            RpcInvocation rpcInvocation) {
+        super(url, requestFacade, response, serviceDeployer);
+        this.result = result;
+        this.rpcInvocation = rpcInvocation;
+    }
+
+    public Object getResult() {
+        return result;
+    }
+
+    public RpcInvocation getRpcInvocation() {
+        return rpcInvocation;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java
new file mode 100644
index 0000000..55b24de
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/handler/NettyHttpHandler.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.handler;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.remoting.http.HttpHandler;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.exception.MediaTypeUnSupportException;
+import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException;
+import org.apache.dubbo.rpc.protocol.rest.exception.PathNoFoundException;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestFilter;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestRequestFilter;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestResponseFilter;
+import org.apache.dubbo.rpc.protocol.rest.filter.ServiceInvokeRestFilter;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * netty http request handler
+ */
+public class NettyHttpHandler implements HttpHandler<NettyRequestFacade, NettyHttpResponse> {
+    private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+    private final ServiceDeployer serviceDeployer;
+    private final URL url;
+    private final List<RestFilter> restRequestFilters;
+    private final List<RestFilter> restResponseFilters;
+
+    public NettyHttpHandler(ServiceDeployer serviceDeployer, URL url) {
+        this.serviceDeployer = serviceDeployer;
+        this.url = url;
+        restRequestFilters = new ArrayList<>(url.getOrDefaultFrameworkModel()
+                .getExtensionLoader(RestRequestFilter.class)
+                .getActivateExtensions());
+        restResponseFilters = new ArrayList<>(url.getOrDefaultFrameworkModel()
+                .getExtensionLoader(RestResponseFilter.class)
+                .getActivateExtensions());
+    }
+
+    @Override
+    public void handle(NettyRequestFacade requestFacade, NettyHttpResponse nettyHttpResponse) throws IOException {
+
+        // set remote address
+        RpcContext.getServiceContext().setRemoteAddress(requestFacade.getRemoteAddr(), requestFacade.getRemotePort());
+
+        // set local address
+        RpcContext.getServiceContext().setLocalAddress(requestFacade.getLocalAddr(), requestFacade.getLocalPort());
+
+        // set request
+        RpcContext.getServiceContext().setRequest(requestFacade);
+
+        // set response
+        RpcContext.getServiceContext().setResponse(nettyHttpResponse);
+
+        Object nettyHttpRequest = requestFacade.getRequest();
+
+        RestFilterContext restFilterContext =
+                new RestFilterContext(url, requestFacade, nettyHttpResponse, serviceDeployer);
+
+        try {
+
+            // first request filter
+            executeFilters(restFilterContext, restRequestFilters);
+
+        } catch (PathNoFoundException pathNoFoundException) {
+            logger.error(
+                    "",
+                    pathNoFoundException.getMessage(),
+                    "",
+                    "dubbo rest protocol provider path   no found ,raw request is :" + nettyHttpRequest,
+                    pathNoFoundException);
+            nettyHttpResponse.sendError(404, pathNoFoundException.getMessage());
+        } catch (ParamParseException paramParseException) {
+            logger.error(
+                    "",
+                    paramParseException.getMessage(),
+                    "",
+                    "dubbo rest protocol provider param parse error ,and raw request is :" + nettyHttpRequest,
+                    paramParseException);
+            nettyHttpResponse.sendError(400, paramParseException.getMessage());
+        } catch (MediaTypeUnSupportException contentTypeException) {
+            logger.error(
+                    "",
+                    contentTypeException.getMessage(),
+                    "",
+                    "dubbo rest protocol provider content-type un support" + nettyHttpRequest,
+                    contentTypeException);
+            nettyHttpResponse.sendError(415, contentTypeException.getMessage());
+        } catch (Throwable throwable) {
+            logger.error(
+                    "",
+                    throwable.getMessage(),
+                    "",
+                    "dubbo rest protocol provider error ,and raw request is  " + nettyHttpRequest,
+                    throwable);
+            nettyHttpResponse.sendError(
+                    500,
+                    "dubbo rest invoke Internal error, message is " + throwable.getMessage()
+                            + " ,and exception type is : " + throwable.getClass() + " , stacktrace is: "
+                            + ServiceInvokeRestFilter.stackTraceToString(throwable));
+        }
+
+        // second response filter
+        try {
+            executeFilters(restFilterContext, restResponseFilters);
+        } catch (Throwable throwable) {
+            logger.error(
+                    "",
+                    throwable.getMessage(),
+                    "",
+                    "dubbo rest protocol provider error ,and raw request is  " + nettyHttpRequest,
+                    throwable);
+            nettyHttpResponse.sendError(
+                    500,
+                    "dubbo rest invoke Internal error, message is " + throwable.getMessage()
+                            + " ,and exception type is : " + throwable.getClass() + " , stacktrace is: "
+                            + ServiceInvokeRestFilter.stackTraceToString(throwable));
+        }
+    }
+
+    /**
+     * execute rest filters
+     *
+     * @param restFilterContext
+     * @param restFilters
+     * @throws Exception
+     */
+    public void executeFilters(RestFilterContext restFilterContext, List<RestFilter> restFilters) throws Exception {
+
+        for (RestFilter restFilter : restFilters) {
+            restFilter.filter(restFilterContext);
+            if (restFilterContext.complete()) {
+                break;
+            }
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/integration/swagger/DubboSwaggerApiListingResource.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/integration/swagger/DubboSwaggerApiListingResource.java
new file mode 100644
index 0000000..60e75de
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/integration/swagger/DubboSwaggerApiListingResource.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.integration.swagger;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import io.swagger.jaxrs.listing.BaseApiListingResource;
+import org.apache.dubbo.config.annotation.Service;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+@Service
+public class DubboSwaggerApiListingResource extends BaseApiListingResource implements DubboSwaggerService {
+
+    @Context
+    ServletContext context;
+
+    @Override
+    public Response getListingJson(Application app, ServletConfig sc, HttpHeaders headers, UriInfo uriInfo)
+            throws JsonProcessingException {
+        Response response = getListingJsonResponse(app, context, sc, headers, uriInfo);
+        response.getHeaders().add("Access-Control-Allow-Origin", "*");
+        response.getHeaders().add("Access-Control-Allow-Headers", "x-requested-with, ssi-token");
+        response.getHeaders().add("Access-Control-Max-Age", "3600");
+        response.getHeaders().add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
+        return response;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/integration/swagger/DubboSwaggerService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/integration/swagger/DubboSwaggerService.java
new file mode 100644
index 0000000..628ea7e
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/integration/swagger/DubboSwaggerService.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.integration.swagger;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import javax.servlet.ServletConfig;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+@Path("dubbo")
+@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
+@Produces({MediaType.APPLICATION_JSON + "; " + "charset=UTF-8", MediaType.TEXT_XML + "; " + "charset=UTF-8"})
+public interface DubboSwaggerService {
+
+    @GET
+    @Path("swagger")
+    Response getListingJson(
+        @Context Application app, @Context ServletConfig sc, @Context HttpHeaders headers, @Context UriInfo uriInfo)
+            throws JsonProcessingException;
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java
new file mode 100644
index 0000000..f4f9a1e
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodec.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message;
+
+import org.apache.dubbo.common.extension.ExtensionScope;
+import org.apache.dubbo.common.extension.SPI;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+
+/**
+ *  for http body codec
+ * @param <InputStream>
+ * @param <OutputStream>
+ */
+@SPI(scope = ExtensionScope.FRAMEWORK)
+public interface HttpMessageCodec<InputStream, OutputStream>
+        extends HttpMessageDecode<InputStream>, HttpMessageEncode<OutputStream> {
+
+    /**
+     *  content-type support judge
+     * @param mediaType
+     * @param targetType
+     * @return
+     */
+    boolean contentTypeSupport(MediaType mediaType, Class<?> targetType);
+
+    /**
+     *  class type support judge
+     * @param targetType
+     * @return
+     */
+    boolean typeSupport(Class<?> targetType);
+
+    MediaType contentType();
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java
new file mode 100644
index 0000000..e3f5f30
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageCodecManager.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException;
+import org.apache.dubbo.rpc.protocol.rest.pair.MessageCodecResultPair;
+
+import java.io.OutputStream;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+public class HttpMessageCodecManager {
+    private static final Set<HttpMessageCodec> httpMessageCodecs = FrameworkModel.defaultModel()
+            .getExtensionLoader(HttpMessageCodec.class)
+            .getSupportedExtensionInstances();
+
+    public static Object httpMessageDecode(byte[] body, Class<?> type, Type actualType, MediaType mediaType)
+            throws Exception {
+        if (body == null || body.length == 0) {
+            return null;
+        }
+
+        for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) {
+            if (httpMessageCodec.contentTypeSupport(mediaType, type) || typeJudge(mediaType, type, httpMessageCodec)) {
+                return httpMessageCodec.decode(body, type, actualType);
+            }
+        }
+        throw new UnSupportContentTypeException("UnSupport content-type :" + mediaType.value);
+    }
+
+    public static MessageCodecResultPair httpMessageEncode(
+        OutputStream outputStream, Object unSerializedBody, URL url, MediaType mediaType, Class<?> bodyType)
+            throws Exception {
+
+        if (unSerializedBody == null) {
+            for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) {
+                if (httpMessageCodec.contentTypeSupport(mediaType, bodyType)
+                        || typeJudge(mediaType, bodyType, httpMessageCodec)) {
+                    return MessageCodecResultPair.pair(false, httpMessageCodec.contentType());
+                }
+            }
+        }
+
+        for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) {
+            if (httpMessageCodec.contentTypeSupport(mediaType, bodyType)
+                    || typeJudge(mediaType, bodyType, httpMessageCodec)) {
+                httpMessageCodec.encode(outputStream, unSerializedBody, url);
+                return MessageCodecResultPair.pair(true, httpMessageCodec.contentType());
+            }
+        }
+
+        throw new UnSupportContentTypeException("UnSupport content-type :" + mediaType.value);
+    }
+
+    /**
+     * if content-type is null or  all ,will judge media type by class type
+     *
+     * @param mediaType
+     * @param bodyType
+     * @param httpMessageCodec
+     * @return
+     */
+    private static boolean typeJudge(MediaType mediaType, Class<?> bodyType, HttpMessageCodec httpMessageCodec) {
+        return (MediaType.ALL_VALUE.equals(mediaType) || mediaType == null)
+                && bodyType != null
+                && httpMessageCodec.typeSupport(bodyType);
+    }
+
+    public static MediaType typeSupport(Class<?> type) {
+        for (HttpMessageCodec httpMessageCodec : httpMessageCodecs) {
+
+            if (httpMessageCodec.typeSupport(type)) {
+                return httpMessageCodec.contentType();
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java
new file mode 100644
index 0000000..8b32281
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageDecode.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message;
+
+import java.lang.reflect.Type;
+
+public interface HttpMessageDecode<InputStream> {
+
+    Object decode(InputStream body, Class<?> targetType, Type actualTYpe) throws Exception;
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageEncode.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageEncode.java
new file mode 100644
index 0000000..8ae427e
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/HttpMessageEncode.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message;
+
+import org.apache.dubbo.common.URL;
+
+public interface HttpMessageEncode<OutputStream> {
+
+    void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception;
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java
new file mode 100644
index 0000000..e9a2b24
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/MediaTypeMatcher.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum MediaTypeMatcher {
+    MULTI_VALUE(createMediaList(MediaType.APPLICATION_FORM_URLENCODED_VALUE)),
+    APPLICATION_JSON(createMediaList(MediaType.APPLICATION_JSON_VALUE)),
+    TEXT_PLAIN(createMediaList(MediaType.TEXT_PLAIN, MediaType.OCTET_STREAM)),
+    TEXT_XML(createMediaList(MediaType.TEXT_XML)),
+    ;
+
+    private List<MediaType> mediaTypes;
+
+    MediaTypeMatcher(List<MediaType> mediaTypes) {
+        this.mediaTypes = mediaTypes;
+    }
+
+    private static List<MediaType> createMediaList(MediaType... mediaTypes) {
+        List<MediaType> mediaTypeList = getDefaultList();
+
+        for (MediaType mediaType : mediaTypes) {
+
+            mediaTypeList.add(mediaType);
+        }
+        return mediaTypeList;
+    }
+
+    private static List<MediaType> getDefaultList() {
+
+        List<MediaType> defaultList = new ArrayList<>();
+        return defaultList;
+    }
+
+    public boolean mediaSupport(MediaType mediaType) {
+        return mediaTypes.contains(mediaType);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java
new file mode 100644
index 0000000..5032392
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ByteArrayCodec.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message.codec;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec;
+
+import java.io.OutputStream;
+import java.lang.reflect.Type;
+
+/**
+ *  body type is byte array
+ */
+@Activate("byteArray")
+public class ByteArrayCodec implements HttpMessageCodec<byte[], OutputStream> {
+
+    @Override
+    public Object decode(byte[] body, Class<?> targetType, Type type) throws Exception {
+        return body;
+    }
+
+    @Override
+    public boolean contentTypeSupport(MediaType mediaType, Class<?> targetType) {
+        return byte[].class.equals(targetType);
+    }
+
+    @Override
+    public boolean typeSupport(Class<?> targetType) {
+        return byte[].class.equals(targetType);
+    }
+
+    @Override
+    public MediaType contentType() {
+        return MediaType.OCTET_STREAM;
+    }
+
+    @Override
+    public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception {
+        outputStream.write((byte[]) unSerializedBody);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java
new file mode 100644
index 0000000..fa867d9
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/JsonCodec.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message.codec;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.utils.JsonUtils;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec;
+import org.apache.dubbo.rpc.protocol.rest.message.MediaTypeMatcher;
+import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
+
+import java.io.OutputStream;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * body is json
+ */
+@Activate(value = "json", order = 100)
+public class JsonCodec implements HttpMessageCodec<byte[], OutputStream> {
+    private static final Set<Class> unSupportClasses = new HashSet<>();
+
+    static {
+        unSupportClasses.add(byte[].class);
+    }
+
+    public static void addUnSupportClass(Class<?> unSupportClass) {
+        unSupportClasses.add(unSupportClass);
+    }
+
+    @Override
+    public Object decode(byte[] body, Class<?> targetType, Type actualType) throws Exception {
+        return DataParseUtils.jsonConvert(actualType, body);
+    }
+
+    @Override
+    public boolean contentTypeSupport(MediaType mediaType, Class<?> targetType) {
+        return MediaTypeMatcher.APPLICATION_JSON.mediaSupport(mediaType) && !unSupportClasses.contains(targetType);
+    }
+
+    @Override
+    public boolean typeSupport(Class<?> targetType) {
+        return !unSupportClasses.contains(targetType) && !DataParseUtils.isTextType(targetType);
+    }
+
+    @Override
+    public MediaType contentType() {
+        return MediaType.APPLICATION_JSON_VALUE;
+    }
+
+    @Override
+    public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception {
+        outputStream.write(JsonUtils.toJson(unSerializedBody).getBytes(StandardCharsets.UTF_8));
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java
new file mode 100644
index 0000000..7255147
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/MultiValueCodec.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message.codec;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.utils.ReflectUtils;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec;
+import org.apache.dubbo.rpc.protocol.rest.message.MediaTypeMatcher;
+import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
+
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *  body is form
+ */
+@Activate("multiValue")
+public class MultiValueCodec implements HttpMessageCodec<byte[], OutputStream> {
+
+    @Override
+    public Object decode(byte[] body, Class<?> targetType, Type type) throws Exception {
+        Object map = DataParseUtils.multipartFormConvert(body, targetType);
+        Map valuesMap = (Map) map;
+        if (Map.class.isAssignableFrom(targetType)) {
+            return map;
+        } else if (DataParseUtils.isTextType(targetType)) {
+
+            // only fetch  first
+            Set set = valuesMap.keySet();
+            ArrayList arrayList = new ArrayList<>(set);
+            Object key = arrayList.get(0);
+            Object value = valuesMap.get(key);
+            if (value == null) {
+                return null;
+            }
+            return DataParseUtils.stringTypeConvert(targetType, String.valueOf(((List) value).get(0)));
+
+        } else {
+
+            Map<String, Field> beanPropertyFields = ReflectUtils.getBeanPropertyFields(targetType);
+
+            Object emptyObject = ReflectUtils.getEmptyObject(targetType);
+
+            beanPropertyFields.entrySet().stream().forEach(entry -> {
+                try {
+                    List values = (List) valuesMap.get(entry.getKey());
+                    String value = values == null ? null : String.valueOf(values.get(0));
+                    entry.getValue()
+                            .set(
+                                    emptyObject,
+                                    DataParseUtils.stringTypeConvert(
+                                            entry.getValue().getType(), value));
+                } catch (IllegalAccessException e) {
+
+                }
+            });
+
+            return emptyObject;
+        }
+    }
+
+    @Override
+    public boolean contentTypeSupport(MediaType mediaType, Class<?> targetType) {
+        return MediaTypeMatcher.MULTI_VALUE.mediaSupport(mediaType);
+    }
+
+    @Override
+    public boolean typeSupport(Class<?> targetType) {
+        return false;
+    }
+
+    @Override
+    public MediaType contentType() {
+        return MediaType.APPLICATION_FORM_URLENCODED_VALUE;
+    }
+
+    @Override
+    public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception {
+        DataParseUtils.writeFormContent((Map) unSerializedBody, outputStream);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ResteasyResponseCodec.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ResteasyResponseCodec.java
new file mode 100644
index 0000000..e14aa83
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/ResteasyResponseCodec.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message.codec;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.utils.ClassUtils;
+import org.apache.dubbo.common.utils.JsonUtils;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec;
+
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+
+@Activate(onClass = "javax.ws.rs.core.Response")
+public class ResteasyResponseCodec implements HttpMessageCodec<byte[], OutputStream> {
+
+    private Class<?> responseClass;
+
+    public ResteasyResponseCodec() {
+        try {
+            responseClass = ClassUtils.forName("javax.ws.rs.core.Response");
+            JsonCodec.addUnSupportClass(responseClass);
+        } catch (Exception exception) {
+            responseClass = null;
+        }
+    }
+
+    @Override
+    public boolean contentTypeSupport(MediaType mediaType, Class<?> targetType) {
+        return isMatch(targetType);
+    }
+
+    @Override
+    public boolean typeSupport(Class<?> targetType) {
+        return isMatch(targetType);
+    }
+
+    @Override
+    public MediaType contentType() {
+        return MediaType.APPLICATION_JSON_VALUE;
+    }
+
+    @Override
+    public Object decode(byte[] body, Class<?> targetType, Type type) throws Exception {
+        if (null == body || body.length == 0) {
+            return null;
+        }
+
+        Class<?> builtResponse = ClassUtils.forName("org.jboss.resteasy.specimpl.BuiltResponse");
+
+        Object o = builtResponse.newInstance();
+
+        Method method = builtResponse.getMethod("setEntity", Object.class);
+
+        method.invoke(o, new String(body, StandardCharsets.UTF_8));
+
+        return o;
+    }
+
+    @Override
+    public void encode(OutputStream os, Object target, URL url) throws Exception {
+        if (target != null) {
+            Method method = target.getClass().getMethod("getEntity");
+            method.setAccessible(true);
+            Object result = method.invoke(target);
+            os.write(JsonUtils.toJson(result).getBytes(StandardCharsets.UTF_8));
+        }
+    }
+
+    private boolean isMatch(Class<?> targetType) {
+        return responseClass != null && null != targetType && responseClass.isAssignableFrom(targetType);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.java
new file mode 100644
index 0000000..5c8396c
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/StringCodec.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.dubbo.rpc.protocol.rest.message.codec;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec;
+
+import java.io.OutputStream;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+
+/**
+ *  body is string
+ */
+@Activate(value = "string", order = 200)
+public class StringCodec implements HttpMessageCodec<byte[], OutputStream> {
+
+    @Override
+    public Object decode(byte[] body, Class<?> targetType, Type type) throws Exception {
+        if (body == null || body.length == 0) {
+            return null;
+        }
+        return new String(body);
+    }
+
+    @Override
+    public boolean contentTypeSupport(MediaType mediaType, Class<?> targetType) {
+        return String.class.equals(targetType);
+    }
+
+    @Override
+    public boolean typeSupport(Class<?> targetType) {
+        return String.class.equals(targetType);
+    }
+
+    @Override
+    public MediaType contentType() {
+        return MediaType.TEXT_PLAIN;
+    }
+
+    @Override
+    public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception {
+        outputStream.write(((String) unSerializedBody).getBytes(StandardCharsets.UTF_8));
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java
new file mode 100644
index 0000000..08e9de0
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/TextCodec.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message.codec;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec;
+import org.apache.dubbo.rpc.protocol.rest.message.MediaTypeMatcher;
+import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
+
+import java.io.OutputStream;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+
+/**
+ *  content-type is text/html
+ */
+@Activate("text")
+public class TextCodec implements HttpMessageCodec<byte[], OutputStream> {
+
+    @Override
+    public Object decode(byte[] body, Class<?> targetType, Type type) throws Exception {
+        return DataParseUtils.stringTypeConvert(targetType, new String(body, StandardCharsets.UTF_8));
+    }
+
+    @Override
+    public boolean contentTypeSupport(MediaType mediaType, Class<?> targetType) {
+        return MediaTypeMatcher.TEXT_PLAIN.mediaSupport(mediaType);
+    }
+
+    @Override
+    public boolean typeSupport(Class<?> targetType) {
+        return DataParseUtils.isTextType(targetType);
+    }
+
+    @Override
+    public MediaType contentType() {
+        return MediaType.TEXT_PLAIN;
+    }
+
+    @Override
+    public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception {
+        DataParseUtils.writeTextContent(unSerializedBody, outputStream);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java
new file mode 100644
index 0000000..895fa4b
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/message/codec/XMLCodec.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.message.codec;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec;
+import org.apache.dubbo.rpc.protocol.rest.message.MediaTypeMatcher;
+import org.xml.sax.InputSource;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.sax.SAXSource;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.lang.reflect.Type;
+
+/**
+ *  body content-type is xml
+ */
+@Activate("xml")
+public class XMLCodec implements HttpMessageCodec<byte[], OutputStream> {
+
+    @Override
+    public Object decode(byte[] body, Class<?> targetType, Type type) throws Exception {
+
+        SAXParserFactory spf = SAXParserFactory.newInstance();
+        spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
+        spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+        spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+
+        // Do unmarshall operation
+        Source xmlSource =
+                new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(new StringReader(new String(body))));
+
+        JAXBContext context = JAXBContext.newInstance(targetType);
+        Unmarshaller unmarshaller = context.createUnmarshaller();
+        return unmarshaller.unmarshal(xmlSource);
+    }
+
+    @Override
+    public boolean contentTypeSupport(MediaType mediaType, Class<?> targetType) {
+        return MediaTypeMatcher.TEXT_XML.mediaSupport(mediaType);
+    }
+
+    @Override
+    public boolean typeSupport(Class<?> targetType) {
+        return false;
+    }
+
+    @Override
+    public MediaType contentType() {
+        return MediaType.TEXT_XML;
+    }
+
+    @Override
+    public void encode(OutputStream outputStream, Object unSerializedBody, URL url) throws Exception {
+        Marshaller marshaller =
+                JAXBContext.newInstance(unSerializedBody.getClass()).createMarshaller();
+        marshaller.marshal(unSerializedBody, outputStream);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ChunkOutputStream.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ChunkOutputStream.java
new file mode 100644
index 0000000..ff6cb68
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ChunkOutputStream.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.netty;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.DefaultHttpContent;
+import org.apache.dubbo.remoting.transport.ExceedPayloadLimitException;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class ChunkOutputStream extends OutputStream {
+    final ByteBuf buffer;
+    final ChannelHandlerContext ctx;
+    final NettyHttpResponse response;
+    int chunkSize = 0;
+
+    ChunkOutputStream(final NettyHttpResponse response, final ChannelHandlerContext ctx, final int chunkSize) {
+        this.response = response;
+        if (chunkSize < 1) {
+            throw new IllegalArgumentException();
+        }
+        this.buffer = Unpooled.buffer(0, chunkSize);
+        this.chunkSize = chunkSize;
+        this.ctx = ctx;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        if (buffer.maxWritableBytes() < 1) {
+            throwExceedPayloadLimitException(buffer.readableBytes() + 1);
+        }
+        buffer.writeByte(b);
+    }
+
+    private void throwExceedPayloadLimitException(int dataSize) throws ExceedPayloadLimitException {
+        throw new ExceedPayloadLimitException("Data length too large: " + dataSize + ", max payload: " + chunkSize);
+    }
+
+    public void reset() {
+        if (response.isCommitted()) throw new IllegalStateException();
+        buffer.clear();
+    }
+
+    @Override
+    public void close() throws IOException {
+        flush();
+        super.close();
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        int dataLengthLeftToWrite = len;
+        int dataToWriteOffset = off;
+        if (buffer.maxWritableBytes() < dataLengthLeftToWrite) {
+            throwExceedPayloadLimitException(buffer.readableBytes() + len);
+        }
+        buffer.writeBytes(b, dataToWriteOffset, dataLengthLeftToWrite);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        int readable = buffer.readableBytes();
+        if (readable == 0) return;
+        if (!response.isCommitted()) response.prepareChunkStream();
+        ctx.writeAndFlush(new DefaultHttpContent(buffer.copy()));
+        buffer.clear();
+        super.flush();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/HttpResponse.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/HttpResponse.java
new file mode 100644
index 0000000..888547b
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/HttpResponse.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.netty;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+public interface HttpResponse {
+    int getStatus();
+
+    void setStatus(int status);
+
+    Map<String, List<String>> getOutputHeaders();
+
+    OutputStream getOutputStream() throws IOException;
+
+    void setOutputStream(OutputStream os);
+
+    void sendError(int status) throws IOException;
+
+    void sendError(int status, String message) throws IOException;
+
+    boolean isCommitted();
+
+    /**
+     * reset status and headers.  Will fail if response is committed
+     */
+    void reset();
+
+    void flushBuffer() throws IOException;
+
+    void addOutputHeaders(String name, String value);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyHttpResponse.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyHttpResponse.java
new file mode 100644
index 0000000..a700cd1
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyHttpResponse.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.netty;
+
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.DefaultHttpResponse;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpHeaders.Names;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.LastHttpContent;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
+
+/**
+ * netty http response
+ */
+public class NettyHttpResponse implements HttpResponse {
+    private static final int EMPTY_CONTENT_LENGTH = 0;
+    private int status = 200;
+    private OutputStream os;
+    private Map<String, List<String>> outputHeaders;
+    private final ChannelHandlerContext ctx;
+    private boolean committed;
+    private boolean keepAlive;
+    private HttpMethod method;
+    // raw response body
+    private Object responseBody;
+    // raw response class
+    private Class<?> entityClass;
+
+    public NettyHttpResponse(final ChannelHandlerContext ctx, final boolean keepAlive, URL url) {
+        this(ctx, keepAlive, null, url);
+    }
+
+    public NettyHttpResponse(final ChannelHandlerContext ctx, final boolean keepAlive, HttpMethod method, URL url) {
+        outputHeaders = new HashMap<>();
+        this.method = method;
+        os = new ChunkOutputStream(this, ctx, url.getParameter(Constants.PAYLOAD_KEY, Constants.DEFAULT_PAYLOAD));
+        this.ctx = ctx;
+        this.keepAlive = keepAlive;
+    }
+
+    public void setOutputStream(OutputStream os) {
+        this.os = os;
+    }
+
+    @Override
+    public int getStatus() {
+        return status;
+    }
+
+    @Override
+    public void setStatus(int status) {
+        if (status > 200) {
+            addOutputHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), MediaType.TEXT_PLAIN.value);
+        }
+        this.status = status;
+    }
+
+    @Override
+    public Map<String, List<String>> getOutputHeaders() {
+        return outputHeaders;
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        return os;
+    }
+
+    @Override
+    public void sendError(int status) throws IOException {
+        sendError(status, null);
+    }
+
+    @Override
+    public void sendError(int status, String message) throws IOException {
+        setStatus(status);
+        setResponseBody(message);
+        if (message != null) {
+            getOutputStream().write(message.getBytes(StandardCharsets.UTF_8));
+        }
+    }
+
+    @Override
+    public boolean isCommitted() {
+        return committed;
+    }
+
+    @Override
+    public void reset() {
+        if (committed) {
+            throw new IllegalStateException("Messages.MESSAGES.alreadyCommitted()");
+        }
+        outputHeaders.clear();
+        outputHeaders.clear();
+    }
+
+    public boolean isKeepAlive() {
+        return keepAlive;
+    }
+
+    public DefaultHttpResponse getDefaultHttpResponse() {
+        DefaultHttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(getStatus()));
+        transformResponseHeaders(res);
+        return res;
+    }
+
+    public DefaultHttpResponse getEmptyHttpResponse() {
+        DefaultFullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(getStatus()));
+        if (method == null || !method.equals(HttpMethod.HEAD)) {
+            res.headers().add(Names.CONTENT_LENGTH, EMPTY_CONTENT_LENGTH);
+        }
+        transformResponseHeaders(res);
+
+        return res;
+    }
+
+    private void transformResponseHeaders(io.netty.handler.codec.http.HttpResponse res) {
+        transformHeaders(this, res);
+    }
+
+    public void prepareChunkStream() {
+        committed = true;
+        DefaultHttpResponse response = getDefaultHttpResponse();
+        HttpHeaders.setTransferEncodingChunked(response);
+        ctx.write(response);
+    }
+
+    public void finish() throws IOException {
+        if (os != null) os.flush();
+        ChannelFuture future;
+        if (isCommitted()) {
+            // if committed this means the output stream was used.
+            future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
+        } else {
+            future = ctx.writeAndFlush(getEmptyHttpResponse());
+        }
+
+        if (!isKeepAlive()) {
+            future.addListener(ChannelFutureListener.CLOSE);
+        }
+
+        getOutputStream().close();
+    }
+
+    @Override
+    public void flushBuffer() throws IOException {
+        if (os != null) os.flush();
+        ctx.flush();
+    }
+
+    @Override
+    public void addOutputHeaders(String name, String value) {
+
+        List<String> values = outputHeaders.get(name);
+
+        if (values == null) {
+            values = new ArrayList<>();
+            outputHeaders.put(name, values);
+        }
+
+        if (values.contains(value)) {
+            return;
+        }
+
+        values.add(value);
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public static void transformHeaders(
+            NettyHttpResponse nettyResponse, io.netty.handler.codec.http.HttpResponse response) {
+        //        if (nettyResponse.isKeepAlive()) {
+        //            response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
+        //        } else {
+        //            response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
+        //        }
+
+        for (Map.Entry<String, List<String>> entry :
+                nettyResponse.getOutputHeaders().entrySet()) {
+            String key = entry.getKey();
+            for (String value : entry.getValue()) {
+                response.headers().set(key, value);
+            }
+        }
+    }
+
+    public Object getResponseBody() {
+        return responseBody;
+    }
+
+    public void setResponseBody(Object responseBody) {
+
+        this.responseBody = responseBody;
+
+        if (responseBody != null) {
+            this.entityClass = responseBody.getClass();
+        }
+    }
+
+    public Class<?> getEntityClass() {
+        return entityClass;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyServer.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyServer.java
new file mode 100644
index 0000000..42264e9
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/NettyServer.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.netty;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.timeout.IdleStateHandler;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.NamedThreadFactory;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.remoting.Constants.EVENT_LOOP_BOSS_POOL_NAME;
+import static org.apache.dubbo.remoting.Constants.EVENT_LOOP_WORKER_POOL_NAME;
+
+public class NettyServer {
+
+    protected ServerBootstrap bootstrap = new ServerBootstrap();
+    protected String hostname = null;
+    protected int configuredPort = 8080;
+    protected int runtimePort = -1;
+
+    private EventLoopGroup eventLoopGroup;
+    private EventLoopGroup workerLoopGroup;
+    private int ioWorkerCount = Runtime.getRuntime().availableProcessors() * 2;
+
+    private List<ChannelHandler> channelHandlers = Collections.emptyList();
+    private Map<ChannelOption, Object> channelOptions = Collections.emptyMap();
+    private Map<ChannelOption, Object> childChannelOptions = Collections.emptyMap();
+    private UnSharedHandlerCreator unSharedHandlerCallBack;
+
+    public NettyServer() {}
+
+    /**
+     * Specify the worker count to use. For more information about this please see the javadocs of {@link EventLoopGroup}
+     *
+     * @param ioWorkerCount worker count
+     */
+    public void setIoWorkerCount(int ioWorkerCount) {
+        this.ioWorkerCount = ioWorkerCount;
+    }
+
+    public String getHostname() {
+        return hostname;
+    }
+
+    public void setHostname(String hostname) {
+        this.hostname = hostname;
+    }
+
+    public int getPort() {
+        return runtimePort > 0 ? runtimePort : configuredPort;
+    }
+
+    public void setPort(int port) {
+        this.configuredPort = port;
+    }
+
+    /**
+     * Add additional {@link ChannelHandler}s to the {@link ServerBootstrap}.
+     * <p>The additional channel handlers are being added <em>before</em> the HTTP handling.</p>
+     *
+     * @param channelHandlers the additional {@link ChannelHandler}s.
+     */
+    public void setChannelHandlers(final List<ChannelHandler> channelHandlers) {
+        this.channelHandlers = channelHandlers == null ? Collections.<ChannelHandler>emptyList() : channelHandlers;
+    }
+
+    /**
+     * Add Netty {@link ChannelOption}s to the {@link ServerBootstrap}.
+     *
+     * @param channelOptions the additional {@link ChannelOption}s.
+     * @see ServerBootstrap#option(ChannelOption, Object)
+     */
+    public void setChannelOptions(final Map<ChannelOption, Object> channelOptions) {
+        this.channelOptions = channelOptions == null ? Collections.<ChannelOption, Object>emptyMap() : channelOptions;
+    }
+
+    /**
+     * Add child options to the {@link ServerBootstrap}.
+     *
+     * @param channelOptions the additional child {@link ChannelOption}s.
+     * @see ServerBootstrap#childOption(ChannelOption, Object)
+     */
+    public void setChildChannelOptions(final Map<ChannelOption, Object> channelOptions) {
+        this.childChannelOptions =
+                channelOptions == null ? Collections.<ChannelOption, Object>emptyMap() : channelOptions;
+    }
+
+    public void setUnSharedHandlerCallBack(UnSharedHandlerCreator unSharedHandlerCallBack) {
+        this.unSharedHandlerCallBack = unSharedHandlerCallBack;
+    }
+
+    public void start(URL url) {
+        eventLoopGroup = new NioEventLoopGroup(1, new NamedThreadFactory(EVENT_LOOP_BOSS_POOL_NAME));
+        workerLoopGroup = new NioEventLoopGroup(ioWorkerCount, new NamedThreadFactory(EVENT_LOOP_WORKER_POOL_NAME));
+
+        // Configure the server.
+        bootstrap
+                .group(eventLoopGroup, workerLoopGroup)
+                .channel(NioServerSocketChannel.class)
+                .childHandler(setupHandlers(url));
+
+        for (Map.Entry<ChannelOption, Object> entry : channelOptions.entrySet()) {
+            bootstrap.option(entry.getKey(), entry.getValue());
+        }
+
+        for (Map.Entry<ChannelOption, Object> entry : childChannelOptions.entrySet()) {
+            bootstrap.childOption(entry.getKey(), entry.getValue());
+        }
+
+        final InetSocketAddress socketAddress;
+        if (null == getHostname() || getHostname().isEmpty()) {
+            socketAddress = new InetSocketAddress(configuredPort);
+        } else {
+            socketAddress = new InetSocketAddress(hostname, configuredPort);
+        }
+
+        Channel channel = bootstrap.bind(socketAddress).syncUninterruptibly().channel();
+        runtimePort = ((InetSocketAddress) channel.localAddress()).getPort();
+    }
+
+    protected ChannelHandler setupHandlers(URL url) {
+
+        return new ChannelInitializer<SocketChannel>() {
+            @Override
+            public void initChannel(SocketChannel ch) throws Exception {
+                ChannelPipeline channelPipeline = ch.pipeline();
+
+                int idleTimeout = url.getParameter(RestConstant.IDLE_TIMEOUT_PARAM, RestConstant.IDLE_TIMEOUT);
+                if (idleTimeout > 0) {
+                    channelPipeline.addLast(new IdleStateHandler(0, 0, idleTimeout));
+                }
+
+                channelPipeline.addLast(channelHandlers.toArray(new ChannelHandler[channelHandlers.size()]));
+
+                List<ChannelHandler> unSharedHandlers = unSharedHandlerCallBack.getUnSharedHandlers(url);
+
+                for (ChannelHandler unSharedHandler : unSharedHandlers) {
+                    channelPipeline.addLast(unSharedHandler);
+                }
+            }
+        };
+    }
+
+    public void stop() {
+        runtimePort = -1;
+        eventLoopGroup.shutdownGracefully();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/RestHttpRequestDecoder.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/RestHttpRequestDecoder.java
new file mode 100644
index 0000000..37aba82
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/RestHttpRequestDecoder.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.netty;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToMessageDecoder;
+import io.netty.handler.codec.http.HttpHeaders;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
+import org.apache.dubbo.common.utils.ExecutorUtil;
+import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.handler.NettyHttpHandler;
+import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+import static org.apache.dubbo.config.Constants.SERVER_THREAD_POOL_NAME;
+
+public class RestHttpRequestDecoder extends MessageToMessageDecoder<io.netty.handler.codec.http.FullHttpRequest> {
+    private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass());
+
+    private final Executor executor;
+    private final ServiceDeployer serviceDeployer;
+    private final URL url;
+    private final NettyHttpHandler nettyHttpHandler;
+
+    public RestHttpRequestDecoder(URL url, ServiceDeployer serviceDeployer) {
+
+        this.url = url;
+        this.serviceDeployer = serviceDeployer;
+        executor = ExecutorRepository.getInstance(url.getOrDefaultApplicationModel())
+                .createExecutorIfAbsent(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME));
+        nettyHttpHandler = new NettyHttpHandler(serviceDeployer, url);
+    }
+
+    @Override
+    protected void decode(
+            ChannelHandlerContext ctx, io.netty.handler.codec.http.FullHttpRequest request, List<Object> out)
+            throws Exception {
+        boolean keepAlive = HttpHeaders.isKeepAlive(request);
+
+        NettyHttpResponse nettyHttpResponse = new NettyHttpResponse(ctx, keepAlive, url);
+        NettyRequestFacade requestFacade = new NettyRequestFacade(request, ctx, serviceDeployer);
+
+        executor.execute(() -> {
+
+            // business handler
+            try {
+                nettyHttpHandler.handle(requestFacade, nettyHttpResponse);
+
+            } catch (IOException e) {
+                logger.error(
+                        "", e.getCause().getMessage(), "dubbo rest rest http request handler error", e.getMessage(), e);
+            } finally {
+                // write response
+                try {
+                    nettyHttpResponse.addOutputHeaders(RestHeaderEnum.CONNECTION.getHeader(), "close");
+                    nettyHttpResponse.finish();
+                } catch (IOException e) {
+                    logger.error(
+                            "",
+                            e.getCause().getMessage(),
+                            "dubbo rest rest http response flush error",
+                            e.getMessage(),
+                            e);
+                }
+            }
+        });
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/UnSharedHandlerCreator.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/UnSharedHandlerCreator.java
new file mode 100644
index 0000000..24fce27
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/UnSharedHandlerCreator.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.netty;
+
+import io.netty.channel.ChannelHandler;
+import org.apache.dubbo.common.URL;
+
+import java.util.List;
+
+/**
+ *  FOR create netty un shared (no @Shared) handler
+ */
+public interface UnSharedHandlerCreator {
+
+    List<ChannelHandler> getUnSharedHandlers(URL url);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ssl/SslContexts.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ssl/SslContexts.java
new file mode 100644
index 0000000..f36872d
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ssl/SslContexts.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.netty.ssl;
+
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.OpenSsl;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.SslProvider;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.ssl.AuthPolicy;
+import org.apache.dubbo.common.ssl.Cert;
+import org.apache.dubbo.common.ssl.CertManager;
+import org.apache.dubbo.common.ssl.ProviderCert;
+
+import javax.net.ssl.SSLException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Provider;
+import java.security.Security;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.TRANSPORT_FAILED_CLOSE_STREAM;
+
+public class SslContexts {
+
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SslContexts.class);
+
+    public static SslContext buildServerSslContext(ProviderCert providerConnectionConfig) {
+        SslContextBuilder sslClientContextBuilder;
+        InputStream serverKeyCertChainPathStream = null;
+        InputStream serverPrivateKeyPathStream = null;
+        InputStream serverTrustCertStream = null;
+        try {
+            serverKeyCertChainPathStream = providerConnectionConfig.getKeyCertChainInputStream();
+            serverPrivateKeyPathStream = providerConnectionConfig.getPrivateKeyInputStream();
+            serverTrustCertStream = providerConnectionConfig.getTrustCertInputStream();
+            String password = providerConnectionConfig.getPassword();
+            if (password != null) {
+                sslClientContextBuilder =
+                        SslContextBuilder.forServer(serverKeyCertChainPathStream, serverPrivateKeyPathStream, password);
+            } else {
+                sslClientContextBuilder =
+                        SslContextBuilder.forServer(serverKeyCertChainPathStream, serverPrivateKeyPathStream);
+            }
+
+            if (serverTrustCertStream != null) {
+                sslClientContextBuilder.trustManager(serverTrustCertStream);
+                if (providerConnectionConfig.getAuthPolicy() == AuthPolicy.CLIENT_AUTH) {
+                    sslClientContextBuilder.clientAuth(ClientAuth.REQUIRE);
+                } else {
+                    sslClientContextBuilder.clientAuth(ClientAuth.OPTIONAL);
+                }
+            }
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Could not find certificate file or the certificate is invalid.", e);
+        } finally {
+            safeCloseStream(serverTrustCertStream);
+            safeCloseStream(serverKeyCertChainPathStream);
+            safeCloseStream(serverPrivateKeyPathStream);
+        }
+        try {
+            return sslClientContextBuilder.sslProvider(findSslProvider()).build();
+        } catch (SSLException e) {
+            throw new IllegalStateException("Build SslSession failed.", e);
+        }
+    }
+
+    public static SslContext buildClientSslContext(URL url) {
+        CertManager certManager =
+                url.getOrDefaultFrameworkModel().getBeanFactory().getBean(CertManager.class);
+        Cert consumerConnectionConfig = certManager.getConsumerConnectionConfig(url);
+        if (consumerConnectionConfig == null) {
+            return null;
+        }
+
+        SslContextBuilder builder = SslContextBuilder.forClient();
+        InputStream clientTrustCertCollectionPath = null;
+        InputStream clientCertChainFilePath = null;
+        InputStream clientPrivateKeyFilePath = null;
+        try {
+            clientTrustCertCollectionPath = consumerConnectionConfig.getTrustCertInputStream();
+            if (clientTrustCertCollectionPath != null) {
+                builder.trustManager(clientTrustCertCollectionPath);
+            }
+
+            clientCertChainFilePath = consumerConnectionConfig.getKeyCertChainInputStream();
+            clientPrivateKeyFilePath = consumerConnectionConfig.getPrivateKeyInputStream();
+            if (clientCertChainFilePath != null && clientPrivateKeyFilePath != null) {
+                String password = consumerConnectionConfig.getPassword();
+                if (password != null) {
+                    builder.keyManager(clientCertChainFilePath, clientPrivateKeyFilePath, password);
+                } else {
+                    builder.keyManager(clientCertChainFilePath, clientPrivateKeyFilePath);
+                }
+            }
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Could not find certificate file or find invalid certificate.", e);
+        } finally {
+            safeCloseStream(clientTrustCertCollectionPath);
+            safeCloseStream(clientCertChainFilePath);
+            safeCloseStream(clientPrivateKeyFilePath);
+        }
+        try {
+            return builder.sslProvider(findSslProvider()).build();
+        } catch (SSLException e) {
+            throw new IllegalStateException("Build SslSession failed.", e);
+        }
+    }
+
+    /**
+     * Returns OpenSSL if available, otherwise returns the JDK provider.
+     */
+    private static SslProvider findSslProvider() {
+        if (OpenSsl.isAvailable()) {
+            logger.debug("Using OPENSSL provider.");
+            return SslProvider.OPENSSL;
+        }
+        if (checkJdkProvider()) {
+            logger.debug("Using JDK provider.");
+            return SslProvider.JDK;
+        }
+        throw new IllegalStateException(
+                "Could not find any valid TLS provider, please check your dependency or deployment environment, "
+                        + "usually netty-tcnative, Conscrypt, or Jetty NPN/ALPN is needed.");
+    }
+
+    private static boolean checkJdkProvider() {
+        Provider[] jdkProviders = Security.getProviders("SSLContext.TLS");
+        return (jdkProviders != null && jdkProviders.length > 0);
+    }
+
+    private static void safeCloseStream(InputStream stream) {
+        if (stream == null) {
+            return;
+        }
+        try {
+            stream.close();
+        } catch (IOException e) {
+            logger.warn(TRANSPORT_FAILED_CLOSE_STREAM, "", "", "Failed to close a stream.", e);
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ssl/SslServerTlsHandler.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ssl/SslServerTlsHandler.java
new file mode 100644
index 0000000..0539318
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/netty/ssl/SslServerTlsHandler.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.netty.ssl;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.handler.ssl.SslHandshakeCompletionEvent;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.ssl.AuthPolicy;
+import org.apache.dubbo.common.ssl.CertManager;
+import org.apache.dubbo.common.ssl.ProviderCert;
+
+import javax.net.ssl.SSLSession;
+import java.util.List;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR;
+
+public class SslServerTlsHandler extends ByteToMessageDecoder {
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(SslServerTlsHandler.class);
+
+    private final URL url;
+
+    private final boolean sslDetected;
+
+    public SslServerTlsHandler(URL url) {
+        this.url = url;
+        this.sslDetected = false;
+    }
+
+    public SslServerTlsHandler(URL url, boolean sslDetected) {
+        this.url = url;
+        this.sslDetected = sslDetected;
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        logger.error(
+                INTERNAL_ERROR,
+                "unknown error in remoting module",
+                "",
+                "TLS negotiation failed when trying to accept new connection.",
+                cause);
+    }
+
+    @Override
+    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+        if (evt instanceof SslHandshakeCompletionEvent) {
+            SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
+            if (handshakeEvent.isSuccess()) {
+                SSLSession session =
+                        ctx.pipeline().get(SslHandler.class).engine().getSession();
+                logger.info("TLS negotiation succeed with: " + session.getPeerHost());
+                // Remove after handshake success.
+                ctx.pipeline().remove(this);
+            } else {
+                logger.error(
+                        INTERNAL_ERROR,
+                        "",
+                        "",
+                        "TLS negotiation failed when trying to accept new connection.",
+                        handshakeEvent.cause());
+                ctx.close();
+            }
+        }
+        super.userEventTriggered(ctx, evt);
+    }
+
+    @Override
+    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list)
+            throws Exception {
+        // Will use the first five bytes to detect a protocol.
+        if (byteBuf.readableBytes() < 5) {
+            return;
+        }
+
+        if (sslDetected) {
+            return;
+        }
+
+        CertManager certManager =
+                url.getOrDefaultFrameworkModel().getBeanFactory().getBean(CertManager.class);
+        ProviderCert providerConnectionConfig = certManager.getProviderConnectionConfig(
+                url, channelHandlerContext.channel().remoteAddress());
+
+        if (providerConnectionConfig == null) {
+            channelHandlerContext.pipeline().remove(this);
+            return;
+        }
+
+        if (isSsl(byteBuf)) {
+            SslContext sslContext = SslContexts.buildServerSslContext(providerConnectionConfig);
+            enableSsl(channelHandlerContext, sslContext);
+            return;
+        }
+
+        if (providerConnectionConfig.getAuthPolicy() == AuthPolicy.NONE) {
+            channelHandlerContext.pipeline().remove(this);
+            return;
+        }
+
+        logger.error(INTERNAL_ERROR, "", "", "TLS negotiation failed when trying to accept new connection.");
+        channelHandlerContext.close();
+    }
+
+    private boolean isSsl(ByteBuf buf) {
+        return SslHandler.isEncrypted(buf);
+    }
+
+    private void enableSsl(ChannelHandlerContext ctx, SslContext sslContext) {
+        ChannelPipeline p = ctx.pipeline();
+        ctx.pipeline().addAfter(ctx.name(), null, sslContext.newHandler(ctx.alloc()));
+        p.addLast("unificationA", new SslServerTlsHandler(url, true));
+        p.remove(this);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/InvokerAndRestMethodMetadataPair.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/InvokerAndRestMethodMetadataPair.java
new file mode 100644
index 0000000..e95978e
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/InvokerAndRestMethodMetadataPair.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.pair;
+
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.rpc.Invoker;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * for invoker & restMethodMetadata pair
+ */
+public class InvokerAndRestMethodMetadataPair {
+
+    Invoker invoker;
+    RestMethodMetadata restMethodMetadata;
+
+    public InvokerAndRestMethodMetadataPair(Invoker invoker, RestMethodMetadata restMethodMetadata) {
+        this.invoker = invoker;
+        this.restMethodMetadata = restMethodMetadata;
+    }
+
+    public Invoker getInvoker() {
+        return invoker;
+    }
+
+    public RestMethodMetadata getRestMethodMetadata() {
+        return restMethodMetadata;
+    }
+
+    public static InvokerAndRestMethodMetadataPair pair(Invoker invoker, RestMethodMetadata restMethodMetadata) {
+        return new InvokerAndRestMethodMetadataPair(invoker, restMethodMetadata);
+    }
+
+    /**
+     * same interface  & same  method desc
+     *
+     * @param beforeMetadata
+     * @return
+     */
+    public boolean compareServiceMethod(InvokerAndRestMethodMetadataPair beforeMetadata) {
+
+        Class currentServiceInterface = this.invoker.getInterface();
+        Class<?> beforeServiceInterface = beforeMetadata.getInvoker().getInterface();
+
+        if (!currentServiceInterface.equals(beforeServiceInterface)) {
+            return false;
+        }
+
+        Method beforeServiceMethod = beforeMetadata.getRestMethodMetadata().getReflectMethod();
+
+        Method currentReflectMethod = this.restMethodMetadata.getReflectMethod();
+
+        if (beforeServiceMethod.getName().equals(currentReflectMethod.getName()) // method name
+                // method param types
+                && Arrays.toString(beforeServiceMethod.getParameterTypes())
+                        .equals(Arrays.toString(currentReflectMethod.getParameterTypes()))) {
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/MessageCodecResultPair.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/MessageCodecResultPair.java
new file mode 100644
index 0000000..91e5648
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/pair/MessageCodecResultPair.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.pair;
+
+
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+
+/**
+ *  for http message codec result
+ */
+public class MessageCodecResultPair {
+    /**
+     *  has coded
+     */
+    boolean coded;
+
+    /**
+     *  codec type
+     */
+    MediaType mediaType;
+
+    public MessageCodecResultPair(boolean coded, MediaType mediaType) {
+        this.coded = coded;
+        this.mediaType = mediaType;
+    }
+
+    public boolean isCoded() {
+        return coded;
+    }
+
+    public MediaType getMediaType() {
+        return mediaType;
+    }
+
+    public static MessageCodecResultPair pair(boolean coded, MediaType mediaType) {
+        return new MessageCodecResultPair(coded, mediaType);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/NettyRequestFacade.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/NettyRequestFacade.java
new file mode 100644
index 0000000..31f04ce
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/NettyRequestFacade.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.request;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufInputStream;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpContent;
+import org.apache.dubbo.common.utils.IOUtils;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+/**
+ * netty request facade
+ */
+public class NettyRequestFacade extends RequestFacade<FullHttpRequest> {
+
+    private ChannelHandlerContext context;
+
+    public NettyRequestFacade(Object request, ChannelHandlerContext context) {
+        super((FullHttpRequest) request);
+        this.context = context;
+    }
+
+    public NettyRequestFacade(Object request, ChannelHandlerContext context, ServiceDeployer serviceDeployer) {
+        super((FullHttpRequest) request, serviceDeployer);
+        this.context = context;
+    }
+
+    protected void initHeaders() {
+        for (Map.Entry<String, String> header : request.headers()) {
+
+            String key = header.getKey();
+
+            ArrayList<String> tmpHeaders = headers.get(key);
+
+            if (tmpHeaders == null) {
+                tmpHeaders = new ArrayList<>();
+                headers.put(key, tmpHeaders);
+            }
+
+            tmpHeaders.add(header.getValue());
+        }
+    }
+
+    @Override
+    public String getHeader(String name) {
+
+        List<String> values = headers.get(name);
+
+        if (values == null && name != null) {
+            values = headers.get(name.toLowerCase());
+        }
+
+        if (values == null || values.isEmpty()) {
+            return null;
+        } else {
+            return values.get(0);
+        }
+    }
+
+    @Override
+    public Enumeration<String> getHeaders(String name) {
+
+        List<String> list = headers.get(name);
+
+        if (list == null) {
+            list = new ArrayList<>();
+        }
+
+        ListIterator<String> stringListIterator = list.listIterator();
+
+        return new Enumeration<String>() {
+            @Override
+            public boolean hasMoreElements() {
+                return stringListIterator.hasNext();
+            }
+
+            @Override
+            public String nextElement() {
+                return stringListIterator.next();
+            }
+        };
+    }
+
+    @Override
+    public Enumeration<String> getHeaderNames() {
+
+        Iterator<String> strings = headers.keySet().iterator();
+
+        return new Enumeration<String>() {
+            @Override
+            public boolean hasMoreElements() {
+                return strings.hasNext();
+            }
+
+            @Override
+            public String nextElement() {
+                return strings.next();
+            }
+        };
+    }
+
+    @Override
+    public String getMethod() {
+        return request.method().name();
+    }
+
+    @Override
+    public String getPath() {
+        return path;
+    }
+
+    @Override
+    public String getContextPath() {
+        // TODO add ContextPath
+        return null;
+    }
+
+    @Override
+    public String getRequestURI() {
+        return request.uri();
+    }
+
+    @Override
+    public String getParameter(String name) {
+        ArrayList<String> strings = parameters.get(name);
+
+        String value = null;
+        if (strings != null && !strings.isEmpty()) {
+            value = strings.get(0);
+        }
+        return value;
+    }
+
+    @Override
+    public Enumeration<String> getParameterNames() {
+
+        Iterator<String> iterator = parameters.keySet().iterator();
+
+        return new Enumeration<String>() {
+            @Override
+            public boolean hasMoreElements() {
+                return iterator.hasNext();
+            }
+
+            @Override
+            public String nextElement() {
+                return iterator.next();
+            }
+        };
+    }
+
+    @Override
+    public String[] getParameterValues(String name) {
+
+        if (!parameters.containsKey(name)) {
+
+            return null;
+        }
+        return parameters.get(name).toArray(new String[0]);
+    }
+
+    @Override
+    public Map<String, String[]> getParameterMap() {
+        HashMap<String, String[]> map = new HashMap<>();
+        parameters.entrySet().forEach(entry -> {
+            map.put(entry.getKey(), entry.getValue().toArray(new String[0]));
+        });
+        return map;
+    }
+
+    @Override
+    public String getRemoteAddr() {
+        return getChannel().remoteAddress().getHostString();
+    }
+
+    @Override
+    public String getRemoteHost() {
+        return getRemoteAddr() + ":" + getRemotePort();
+    }
+
+    @Override
+    public int getRemotePort() {
+        return getChannel().remoteAddress().getPort();
+    }
+
+    @Override
+    public String getLocalAddr() {
+        return getChannel().localAddress().getHostString();
+    }
+
+    @Override
+    public String getLocalHost() {
+        return getRemoteAddr() + ":" + getLocalPort();
+    }
+
+    private NioSocketChannel getChannel() {
+        return (NioSocketChannel) context.channel();
+    }
+
+    @Override
+    public int getLocalPort() {
+        return getChannel().localAddress().getPort();
+    }
+
+    @Override
+    public byte[] getInputStream() throws IOException {
+
+        return body;
+    }
+
+    protected void parseBody() {
+        ByteBuf byteBuf = ((HttpContent) request).content();
+
+        if (byteBuf.readableBytes() > 0) {
+
+            try {
+                body = IOUtils.toByteArray(new ByteBufInputStream(byteBuf));
+            } catch (IOException e) {
+
+            }
+        }
+    }
+
+    public ChannelHandlerContext getNettyChannelContext() {
+        return context;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/RequestFacade.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/RequestFacade.java
new file mode 100644
index 0000000..c3c4ac5
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/request/RequestFacade.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.request;
+
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
+
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.DEFAULT_CHARSET;
+
+/**
+ * request facade for different request
+ *
+ * @param <T>
+ */
+public abstract class RequestFacade<T> {
+    protected Map<String, ArrayList<String>> headers = new HashMap<>();
+    protected Map<String, ArrayList<String>> parameters = new HashMap<>();
+
+    protected String path;
+    protected T request;
+    protected byte[] body = new byte[0];
+    protected ServiceDeployer serviceDeployer;
+
+    public RequestFacade(T request) {
+        this.request = request;
+        initHeaders();
+        initParameters();
+        parseBody();
+    }
+
+    public RequestFacade(T request, ServiceDeployer serviceDeployer) {
+        this(request);
+        this.serviceDeployer = serviceDeployer;
+    }
+
+    protected void initHeaders() {}
+
+    protected void initParameters() {
+        String requestURI = getRequestURI();
+        String decodedRequestURI = null;
+
+        try {
+            String enc = DEFAULT_CHARSET;
+            ArrayList<String> charset = headers.get(RestConstant.ACCEPT_CHARSET);
+            // take the highest priority charset
+            String[] parsed = DataParseUtils.parseAcceptCharset(charset);
+            if (parsed != null && parsed.length > 0) {
+                enc = parsed[0].toUpperCase();
+            }
+            decodedRequestURI = URLDecoder.decode(requestURI, enc);
+        } catch (Throwable t) {
+            // do nothing, try best to deliver
+        }
+
+        if (StringUtils.isNotEmpty(decodedRequestURI)) {
+            requestURI = decodedRequestURI;
+        }
+
+        if (requestURI != null && requestURI.contains("?")) {
+
+            String queryString = requestURI.substring(requestURI.indexOf("?") + 1);
+            path = requestURI.substring(0, requestURI.indexOf("?"));
+
+            String[] split = queryString.split("&");
+
+            for (String params : split) {
+                // key a=  ;value b==c
+                int index = params.indexOf("=");
+                if (index <= 0) {
+                    continue;
+                }
+
+                String name = params.substring(0, index);
+                String value = params.substring(index + 1);
+                if (!StringUtils.isEmpty(name)) {
+                    ArrayList<String> values = parameters.get(name);
+
+                    if (values == null) {
+                        values = new ArrayList<>();
+                        parameters.put(name, values);
+                    }
+                    values.add(value);
+                }
+            }
+        } else {
+            path = requestURI;
+        }
+    }
+
+    public T getRequest() {
+        return request;
+    }
+
+    public abstract String getHeader(String name);
+
+    public abstract Enumeration<String> getHeaders(String name);
+
+    public abstract Enumeration<String> getHeaderNames();
+
+    public abstract String getMethod();
+
+    public abstract String getPath();
+
+    public abstract String getContextPath();
+
+    public abstract String getRequestURI();
+
+    public abstract String getParameter(String name);
+
+    public abstract Enumeration<String> getParameterNames();
+
+    public abstract String[] getParameterValues(String name);
+
+    public abstract Map<String, String[]> getParameterMap();
+
+    public abstract String getRemoteAddr();
+
+    public abstract String getRemoteHost();
+
+    public abstract int getRemotePort();
+
+    public abstract String getLocalAddr();
+
+    public abstract String getLocalHost();
+
+    public abstract int getLocalPort();
+
+    public abstract byte[] getInputStream() throws IOException;
+
+    protected abstract void parseBody();
+
+    public ServiceDeployer getServiceDeployer() {
+        return serviceDeployer;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/ResteasyContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/ResteasyContext.java
new file mode 100644
index 0000000..147b81c
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/ResteasyContext.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.handler.codec.http.HttpContent;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpRequest;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboContainerResponseContextImpl;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboPreMatchContainerRequestContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.ServiceInvokeRestFilter;
+import org.apache.dubbo.rpc.protocol.rest.netty.ChunkOutputStream;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil;
+import org.jboss.resteasy.core.interception.ResponseContainerRequestContext;
+import org.jboss.resteasy.plugins.server.netty.NettyHttpRequest;
+import org.jboss.resteasy.plugins.server.netty.NettyUtil;
+import org.jboss.resteasy.specimpl.BuiltResponse;
+import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
+import org.jboss.resteasy.spi.HttpResponse;
+import org.jboss.resteasy.spi.ResteasyUriInfo;
+
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MultivaluedMap;
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+public interface ResteasyContext {
+    String HTTP_PROTOCOL = "http://";
+    String HTTP = "http";
+    String HTTPS_PROTOCOL = "https://";
+
+    /**
+     * return extensions that are  filtered by  extension type
+     *
+     * @param extension
+     * @param <T>
+     * @return
+     */
+    default <T> List<T> getExtension(ServiceDeployer serviceDeployer, Class<T> extension) {
+
+        return serviceDeployer.getExtensions(extension);
+    }
+
+    default DubboPreMatchContainerRequestContext convertHttpRequestToContainerRequestContext(
+            RequestFacade requestFacade, ContainerRequestFilter[] requestFilters) {
+
+        NettyRequestFacade nettyRequestFacade = (NettyRequestFacade) requestFacade;
+        HttpRequest request = (HttpRequest) requestFacade.getRequest();
+
+        NettyHttpRequest nettyRequest = createNettyHttpRequest(nettyRequestFacade, request);
+
+        if (request instanceof HttpContent) {
+
+            try {
+                byte[] inputStream = requestFacade.getInputStream();
+                ByteBuf buffer =
+                        nettyRequestFacade.getNettyChannelContext().alloc().buffer();
+                buffer.writeBytes(inputStream);
+                nettyRequest.setContentBuffer(buffer);
+            } catch (IOException e) {
+            }
+        }
+
+        return new DubboPreMatchContainerRequestContext(nettyRequest, requestFilters, null);
+    }
+
+    default ResteasyUriInfo extractUriInfo(HttpRequest request) {
+        String host = HttpHeaders.getHost(request, "unknown");
+        if ("".equals(host)) {
+            host = "unknown";
+        }
+        String uri = request.getUri();
+
+        String uriString;
+
+        // If we appear to have an absolute URL, don't try to recreate it from the host and request line.
+        if (uri.startsWith(HTTP_PROTOCOL) || uri.startsWith(HTTPS_PROTOCOL)) {
+            uriString = uri;
+        } else {
+            uriString = HTTP + "://" + host + uri;
+        }
+
+        URI absoluteURI = URI.create(uriString);
+        return new ResteasyUriInfo(uriString, absoluteURI.getRawQuery(), "");
+    }
+
+    default NettyHttpRequest createNettyHttpRequest(NettyRequestFacade nettyRequestFacade, HttpRequest request) {
+        ResteasyHttpHeaders headers = NettyUtil.extractHttpHeaders(request);
+        ResteasyUriInfo uriInfo = extractUriInfo(request);
+        NettyHttpRequest nettyRequest = new NettyHttpRequest(
+                nettyRequestFacade.getNettyChannelContext(),
+                headers,
+                uriInfo,
+                request.getMethod().name(),
+                null,
+                null,
+                HttpHeaders.is100ContinueExpected(request));
+
+        return nettyRequest;
+    }
+
+    default NettyHttpRequest createNettyHttpRequest(RequestFacade requestFacade) {
+        NettyRequestFacade nettyRequestFacade = (NettyRequestFacade) requestFacade;
+        HttpRequest request = (HttpRequest) requestFacade.getRequest();
+
+        ResteasyHttpHeaders headers = NettyUtil.extractHttpHeaders(request);
+        ResteasyUriInfo uriInfo = extractUriInfo(request);
+        NettyHttpRequest nettyRequest = new NettyHttpRequest(
+                nettyRequestFacade.getNettyChannelContext(),
+                headers,
+                uriInfo,
+                request.getMethod().name(),
+                null,
+                null,
+                HttpHeaders.is100ContinueExpected(request));
+
+        return nettyRequest;
+    }
+
+    default void writeResteasyResponse(
+            URL url, RequestFacade requestFacade, NettyHttpResponse response, BuiltResponse restResponse)
+            throws Exception {
+        if (restResponse.getMediaType() != null) {
+            MediaType mediaType = MediaTypeUtil.convertMediaType(
+                    restResponse.getEntityClass(), restResponse.getMediaType().toString());
+            ServiceInvokeRestFilter.writeResult(
+                    response, url, restResponse.getEntity(), restResponse.getEntityClass(), mediaType);
+        } else {
+            ServiceInvokeRestFilter.writeResult(
+                    response, requestFacade, url, restResponse.getEntity(), restResponse.getEntityClass());
+        }
+    }
+
+    default MediaType getAcceptMediaType(RequestFacade request, Class<?> returnType) {
+
+        return ServiceInvokeRestFilter.getAcceptMediaType(request, returnType);
+    }
+
+    default void addResponseHeaders(NettyHttpResponse response, MultivaluedMap<String, Object> headers) {
+        if (headers == null || headers.isEmpty()) {
+
+            return;
+        }
+        for (Map.Entry<String, List<Object>> entry : headers.entrySet()) {
+
+            String key = entry.getKey();
+            List<Object> value = entry.getValue();
+            if (value == null || value.isEmpty()) {
+                continue;
+            }
+            for (Object tmp : value) {
+                response.addOutputHeaders(key, tmp.toString());
+            }
+        }
+    }
+
+    default DubboContainerResponseContextImpl createContainerResponseContext(
+            Object originRequest,
+            RequestFacade request,
+            HttpResponse httpResponse,
+            BuiltResponse jaxrsResponse,
+            ContainerResponseFilter[] responseFilters) {
+
+        NettyHttpRequest nettyHttpRequest =
+                originRequest == null ? createNettyHttpRequest(request) : (NettyHttpRequest) originRequest;
+
+        ResponseContainerRequestContext requestContext = new ResponseContainerRequestContext(nettyHttpRequest);
+        DubboContainerResponseContextImpl responseContext = new DubboContainerResponseContextImpl(
+                nettyHttpRequest, httpResponse, jaxrsResponse, requestContext, responseFilters, null, null);
+
+        return responseContext;
+    }
+
+    default void restOutputStream(NettyHttpResponse response) throws IOException {
+        ChunkOutputStream outputStream = (ChunkOutputStream) response.getOutputStream();
+        outputStream.reset();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/DubboBuiltResponse.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/DubboBuiltResponse.java
new file mode 100644
index 0000000..3156114
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/DubboBuiltResponse.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy.filter;
+
+import org.jboss.resteasy.specimpl.BuiltResponse;
+
+/**
+ * wrapper resteasy  BuiltResponse
+ */
+public class DubboBuiltResponse extends BuiltResponse {
+
+    // user reset entity
+    private boolean resetEntity;
+
+    public DubboBuiltResponse(Object entity, int status, Class<?> entityClass) {
+
+        this.entity = entity;
+        this.entityClass = entityClass;
+        this.status = status;
+    }
+
+    @Override
+    public void setEntity(Object entity) {
+        if (entity == null) {
+            return;
+        }
+
+        if (entity.equals(this.entity)) {
+            return;
+        }
+        //  reset entity true
+        this.resetEntity = true;
+        super.setEntity(entity);
+    }
+
+    public boolean isResetEntity() {
+        return resetEntity;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/DubboContainerResponseContextImpl.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/DubboContainerResponseContextImpl.java
new file mode 100644
index 0000000..954fdc9
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/DubboContainerResponseContextImpl.java
@@ -0,0 +1,391 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy.filter;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.jboss.resteasy.core.Dispatcher;
+import org.jboss.resteasy.core.ServerResponseWriter;
+import org.jboss.resteasy.core.SynchronousDispatcher;
+import org.jboss.resteasy.core.interception.ResponseContainerRequestContext;
+import org.jboss.resteasy.core.interception.jaxrs.SuspendableContainerResponseContext;
+import org.jboss.resteasy.specimpl.BuiltResponse;
+import org.jboss.resteasy.spi.ApplicationException;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.HttpResponse;
+import org.jboss.resteasy.spi.ResteasyAsynchronousResponse;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Link;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class DubboContainerResponseContextImpl implements SuspendableContainerResponseContext {
+    private static final ErrorTypeAwareLogger logger =
+            LoggerFactory.getErrorTypeAwareLogger(DubboContainerResponseContextImpl.class);
+
+    protected final HttpRequest request;
+    protected final HttpResponse httpResponse;
+    protected final BuiltResponse jaxrsResponse;
+    private ResponseContainerRequestContext requestContext;
+    private ContainerResponseFilter[] responseFilters;
+    private ServerResponseWriter.RunnableWithIOException continuation;
+    private int currentFilter;
+    private boolean suspended;
+    private boolean filterReturnIsMeaningful = true;
+    private Map<Class<?>, Object> contextDataMap;
+    private boolean inFilter;
+    private Throwable throwable;
+    private Consumer<Throwable> onComplete;
+    private boolean weSuspended;
+
+    public DubboContainerResponseContextImpl(
+            final HttpRequest request,
+            final HttpResponse httpResponse,
+            final BuiltResponse serverResponse,
+            final ResponseContainerRequestContext requestContext,
+            final ContainerResponseFilter[] responseFilters,
+            final Consumer<Throwable> onComplete,
+            final ServerResponseWriter.RunnableWithIOException continuation) {
+        this.request = request;
+        this.httpResponse = httpResponse;
+        this.jaxrsResponse = serverResponse;
+        this.requestContext = requestContext;
+        this.responseFilters = responseFilters;
+        this.continuation = continuation;
+        this.onComplete = onComplete;
+        contextDataMap = ResteasyProviderFactory.getContextDataMap();
+    }
+
+    public BuiltResponse getJaxrsResponse() {
+        return jaxrsResponse;
+    }
+
+    public HttpResponse getHttpResponse() {
+        return httpResponse;
+    }
+
+    @Override
+    public int getStatus() {
+        return jaxrsResponse.getStatus();
+    }
+
+    @Override
+    public void setStatus(int code) {
+        httpResponse.setStatus(code);
+        jaxrsResponse.setStatus(code);
+    }
+
+    @Override
+    public Response.StatusType getStatusInfo() {
+        return jaxrsResponse.getStatusInfo();
+    }
+
+    @Override
+    public void setStatusInfo(Response.StatusType statusInfo) {
+        httpResponse.setStatus(statusInfo.getStatusCode());
+        jaxrsResponse.setStatus(statusInfo.getStatusCode());
+    }
+
+    @Override
+    public Class<?> getEntityClass() {
+        return jaxrsResponse.getEntityClass();
+    }
+
+    @Override
+    public Type getEntityType() {
+        return jaxrsResponse.getGenericType();
+    }
+
+    @Override
+    public void setEntity(Object entity) {
+        if (entity != null && jaxrsResponse.getEntity() != null) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Dubbo container response context filter set entity ,before entity is: "
+                        + jaxrsResponse.getEntity() + "and after entity is: " + entity);
+            }
+        }
+        jaxrsResponse.setEntity(entity);
+        // it resets the entity in a response filter which results
+        // in a bad content-length being sent back to the client
+        // so, we'll remove any content-length setting
+        getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
+    }
+
+    @Override
+    public void setEntity(Object entity, Annotation[] annotations, MediaType mediaType) {
+        if (entity != null && jaxrsResponse.getEntity() != null) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Dubbo container response context filter set entity ,before entity is: "
+                        + jaxrsResponse.getEntity() + "and after entity is: " + entity);
+            }
+        }
+        jaxrsResponse.setEntity(entity);
+        jaxrsResponse.setAnnotations(annotations);
+        jaxrsResponse.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, mediaType);
+        // it resets the entity in a response filter which results
+        // in a bad content-length being sent back to the client
+        // so, we'll remove any content-length setting
+        getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
+    }
+
+    @Override
+    public MultivaluedMap<String, Object> getHeaders() {
+        return jaxrsResponse.getMetadata();
+    }
+
+    @Override
+    public Set<String> getAllowedMethods() {
+        return jaxrsResponse.getAllowedMethods();
+    }
+
+    @Override
+    public Date getDate() {
+        return jaxrsResponse.getDate();
+    }
+
+    @Override
+    public Locale getLanguage() {
+        return jaxrsResponse.getLanguage();
+    }
+
+    @Override
+    public int getLength() {
+        return jaxrsResponse.getLength();
+    }
+
+    @Override
+    public MediaType getMediaType() {
+        return jaxrsResponse.getMediaType();
+    }
+
+    @Override
+    public Map<String, NewCookie> getCookies() {
+        return jaxrsResponse.getCookies();
+    }
+
+    @Override
+    public EntityTag getEntityTag() {
+        return jaxrsResponse.getEntityTag();
+    }
+
+    @Override
+    public Date getLastModified() {
+        return jaxrsResponse.getLastModified();
+    }
+
+    @Override
+    public URI getLocation() {
+        return jaxrsResponse.getLocation();
+    }
+
+    @Override
+    public Set<Link> getLinks() {
+        return jaxrsResponse.getLinks();
+    }
+
+    @Override
+    public boolean hasLink(String relation) {
+        return jaxrsResponse.hasLink(relation);
+    }
+
+    @Override
+    public Link getLink(String relation) {
+        return jaxrsResponse.getLink(relation);
+    }
+
+    @Override
+    public Link.Builder getLinkBuilder(String relation) {
+        return jaxrsResponse.getLinkBuilder(relation);
+    }
+
+    @Override
+    public boolean hasEntity() {
+        return !jaxrsResponse.isClosed() && jaxrsResponse.hasEntity();
+    }
+
+    @Override
+    public Object getEntity() {
+        return !jaxrsResponse.isClosed() ? jaxrsResponse.getEntity() : null;
+    }
+
+    @Override
+    public OutputStream getEntityStream() {
+        try {
+            return httpResponse.getOutputStream();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void setEntityStream(OutputStream entityStream) {
+        httpResponse.setOutputStream(entityStream);
+    }
+
+    @Override
+    public Annotation[] getEntityAnnotations() {
+        return jaxrsResponse.getAnnotations();
+    }
+
+    @Override
+    public MultivaluedMap<String, String> getStringHeaders() {
+        return jaxrsResponse.getStringHeaders();
+    }
+
+    @Override
+    public String getHeaderString(String name) {
+        return jaxrsResponse.getHeaderString(name);
+    }
+
+    @Override
+    public synchronized void suspend() {
+        if (continuation == null) throw new RuntimeException("Suspend not supported yet");
+        suspended = true;
+    }
+
+    @Override
+    public synchronized void resume() {
+        if (!suspended) throw new RuntimeException("Cannot resume: not suspended");
+        if (inFilter) {
+            // suspend/resume within filter, same thread: just ignore and move on
+            suspended = false;
+            return;
+        }
+
+        // go on, but with proper exception handling
+        try (ResteasyProviderFactory.CloseableContext c =
+                ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+            filter();
+        } catch (Throwable t) {
+            // don't throw to client
+            writeException(t);
+        }
+    }
+
+    @Override
+    public synchronized void resume(Throwable t) {
+        if (!suspended) throw new RuntimeException("Cannot resume: not suspended");
+        if (inFilter) {
+            // not suspended, or suspend/abortWith within filter, same thread: collect and move on
+            throwable = t;
+            suspended = false;
+        } else {
+            try (ResteasyProviderFactory.CloseableContext c =
+                    ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+                writeException(t);
+            }
+        }
+    }
+
+    private void writeException(Throwable t) {
+        /*
+         * Here we cannot call AsyncResponse.resume(t) because that would invoke the response filters
+         * and we should not invoke them because we're already in them.
+         */
+        HttpResponse httpResponse = (HttpResponse) contextDataMap.get(HttpResponse.class);
+        SynchronousDispatcher dispatcher = (SynchronousDispatcher) contextDataMap.get(Dispatcher.class);
+        ResteasyAsynchronousResponse asyncResponse = request.getAsyncContext().getAsyncResponse();
+
+        dispatcher.unhandledAsynchronousException(httpResponse, t);
+        onComplete.accept(t);
+        asyncResponse.complete();
+        asyncResponse.completionCallbacks(t);
+    }
+
+    public synchronized void filter() throws IOException {
+        while (currentFilter < responseFilters.length) {
+            ContainerResponseFilter filter = responseFilters[currentFilter++];
+            try {
+                suspended = false;
+                throwable = null;
+                inFilter = true;
+                filter.filter(requestContext, this);
+            } catch (IOException e) {
+                throw new ApplicationException(e);
+            } finally {
+                inFilter = false;
+            }
+            if (suspended) {
+                if (!request.getAsyncContext().isSuspended()) {
+                    request.getAsyncContext().suspend();
+                    weSuspended = true;
+                }
+                // ignore any abort request until we are resumed
+                filterReturnIsMeaningful = false;
+                return;
+            }
+            if (throwable != null) {
+                // handle the case where we've been suspended by a previous filter
+                if (filterReturnIsMeaningful) SynchronousDispatcher.rethrow(throwable);
+                else {
+                    writeException(throwable);
+                    return;
+                }
+            }
+        }
+        // here it means we reached the last filter
+
+        // some frameworks don't support async request filters, in which case suspend() is forbidden
+        // so if we get here we're still synchronous and don't have a continuation, which must be in
+        // the caller
+        if (continuation == null) return;
+
+        // if we've never been suspended, the caller is valid so let it handle any exception
+        if (filterReturnIsMeaningful) {
+            continuation.run();
+            onComplete.accept(null);
+            return;
+        }
+        // if we've been suspended then the caller is a filter and have to invoke our continuation
+        // try to write it out
+        try {
+            continuation.run();
+            onComplete.accept(null);
+            if (weSuspended) {
+                // if we're the ones who turned the request async, nobody will call complete() for us, so we have to
+                HttpServletRequest httpServletRequest =
+                        (HttpServletRequest) contextDataMap.get(HttpServletRequest.class);
+                httpServletRequest.getAsyncContext().complete();
+            }
+        } catch (IOException e) {
+            logger.error(
+                    "",
+                    "Dubbo container response context filter error",
+                    "request method is: " + request.getHttpMethod() + "and request uri is:"
+                            + request.getUri().getPath(),
+                    "",
+                    e);
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/DubboPreMatchContainerRequestContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/DubboPreMatchContainerRequestContext.java
new file mode 100644
index 0000000..5269a62
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/DubboPreMatchContainerRequestContext.java
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy.filter;
+
+import org.jboss.resteasy.core.interception.jaxrs.SuspendableContainerRequestContext;
+import org.jboss.resteasy.plugins.server.netty.NettyHttpRequest;
+import org.jboss.resteasy.specimpl.BuiltResponse;
+import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
+import org.jboss.resteasy.spi.ApplicationException;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public class DubboPreMatchContainerRequestContext implements SuspendableContainerRequestContext {
+    protected final NettyHttpRequest httpRequest;
+    protected Response response;
+    private ContainerRequestFilter[] requestFilters;
+    private int currentFilter;
+    private boolean suspended;
+    private boolean filterReturnIsMeaningful = true;
+    private Supplier<BuiltResponse> continuation;
+    private Map<Class<?>, Object> contextDataMap;
+    private boolean inFilter;
+    private Throwable throwable;
+    private boolean startedContinuation;
+
+    public DubboPreMatchContainerRequestContext(
+            final NettyHttpRequest request,
+            final ContainerRequestFilter[] requestFilters,
+            final Supplier<BuiltResponse> continuation) {
+        this.httpRequest = request;
+        this.requestFilters = requestFilters;
+        this.continuation = continuation;
+        contextDataMap = ResteasyProviderFactory.getContextDataMap();
+    }
+
+    public NettyHttpRequest getHttpRequest() {
+        return httpRequest;
+    }
+
+    public Response getResponseAbortedWith() {
+        return response;
+    }
+
+    @Override
+    public Object getProperty(String name) {
+        return httpRequest.getAttribute(name);
+    }
+
+    @Override
+    public Collection<String> getPropertyNames() {
+        ArrayList<String> names = new ArrayList<>();
+        Enumeration<String> enames = httpRequest.getAttributeNames();
+        while (enames.hasMoreElements()) {
+            names.add(enames.nextElement());
+        }
+        return names;
+    }
+
+    @Override
+    public void setProperty(String name, Object object) {
+        httpRequest.setAttribute(name, object);
+    }
+
+    @Override
+    public void removeProperty(String name) {
+        httpRequest.removeAttribute(name);
+    }
+
+    @Override
+    public UriInfo getUriInfo() {
+        return httpRequest.getUri();
+    }
+
+    @Override
+    public void setRequestUri(URI requestUri) throws IllegalStateException {
+        httpRequest.setRequestUri(requestUri);
+    }
+
+    @Override
+    public void setRequestUri(URI baseUri, URI requestUri) throws IllegalStateException {
+        httpRequest.setRequestUri(baseUri, requestUri);
+    }
+
+    @Override
+    public String getMethod() {
+        return httpRequest.getHttpMethod();
+    }
+
+    @Override
+    public void setMethod(String method) {
+        httpRequest.setHttpMethod(method);
+    }
+
+    @Override
+    public MultivaluedMap<String, String> getHeaders() {
+        return ((ResteasyHttpHeaders) httpRequest.getHttpHeaders()).getMutableHeaders();
+    }
+
+    @Override
+    public Date getDate() {
+        return httpRequest.getHttpHeaders().getDate();
+    }
+
+    @Override
+    public Locale getLanguage() {
+        return httpRequest.getHttpHeaders().getLanguage();
+    }
+
+    @Override
+    public int getLength() {
+        return httpRequest.getHttpHeaders().getLength();
+    }
+
+    @Override
+    public MediaType getMediaType() {
+        return httpRequest.getHttpHeaders().getMediaType();
+    }
+
+    @Override
+    public List<MediaType> getAcceptableMediaTypes() {
+        return httpRequest.getHttpHeaders().getAcceptableMediaTypes();
+    }
+
+    @Override
+    public List<Locale> getAcceptableLanguages() {
+        return httpRequest.getHttpHeaders().getAcceptableLanguages();
+    }
+
+    @Override
+    public Map<String, Cookie> getCookies() {
+        return httpRequest.getHttpHeaders().getCookies();
+    }
+
+    @Override
+    public boolean hasEntity() {
+        return getMediaType() != null;
+    }
+
+    @Override
+    public InputStream getEntityStream() {
+        return httpRequest.getInputStream();
+    }
+
+    @Override
+    public void setEntityStream(InputStream entityStream) {
+        httpRequest.setInputStream(entityStream);
+    }
+
+    @Override
+    public SecurityContext getSecurityContext() {
+        return ResteasyProviderFactory.getContextData(SecurityContext.class);
+    }
+
+    @Override
+    public void setSecurityContext(SecurityContext context) {
+        ResteasyProviderFactory.pushContext(SecurityContext.class, context);
+    }
+
+    @Override
+    public Request getRequest() {
+        return ResteasyProviderFactory.getContextData(Request.class);
+    }
+
+    @Override
+    public String getHeaderString(String name) {
+        return httpRequest.getHttpHeaders().getHeaderString(name);
+    }
+
+    @Override
+    public synchronized void suspend() {
+        if (continuation == null) throw new RuntimeException("Suspend not supported yet");
+        suspended = true;
+    }
+
+    @Override
+    public synchronized void abortWith(Response response) {
+        if (suspended && !inFilter) {
+            try (ResteasyProviderFactory.CloseableContext c =
+                    ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+                httpRequest.getAsyncContext().getAsyncResponse().resume(response);
+            }
+        } else {
+            // not suspended, or suspend/abortWith within filter, same thread: collect and move on
+            this.response = response;
+            suspended = false;
+        }
+    }
+
+    @Override
+    public synchronized void resume() {
+        if (!suspended) throw new RuntimeException("Cannot resume: not suspended");
+        if (inFilter) {
+            // suspend/resume within filter, same thread: just ignore and move on
+            suspended = false;
+            return;
+        }
+
+        // go on, but with proper exception handling
+        try (ResteasyProviderFactory.CloseableContext c =
+                ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+            filter();
+        } catch (Throwable t) {
+            // don't throw to client
+            writeException(t);
+        }
+    }
+
+    @Override
+    public synchronized void resume(Throwable t) {
+        if (!suspended) throw new RuntimeException("Cannot resume: not suspended");
+        if (inFilter) {
+            // not suspended, or suspend/abortWith within filter, same thread: collect and move on
+            throwable = t;
+            suspended = false;
+        } else {
+            try (ResteasyProviderFactory.CloseableContext c =
+                    ResteasyProviderFactory.addCloseableContextDataLevel(contextDataMap)) {
+                writeException(t);
+            }
+        }
+    }
+
+    private void writeException(Throwable t) {
+        /*
+         * Here, contrary to ContainerResponseContextImpl.writeException, we can use the async response
+         * to write the exception, because it calls the right response filters, complete() and callbacks
+         */
+        httpRequest.getAsyncContext().getAsyncResponse().resume(t);
+    }
+
+    public synchronized BuiltResponse filter() throws Throwable {
+        while (currentFilter < requestFilters.length) {
+            ContainerRequestFilter filter = requestFilters[currentFilter++];
+            try {
+                suspended = false;
+                response = null;
+                throwable = null;
+                inFilter = true;
+                filter.filter(this);
+            } catch (IOException e) {
+                throw new ApplicationException(e);
+            } finally {
+                inFilter = false;
+            }
+            if (suspended) {
+                if (!httpRequest.getAsyncContext().isSuspended())
+                    // ignore any abort request until we are resumed
+                    filterReturnIsMeaningful = false;
+                response = null;
+                return null;
+            }
+            BuiltResponse serverResponse = (BuiltResponse) getResponseAbortedWith();
+            if (serverResponse != null) {
+                // handle the case where we've been suspended by a previous filter
+                return serverResponse;
+            }
+
+            if (throwable != null) {
+                // handle the case where we've been suspended by a previous filter
+                throw throwable;
+            }
+        }
+        // here it means we reached the last filter
+        // some frameworks don't support async request filters, in which case suspend() is forbidden
+        // so if we get here we're still synchronous and don't have a continuation, which must be in
+        // the caller
+        startedContinuation = true;
+        if (continuation == null) return null;
+        // in any case, return the continuation: sync will use it, and async will ignore it
+        return continuation.get();
+    }
+
+    public boolean startedContinuation() {
+        return startedContinuation;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/ResteasyNettyHttpResponse.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/ResteasyNettyHttpResponse.java
new file mode 100644
index 0000000..bf6f5bb
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/ResteasyNettyHttpResponse.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy.filter;
+
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.jboss.resteasy.spi.HttpResponse;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+public class ResteasyNettyHttpResponse implements HttpResponse {
+
+    private NettyHttpResponse response;
+
+    private MultivaluedMap<String, Object> multivaluedMap = new MultivaluedMapImpl<>();
+
+    public ResteasyNettyHttpResponse(NettyHttpResponse response) {
+        this.response = response;
+        Map<String, List<String>> outputHeaders = response.getOutputHeaders();
+
+        for (Map.Entry<String, List<String>> headers : outputHeaders.entrySet()) {
+            String key = headers.getKey();
+            List<String> value = headers.getValue();
+
+            if (value == null || value.isEmpty()) {
+                continue;
+            }
+
+            for (String val : value) {
+                multivaluedMap.add(key, val);
+            }
+        }
+    }
+
+    @Override
+    public int getStatus() {
+        return response.getStatus();
+    }
+
+    @Override
+    public void setStatus(int status) {
+
+        response.setStatus(status);
+    }
+
+    @Override
+    public MultivaluedMap<String, Object> getOutputHeaders() {
+        return multivaluedMap;
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        return response.getOutputStream();
+    }
+
+    @Override
+    public void setOutputStream(OutputStream os) {
+        response.setOutputStream(os);
+    }
+
+    @Override
+    public void addNewCookie(NewCookie cookie) {}
+
+    @Override
+    public void sendError(int status) throws IOException {
+
+        response.sendError(status);
+    }
+
+    @Override
+    public void sendError(int status, String message) throws IOException {
+        response.sendError(status, message);
+    }
+
+    @Override
+    public boolean isCommitted() {
+        return false;
+    }
+
+    @Override
+    public void reset() {
+
+        response.reset();
+    }
+
+    @Override
+    public void flushBuffer() throws IOException {}
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/ResteasyRequestContainerFilterAdapter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/ResteasyRequestContainerFilterAdapter.java
new file mode 100644
index 0000000..a62f1b8
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/ResteasyRequestContainerFilterAdapter.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy.filter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboPreMatchContainerRequestContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestRequestFilter;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.jboss.resteasy.specimpl.BuiltResponse;
+
+import javax.ws.rs.container.ContainerRequestFilter;
+import java.util.List;
+
+@Activate(
+        value = "resteasy",
+        onClass = {
+            "javax.ws.rs.container.ContainerRequestFilter",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpRequest",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"
+        },
+        order = Integer.MAX_VALUE - 1)
+public class ResteasyRequestContainerFilterAdapter implements RestRequestFilter, ResteasyContext {
+
+    @Override
+    public void filter(RestFilterContext restFilterContext) throws Exception {
+
+        ServiceDeployer serviceDeployer = restFilterContext.getServiceDeployer();
+        RequestFacade requestFacade = restFilterContext.getRequestFacade();
+        URL url = restFilterContext.getUrl();
+        NettyHttpResponse response = restFilterContext.getResponse();
+
+        List<ContainerRequestFilter> containerRequestFilters =
+                getExtension(serviceDeployer, ContainerRequestFilter.class);
+
+        if (containerRequestFilters.isEmpty()) {
+
+            return;
+        }
+
+        DubboPreMatchContainerRequestContext containerRequestContext = convertHttpRequestToContainerRequestContext(
+                requestFacade, containerRequestFilters.toArray(new ContainerRequestFilter[0]));
+
+        // set resteasy request for save user`s custom  request attribute
+        restFilterContext.setOriginRequest(containerRequestContext.getHttpRequest());
+
+        try {
+            BuiltResponse restResponse = containerRequestContext.filter();
+
+            if (restResponse == null) {
+                return;
+            }
+
+            addResponseHeaders(response, restResponse.getHeaders());
+            writeResteasyResponse(url, requestFacade, response, restResponse);
+            // completed
+            restFilterContext.setComplete(true);
+        } catch (Throwable e) {
+            throw new RuntimeException("dubbo rest resteasy ContainerRequestFilter write response encode error", e);
+        } finally {
+            containerRequestContext.getHttpRequest().releaseContentBuffer();
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/ResteasyResponseContainerFilterAdapter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/ResteasyResponseContainerFilterAdapter.java
new file mode 100644
index 0000000..ecb7bab
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/filter/ResteasyResponseContainerFilterAdapter.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy.filter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboBuiltResponse;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboContainerResponseContextImpl;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.ResteasyNettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestResponseFilter;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestFilterContext;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.jboss.resteasy.spi.HttpResponse;
+
+import javax.ws.rs.container.ContainerResponseFilter;
+import java.util.List;
+
+@Activate(
+        value = "resteasy",
+        order = Integer.MAX_VALUE - 1000,
+        onClass = {
+            "org.jboss.resteasy.specimpl.BuiltResponse",
+            "javax.ws.rs.container.ContainerResponseFilter",
+            "org.jboss.resteasy.spi.HttpResponse",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"
+        })
+public class ResteasyResponseContainerFilterAdapter implements RestResponseFilter, ResteasyContext {
+    @Override
+    public void filter(RestFilterContext restFilterContext) throws Exception {
+
+        ServiceDeployer serviceDeployer = restFilterContext.getServiceDeployer();
+        RequestFacade requestFacade = restFilterContext.getRequestFacade();
+        NettyHttpResponse response = restFilterContext.getResponse();
+        URL url = restFilterContext.getUrl();
+        List<ContainerResponseFilter> containerRequestFilters =
+                getExtension(serviceDeployer, ContainerResponseFilter.class);
+
+        if (containerRequestFilters.isEmpty()) {
+            return;
+        }
+
+        // response filter entity first
+
+        // build jaxrsResponse from rest netty response
+        org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.DubboBuiltResponse dubboBuiltResponse =
+                new DubboBuiltResponse(response.getResponseBody(), response.getStatus(), response.getEntityClass());
+        // NettyHttpResponse wrapper
+        HttpResponse httpResponse = new ResteasyNettyHttpResponse(response);
+        DubboContainerResponseContextImpl containerResponseContext = createContainerResponseContext(
+                restFilterContext.getOriginRequest(),
+                requestFacade,
+                httpResponse,
+                dubboBuiltResponse,
+                containerRequestFilters.toArray(new ContainerResponseFilter[0]));
+        containerResponseContext.filter();
+
+        // user reset entity
+        if (dubboBuiltResponse.hasEntity() && dubboBuiltResponse.isResetEntity()) {
+            // clean  output stream data
+            restOutputStream(response);
+            writeResteasyResponse(url, requestFacade, response, dubboBuiltResponse);
+        }
+        addResponseHeaders(response, httpResponse.getOutputHeaders());
+
+        restFilterContext.setComplete(true);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/intercept/DubboServerWriterInterceptorContext.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/intercept/DubboServerWriterInterceptorContext.java
new file mode 100644
index 0000000..53573a2
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/intercept/DubboServerWriterInterceptorContext.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy.intercept;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.jboss.resteasy.core.interception.ServerWriterInterceptorContext;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.WriterInterceptor;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+public class DubboServerWriterInterceptorContext extends ServerWriterInterceptorContext {
+    private static final ErrorTypeAwareLogger logger =
+            LoggerFactory.getErrorTypeAwareLogger(DubboServerWriterInterceptorContext.class);
+
+    public DubboServerWriterInterceptorContext(
+            WriterInterceptor[] interceptors,
+            ResteasyProviderFactory providerFactory,
+            Object entity,
+            Class type,
+            Type genericType,
+            Annotation[] annotations,
+            MediaType mediaType,
+            MultivaluedMap<String, Object> headers,
+            OutputStream outputStream,
+            HttpRequest request) {
+        super(
+                interceptors,
+                providerFactory,
+                entity,
+                type,
+                genericType,
+                annotations,
+                mediaType,
+                headers,
+                outputStream,
+                request);
+    }
+
+    @Override
+    public void proceed() throws IOException, WebApplicationException {
+        logger.debug("Dubbo server writer intercept  context: " + getClass().getName() + "  Method : proceed");
+
+        if (interceptors == null || index >= interceptors.length) {
+            return;
+        } else {
+
+            logger.debug("Dubbo server writer intercept  context WriterInterceptor: "
+                    + interceptors[index].getClass().getName());
+            interceptors[index++].aroundWriteTo(this);
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/intercept/ResteasyStatusCodeInterceptor.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/intercept/ResteasyStatusCodeInterceptor.java
new file mode 100644
index 0000000..d60dd19
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/intercept/ResteasyStatusCodeInterceptor.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy.intercept;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestInterceptContext;
+import org.jboss.resteasy.specimpl.AbstractBuiltResponse;
+
+@Activate(
+        value = "resteasy-resStatus",
+        onClass = {
+            "javax.ws.rs.ext.WriterInterceptorContext",
+            "org.jboss.resteasy.specimpl.BuiltResponse",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpRequest",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"
+        },
+        order = Integer.MAX_VALUE)
+public class ResteasyStatusCodeInterceptor implements RestResponseInterceptor, ResteasyContext {
+
+    @Override
+    public void intercept(RestInterceptContext restResponseInterceptor) throws Exception {
+        Object result = restResponseInterceptor.getResult();
+
+        if (result == null || (!(result instanceof AbstractBuiltResponse))) {
+            return;
+        }
+
+        AbstractBuiltResponse abstractBuiltResponse = (AbstractBuiltResponse) result;
+
+        restResponseInterceptor.getResponse().setStatus(abstractBuiltResponse.getStatus());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/intercept/ResteasyWriterInterceptorAdapter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/intercept/ResteasyWriterInterceptorAdapter.java
new file mode 100644
index 0000000..601c0af
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/resteasy/intercept/ResteasyWriterInterceptorAdapter.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.resteasy.intercept;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
+import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployer;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.ResteasyContext;
+import org.apache.dubbo.rpc.protocol.rest.extension.resteasy.intercept.DubboServerWriterInterceptorContext;
+import org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor;
+import org.apache.dubbo.rpc.protocol.rest.filter.context.RestInterceptContext;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext;
+import org.jboss.resteasy.plugins.server.netty.NettyHttpRequest;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.WriterInterceptor;
+import java.io.ByteArrayOutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.List;
+
+@Activate(
+        value = "resteasy",
+        onClass = {
+            "javax.ws.rs.ext.WriterInterceptorContext",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpRequest",
+            "org.jboss.resteasy.plugins.server.netty.NettyHttpResponse"
+        })
+public class ResteasyWriterInterceptorAdapter implements RestResponseInterceptor, ResteasyContext {
+
+    private ResteasyProviderFactory resteasyProviderFactory = getResteasyProviderFactory();
+
+    @Override
+    public void intercept(RestInterceptContext restResponseInterceptor) throws Exception {
+
+        RpcInvocation rpcInvocation = restResponseInterceptor.getRpcInvocation();
+        ServiceDeployer serviceDeployer = restResponseInterceptor.getServiceDeployer();
+        RequestFacade request = restResponseInterceptor.getRequestFacade();
+        NettyHttpResponse response = restResponseInterceptor.getResponse();
+        Object result = restResponseInterceptor.getResult();
+
+        Class<?> type = rpcInvocation.getReturnType();
+
+        List<WriterInterceptor> extension = serviceDeployer.getExtensions(WriterInterceptor.class);
+
+        if (extension.isEmpty()) {
+            return;
+        }
+
+        NettyHttpRequest nettyHttpRequest = (NettyHttpRequest) restResponseInterceptor.getOriginRequest();
+
+        HttpRequest restRequest = nettyHttpRequest == null ? createNettyHttpRequest(request) : nettyHttpRequest;
+
+        MultivaluedMap<String, Object> headers = new MultivaluedMapImpl();
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+        try {
+
+            // get content-type
+            String value = getAcceptMediaType(request, type).value;
+
+            MediaType mediaType = MediaType.valueOf(value);
+
+            AbstractWriterInterceptorContext writerContext = getAbstractWriterInterceptorContext(
+                    restRequest, extension, result, type, type, mediaType, os, headers);
+
+            writerContext.proceed();
+            ByteArrayOutputStream outputStream = (ByteArrayOutputStream) writerContext.getOutputStream();
+
+            addResponseHeaders(response, writerContext.getHeaders());
+
+            if (outputStream.size() <= 0) {
+                return;
+            }
+
+            // intercept response  first
+            restOutputStream(response);
+
+            byte[] bytes = outputStream.toByteArray();
+            response.getOutputStream().write(bytes);
+            response.addOutputHeaders(RestHeaderEnum.CONTENT_TYPE.getHeader(), value);
+
+            restResponseInterceptor.setComplete(true);
+        } finally {
+            IOUtils.close(os);
+        }
+    }
+
+    private AbstractWriterInterceptorContext getAbstractWriterInterceptorContext(
+            HttpRequest request,
+            List<WriterInterceptor> extension,
+            Object entity,
+            Class type,
+            Type genericType,
+            MediaType mediaType,
+            ByteArrayOutputStream os,
+            MultivaluedMap<String, Object> headers) {
+        AbstractWriterInterceptorContext writerContext = new DubboServerWriterInterceptorContext(
+                extension.toArray(new WriterInterceptor[0]),
+                resteasyProviderFactory,
+                entity,
+                type,
+                genericType,
+                new Annotation[0],
+                mediaType,
+                headers,
+                os,
+                request);
+        return writerContext;
+    }
+
+    protected ResteasyProviderFactory getResteasyProviderFactory() {
+        return new ResteasyProviderFactory();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/support/ContentType.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/support/ContentType.java
new file mode 100644
index 0000000..8e586b5
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/support/ContentType.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.support;
+
+import javax.ws.rs.core.MediaType;
+
+public class ContentType {
+
+    public static final String APPLICATION_JSON_UTF_8 =
+            MediaType.APPLICATION_JSON + "; " + MediaType.CHARSET_PARAMETER + "=UTF-8";
+    public static final String TEXT_XML_UTF_8 = MediaType.TEXT_XML + "; " + MediaType.CHARSET_PARAMETER + "=UTF-8";
+    public static final String TEXT_PLAIN_UTF_8 = MediaType.TEXT_PLAIN + "; " + MediaType.CHARSET_PARAMETER + "=UTF-8";
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/swagger/DubboSwaggerApiListingResource.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/swagger/DubboSwaggerApiListingResource.java
new file mode 100644
index 0000000..e5fb004
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/swagger/DubboSwaggerApiListingResource.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.swagger;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import io.swagger.jaxrs.listing.BaseApiListingResource;
+import org.apache.dubbo.config.annotation.Service;
+import org.apache.dubbo.rpc.protocol.rest.integration.swagger.DubboSwaggerService;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+@Service
+public class DubboSwaggerApiListingResource extends BaseApiListingResource implements DubboSwaggerService {
+
+    @Context
+    ServletContext context;
+
+    @Override
+    public Response getListingJson(Application app, ServletConfig sc, HttpHeaders headers, UriInfo uriInfo)
+            throws JsonProcessingException {
+        Response response = getListingJsonResponse(app, context, sc, headers, uriInfo);
+        response.getHeaders().add("Access-Control-Allow-Origin", "*");
+        response.getHeaders().add("Access-Control-Allow-Headers", "x-requested-with, ssi-token");
+        response.getHeaders().add("Access-Control-Max-Age", "3600");
+        response.getHeaders().add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
+        return response;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/swagger/DubboSwaggerService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/swagger/DubboSwaggerService.java
new file mode 100644
index 0000000..505ab97
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/swagger/DubboSwaggerService.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.swagger;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import javax.servlet.ServletConfig;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+@Path("dubbo")
+@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
+@Produces({MediaType.APPLICATION_JSON + "; " + "charset=UTF-8", MediaType.TEXT_XML + "; " + "charset=UTF-8"})
+public interface DubboSwaggerService {
+
+    @GET
+    @Path("swagger")
+    Response getListingJson(
+            @Context Application app, @Context ServletConfig sc, @Context HttpHeaders headers, @Context UriInfo uriInfo)
+            throws JsonProcessingException;
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ConstraintViolationExceptionConvert.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ConstraintViolationExceptionConvert.java
new file mode 100644
index 0000000..0663703
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ConstraintViolationExceptionConvert.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.util;
+
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.protocol.rest.RestConstraintViolation;
+import org.apache.dubbo.rpc.protocol.rest.ViolationReport;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+
+public class ConstraintViolationExceptionConvert {
+
+    public static Object handleConstraintViolationException(RpcException rpcException) {
+        ConstraintViolationException cve = (ConstraintViolationException) rpcException.getCause();
+        ViolationReport report = new ViolationReport();
+        for (ConstraintViolation<?> cv : cve.getConstraintViolations()) {
+            report.addConstraintViolation(new RestConstraintViolation(
+                    cv.getPropertyPath().toString(),
+                    cv.getMessage(),
+                    cv.getInvalidValue() == null ? "null" : cv.getInvalidValue().toString()));
+        }
+        return report;
+    }
+
+    public static boolean needConvert(RpcException e) {
+        return isConstraintViolationException(e);
+    }
+
+    private static boolean isConstraintViolationException(RpcException e) {
+        try {
+            return e.getCause() instanceof ConstraintViolationException;
+        } catch (Throwable throwable) {
+            return false;
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java
new file mode 100644
index 0000000..489d99b
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/DataParseUtils.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.util;
+
+import org.apache.dubbo.common.lang.Nullable;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.common.utils.JsonUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Type;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.WEIGHT_IDENTIFIER;
+
+public class DataParseUtils {
+
+    public static Object stringTypeConvert(Class<?> targetType, String value) {
+
+        if (StringUtils.isEmpty(value)) {
+            return null;
+        }
+
+        if (targetType == Boolean.class || targetType == boolean.class) {
+            return Boolean.valueOf(value);
+        }
+
+        if (targetType == String.class) {
+            return value;
+        }
+
+        if (Number.class.isAssignableFrom(targetType)) {
+            return NumberUtils.parseNumber(value, targetType);
+        }
+
+        if (targetType != null && targetType.isPrimitive()) {
+            return NumberUtils.parseNumber(value, targetType);
+        }
+
+        return value;
+    }
+
+    public static boolean isTextType(Class targetType) {
+        if (targetType == null) {
+            return false;
+        }
+
+        return targetType == Boolean.class
+                || targetType == boolean.class
+                || targetType == String.class
+                || Number.class.isAssignableFrom(targetType)
+                || targetType.isPrimitive();
+    }
+
+    /**
+     * content-type text
+     *
+     * @param object
+     * @param outputStream
+     * @throws IOException
+     */
+    public static void writeTextContent(Object object, OutputStream outputStream) throws IOException {
+        outputStream.write(objectTextConvertToByteArray(object));
+    }
+
+    /**
+     * content-type json
+     *
+     * @param object
+     * @param outputStream
+     * @throws Exception
+     */
+    public static void writeJsonContent(Object object, OutputStream outputStream) throws Exception {
+        outputStream.write(JsonUtils.toJson(object).getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * content-type form
+     *
+     * @param formData
+     * @param outputStream
+     * @throws Exception
+     */
+    public static void writeFormContent(Map formData, OutputStream outputStream) throws Exception {
+        outputStream.write(serializeForm(formData, Charset.defaultCharset()).getBytes());
+    }
+
+    // TODO file multipart
+
+    public static String serializeForm(Map formData, Charset charset) {
+        StringBuilder builder = new StringBuilder();
+        formData.forEach((name, values) -> {
+            if (name == null) {
+
+                return;
+            }
+            ((List) values).forEach(value -> {
+                try {
+                    if (builder.length() != 0) {
+                        builder.append('&');
+                    }
+                    builder.append(URLEncoder.encode((String) name, charset.name()));
+                    if (value != null) {
+                        builder.append('=');
+                        builder.append(URLEncoder.encode(String.valueOf(value), charset.name()));
+                    }
+                } catch (UnsupportedEncodingException ex) {
+                    throw new IllegalStateException(ex);
+                }
+            });
+        });
+
+        return builder.toString();
+    }
+
+    public static byte[] objectTextConvertToByteArray(Object object) {
+        Class<?> objectClass = object.getClass();
+
+        if (objectClass == Boolean.class || objectClass == boolean.class) {
+            return object.toString().getBytes();
+        }
+
+        if (objectClass == String.class) {
+            return ((String) object).getBytes();
+        }
+
+        if (objectClass.isAssignableFrom(Number.class) || objectClass.isPrimitive()) {
+            return (byte[]) NumberUtils.numberToBytes((Number) object);
+        }
+
+        return object.toString().getBytes();
+    }
+
+    public static Object jsonConvert(Type targetType, byte[] body) throws Exception {
+        return JsonUtils.toJavaObject(new String(body, StandardCharsets.UTF_8), targetType);
+    }
+
+    public static Object multipartFormConvert(byte[] body, Charset charset, Class<?> targetType) throws Exception {
+        String[] pairs = tokenizeToStringArray(new String(body, StandardCharsets.UTF_8), "&");
+        Object result = MultiValueCreator.providerCreateMultiValueMap(targetType);
+        for (String pair : pairs) {
+            int idx = pair.indexOf('=');
+            if (idx == -1) {
+                MultiValueCreator.add(result, URLDecoder.decode(pair, charset.name()), null);
+            } else {
+                String name = URLDecoder.decode(pair.substring(0, idx), charset.name());
+                String value = URLDecoder.decode(pair.substring(idx + 1), charset.name());
+                MultiValueCreator.add(result, name, value);
+            }
+        }
+
+        return result;
+    }
+
+    public static Object multipartFormConvert(byte[] body, Class<?> targetType) throws Exception {
+        return multipartFormConvert(body, Charset.defaultCharset(), targetType);
+    }
+
+    public static String[] tokenizeToStringArray(String str, String delimiters) {
+        return tokenizeToStringArray(str, delimiters, true, true);
+    }
+
+    public static String[] tokenizeToStringArray(
+            String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
+        if (str == null) {
+            return null;
+        } else {
+            StringTokenizer st = new StringTokenizer(str, delimiters);
+            ArrayList tokens = new ArrayList();
+
+            while (true) {
+                String token;
+                do {
+                    if (!st.hasMoreTokens()) {
+                        return toStringArray(tokens);
+                    }
+
+                    token = st.nextToken();
+                    if (trimTokens) {
+                        token = token.trim();
+                    }
+                } while (ignoreEmptyTokens && token.length() <= 0);
+
+                tokens.add(token);
+            }
+        }
+    }
+
+    public static String[] toStringArray(Collection<String> collection) {
+        return collection == null ? null : collection.toArray(new String[collection.size()]);
+    }
+
+    @Nullable
+    public static String[] parseAcceptCharset(List<String> acceptCharsets) {
+        if (CollectionUtils.isEmpty(acceptCharsets)) {
+            return new String[0];
+        }
+
+        SortedMap<Float, Set<String>> encodings = new TreeMap<>(Comparator.reverseOrder());
+        float defaultWeight = 1.0f;
+        for (String acceptCharset : acceptCharsets) {
+            String[] charsets = acceptCharset.split(",");
+            for (String charset : charsets) {
+                charset = charset.trim();
+                float weight = defaultWeight;
+                String enc = charset;
+                if (charset.contains(WEIGHT_IDENTIFIER)) {
+                    String[] split = charset.split(WEIGHT_IDENTIFIER);
+                    enc = split[0];
+                    weight = Float.parseFloat(split[1]);
+                }
+                encodings.computeIfAbsent(weight, k -> new HashSet<>()).add(enc);
+            }
+        }
+
+        List<String> result = new ArrayList<>();
+        encodings.values().forEach(result::addAll);
+        return result.toArray(new String[0]);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/HttpHeaderUtil.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/HttpHeaderUtil.java
new file mode 100644
index 0000000..5388e7b
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/HttpHeaderUtil.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.util;
+
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.RestResult;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.protocol.rest.RestHeaderEnum;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.netty.NettyHttpResponse;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class HttpHeaderUtil {
+
+    /**
+     * convert attachment to Map<String, List<String>>
+     *
+     * @param attachmentMap
+     * @return
+     */
+    public static Map<String, List<String>> createAttachments(Map<String, Object> attachmentMap) {
+        Map<String, List<String>> attachments = new HashMap<>();
+        int size = 0;
+        for (Map.Entry<String, Object> entry : attachmentMap.entrySet()) {
+            String key = entry.getKey();
+            String value = String.valueOf(entry.getValue());
+
+            if (value != null) {
+                size += value.getBytes(StandardCharsets.UTF_8).length;
+            }
+
+            List<String> strings = attachments.get(key);
+            if (strings == null) {
+                strings = new ArrayList<>();
+                attachments.put(key, strings);
+            }
+            strings.add(value);
+        }
+
+        return attachments;
+    }
+
+    /**
+     * add consumer attachment to request
+     *
+     * @param requestTemplate
+     * @param attachmentMap
+     */
+    public static void addRequestAttachments(RequestTemplate requestTemplate, Map<String, Object> attachmentMap) {
+        Map<String, List<String>> attachments = createAttachments(attachmentMap);
+
+        attachments.entrySet().forEach(attachment -> {
+            requestTemplate.addHeaders(appendPrefixToAttachRealHeader(attachment.getKey()), attachment.getValue());
+        });
+    }
+
+    /**
+     * add  provider attachment to response
+     *
+     * @param nettyHttpResponse
+     */
+    public static void addResponseAttachments(NettyHttpResponse nettyHttpResponse) {
+        Map<String, List<String>> attachments =
+                createAttachments(RpcContext.getServerContext().getObjectAttachments());
+
+        attachments.entrySet().stream().forEach(attachment -> {
+            nettyHttpResponse
+                    .getOutputHeaders()
+                    .put(appendPrefixToAttachRealHeader(attachment.getKey()), attachment.getValue());
+        });
+    }
+
+    /**
+     * parse rest request header  attachment & header
+     *
+     * @param rpcInvocation
+     * @param requestFacade
+     */
+    public static void parseRequestHeader(RpcInvocation rpcInvocation, RequestFacade requestFacade) {
+
+        Enumeration<String> headerNames = requestFacade.getHeaderNames();
+
+        while (headerNames.hasMoreElements()) {
+            String header = headerNames.nextElement();
+
+            if (!isRestAttachHeader(header)) {
+                // attribute
+                rpcInvocation.put(header, requestFacade.getHeader(header));
+                continue;
+            }
+
+            // attachment
+            rpcInvocation.setAttachment(subRestAttachRealHeaderPrefix(header.trim()), requestFacade.getHeader(header));
+        }
+    }
+
+    /**
+     * for judge rest header or rest attachment
+     *
+     * @param header
+     * @return
+     */
+    public static boolean isRestAttachHeader(String header) {
+
+        if (StringUtils.isEmpty(header) || !header.startsWith(RestHeaderEnum.REST_HEADER_PREFIX.getHeader())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * for substring attachment prefix
+     *
+     * @param header
+     * @return
+     */
+    public static String subRestAttachRealHeaderPrefix(String header) {
+
+        return header.substring(RestHeaderEnum.REST_HEADER_PREFIX.getHeader().length());
+    }
+
+    /**
+     * append prefix to rest header  distinguish from normal header
+     *
+     * @param header
+     * @return
+     */
+    public static String appendPrefixToAttachRealHeader(String header) {
+
+        return RestHeaderEnum.REST_HEADER_PREFIX.getHeader() + header;
+    }
+
+    /**
+     *  parse request attribute
+     * @param rpcInvocation
+     * @param request
+     */
+    public static void parseRequestAttribute(RpcInvocation rpcInvocation, RequestFacade request) {
+        int localPort = request.getLocalPort();
+        String localAddr = request.getLocalAddr();
+        int remotePort = request.getRemotePort();
+        String remoteAddr = request.getRemoteAddr();
+
+        rpcInvocation.put(RestConstant.REMOTE_ADDR, remoteAddr);
+        rpcInvocation.put(RestConstant.LOCAL_ADDR, localAddr);
+        rpcInvocation.put(RestConstant.REMOTE_PORT, remotePort);
+        rpcInvocation.put(RestConstant.LOCAL_PORT, localPort);
+    }
+
+    /**
+     *  parse request
+     * @param rpcInvocation
+     * @param request
+     */
+    public static void parseRequest(RpcInvocation rpcInvocation, RequestFacade request) {
+        parseRequestHeader(rpcInvocation, request);
+        parseRequestAttribute(rpcInvocation, request);
+    }
+
+    /**
+     *  parse rest response header to appResponse attribute & attachment
+     * @param appResponse
+     * @param restResult
+     */
+    public static void parseResponseHeader(AppResponse appResponse, RestResult restResult) {
+
+        Map<String, List<String>> headers = restResult.headers();
+        if (headers == null || headers.isEmpty()) {
+            return;
+        }
+
+        headers.entrySet().stream().forEach(entry -> {
+            String header = entry.getKey();
+            if (isRestAttachHeader(header)) {
+                // attachment
+                appResponse.setAttachment(subRestAttachRealHeaderPrefix(header), entry.getValue());
+            } else {
+                // attribute
+                appResponse.setAttribute(header, entry.getValue());
+            }
+        });
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java
new file mode 100644
index 0000000..77e47a0
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MediaTypeUtil.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.util;
+
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class MediaTypeUtil {
+
+    private static final List<MediaType> mediaTypes = MediaType.getSupportMediaTypes();
+
+    /**
+     * return first match , if any multiple content-type  ,acquire mediaType by targetClass type .if contentTypes is empty
+     *
+     * @param contentTypes
+     * @return
+     */
+    public static MediaType convertMediaType(Class<?> targetType, String... contentTypes) {
+
+        if (contentTypes == null || contentTypes.length == 0) {
+            return HttpMessageCodecManager.typeSupport(targetType);
+        }
+
+        for (String contentType : contentTypes) {
+            for (MediaType mediaType : mediaTypes) {
+
+                if (contentType != null && contentType.contains(mediaType.value)) {
+                    return mediaType;
+                }
+            }
+
+            if (contentType != null && contentType.contains(MediaType.ALL_VALUE.value)) {
+                return HttpMessageCodecManager.typeSupport(targetType);
+            }
+        }
+
+        throw new UnSupportContentTypeException(Arrays.toString(contentTypes));
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java
new file mode 100644
index 0000000..31e9112
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/MultiValueCreator.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.util;
+
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+public class MultiValueCreator {
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(MultiValueCreator.class);
+
+    private static final String SPRING_MultiValueMapImpl = "org.springframework.util.LinkedMultiValueMap";
+    private static final String SPRING_MultiValueMap = "org.springframework.util.MultiValueMap";
+    private static final String JAVAX_MultiValueMapImpl = "org.jboss.resteasy.specimpl.MultivaluedMapImpl";
+    private static final String JAVAX_MultiValueMap = "javax.ws.rs.core.MultivaluedMap";
+
+    private static Class springMultiValueMapImplClass = null;
+    private static Class springMultiValueMapClass = null;
+    private static Method springMultiValueMapAdd = null;
+
+    private static Class jaxrsMultiValueMapImplClass = null;
+    private static Class jaxrsMultiValueMapClass = null;
+
+    private static Method jaxrsMultiValueMapAdd = null;
+
+    static {
+        springMultiValueMapClass = ReflectUtils.findClassTryException(SPRING_MultiValueMap);
+        springMultiValueMapImplClass = ReflectUtils.findClassTryException(SPRING_MultiValueMapImpl);
+        springMultiValueMapAdd = ReflectUtils.getMethodByName(springMultiValueMapImplClass, "add");
+
+        jaxrsMultiValueMapClass = ReflectUtils.findClassTryException(JAVAX_MultiValueMap);
+        jaxrsMultiValueMapImplClass = ReflectUtils.findClassTryException(JAVAX_MultiValueMapImpl);
+        jaxrsMultiValueMapAdd = ReflectUtils.getMethodByName(jaxrsMultiValueMapImplClass, "add");
+    }
+
+    public static Object providerCreateMultiValueMap(Class<?> targetType) {
+        try {
+            if (typeJudge(springMultiValueMapClass, targetType)) {
+                return springMultiValueMapImplClass.getDeclaredConstructor().newInstance();
+            } else if (typeJudge(jaxrsMultiValueMapClass, targetType)) {
+                return jaxrsMultiValueMapImplClass.getDeclaredConstructor().newInstance();
+            }
+        } catch (Exception e) {
+            logger.error(
+                    "",
+                    e.getMessage(),
+                    "current param type is: " + targetType + "and support type is : " + springMultiValueMapClass + "or"
+                            + jaxrsMultiValueMapClass,
+                    "dubbo rest form content-type param construct error,un support  param type: ",
+                    e);
+        }
+
+        return null;
+    }
+
+    private static boolean typeJudge(Class<?> parent, Class<?> targetType) {
+        if (parent == null) {
+            return false;
+        }
+
+        if (!Map.class.isAssignableFrom(targetType)) {
+            return true;
+        }
+
+        return parent.isAssignableFrom(targetType) || parent.equals(targetType);
+    }
+
+    public static void add(Object multiValueMap, String key, Object value) {
+        try {
+            if (multiValueMap == null) {
+                return;
+            }
+
+            Method multiValueMapAdd = null;
+            if (springMultiValueMapImplClass.equals(multiValueMap.getClass())) {
+                multiValueMapAdd = springMultiValueMapAdd;
+            } else if (jaxrsMultiValueMapImplClass.equals(multiValueMap.getClass())) {
+                multiValueMapAdd = jaxrsMultiValueMapAdd;
+            }
+
+            ReflectUtils.invokeAndTryCatch(multiValueMap, multiValueMapAdd, new Object[] {key, value});
+        } catch (Exception e) {
+            logger.error("", e.getMessage(), "", "dubbo rest form content-type param add data  error: ", e);
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java
new file mode 100644
index 0000000..d59680f
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/NumberUtils.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.util;
+
+import org.apache.dubbo.common.utils.Assert;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class NumberUtils {
+
+    public static <T> T parseNumber(String text, Class<T> targetClass) {
+        Assert.notNull(text, "Text must not be null");
+        Assert.notNull(targetClass, "Target class must not be null");
+        String trimmed = trimAllWhitespace(text);
+
+        if (Byte.class == targetClass || byte.class == targetClass) {
+            return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed));
+        } else if (Short.class == targetClass || short.class == targetClass) {
+            return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed));
+        } else if (Integer.class == targetClass || int.class == targetClass) {
+            return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed));
+        } else if (Long.class == targetClass || long.class == targetClass) {
+            return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed));
+        } else if (BigInteger.class == targetClass) {
+            return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed));
+        } else if (Float.class == targetClass || float.class == targetClass) {
+            return (T) Float.valueOf(trimmed);
+        } else if (Double.class == targetClass || double.class == targetClass) {
+            return (T) Double.valueOf(trimmed);
+        } else if (BigDecimal.class == targetClass || Number.class == targetClass) {
+            return (T) new BigDecimal(trimmed);
+        } else {
+            throw new IllegalArgumentException(
+                    "Cannot convert String [" + text + "] to target class [" + targetClass.getName() + "]");
+        }
+    }
+
+    private static boolean isHexNumber(String value) {
+        int index = (value.startsWith("-") ? 1 : 0);
+        return (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index));
+    }
+
+    private static BigInteger decodeBigInteger(String value) {
+        int radix = 10;
+        int index = 0;
+        boolean negative = false;
+
+        // Handle minus sign, if present.
+        if (value.startsWith("-")) {
+            negative = true;
+            index++;
+        }
+
+        // Handle radix specifier, if present.
+        if (value.startsWith("0x", index) || value.startsWith("0X", index)) {
+            index += 2;
+            radix = 16;
+        } else if (value.startsWith("#", index)) {
+            index++;
+            radix = 16;
+        } else if (value.startsWith("0", index) && value.length() > 1 + index) {
+            index++;
+            radix = 8;
+        }
+
+        BigInteger result = new BigInteger(value.substring(index), radix);
+        return (negative ? result.negate() : result);
+    }
+
+    public static String trimAllWhitespace(String str) {
+        if (StringUtils.isEmpty(str)) {
+            return str;
+        }
+
+        int len = str.length();
+        StringBuilder sb = new StringBuilder(str.length());
+        for (int i = 0; i < len; i++) {
+            char c = str.charAt(i);
+            if (!Character.isWhitespace(c)) {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    public static Object numberToBytes(Number number) {
+
+        if (number instanceof Byte) {
+            // Use default encoding.
+            return Byte.toString(number.byteValue()).getBytes();
+        } else if (number instanceof Double) {
+            return Double.toString(number.doubleValue()).getBytes();
+        } else if (number instanceof Float) {
+            return Float.toString(number.floatValue()).getBytes();
+        } else if (number instanceof Integer) {
+            return Float.toString(number.intValue()).getBytes();
+        } else if (number instanceof Long) {
+            return Long.toString(number.longValue()).getBytes();
+        } else if (number instanceof Short) {
+            return Short.toString(number.shortValue()).getBytes();
+        } else if (number instanceof BigDecimal) {
+            return BigDecimal.class.cast(number).toString().getBytes();
+        }
+
+        return number;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java
new file mode 100644
index 0000000..deacffa
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/util/ReflectUtils.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ReflectUtils {
+
+    public static Class findClass(String name, ClassLoader classLoader) throws ClassNotFoundException {
+
+        return classLoader.loadClass(name);
+    }
+
+    public static Class findClass(String name) throws ClassNotFoundException {
+
+        return findClass(Thread.currentThread().getContextClassLoader(), name);
+    }
+
+    public static Class findClassAndTryCatch(String name, ClassLoader classLoader) {
+
+        try {
+            return findClass(name, classLoader);
+        } catch (Throwable e) {
+
+        }
+        return null;
+    }
+
+    public static Class findClass(ClassLoader classLoader, String... name) throws ClassNotFoundException {
+
+        String[] names = name;
+
+        Class tmp;
+        for (String s : names) {
+            tmp = findClassAndTryCatch(s, classLoader);
+            if (tmp == null) {
+                continue;
+            } else {
+                return tmp;
+            }
+        }
+        throw new ClassNotFoundException();
+    }
+
+    public static Class findClassTryException(ClassLoader classLoader, String... name) {
+
+        try {
+            return findClass(classLoader, name);
+        } catch (Exception e) {
+
+        }
+        return null;
+    }
+
+    public static List<Method> getMethodByNameList(Class clazz, String name) {
+
+        return getMethodByNameList(clazz, name, false);
+    }
+
+    public static List<Method> getMethodByNameList(Class clazz, String name, boolean declare) {
+        // prevent duplicate method
+        Set<Method> methods = new HashSet<>();
+
+        try {
+            filterMethod(name, methods, clazz.getDeclaredMethods());
+
+        } catch (Exception e) {
+
+        }
+
+        if (!declare) {
+            return new ArrayList<>(methods);
+        }
+
+        try {
+            filterMethod(name, methods, clazz.getMethods());
+        } catch (Exception e) {
+
+        }
+
+        return new ArrayList<>(methods);
+    }
+
+    public static List<Constructor<?>> getConstructList(Class clazz) {
+        // prevent duplicate method
+        Set<Constructor<?>> methods = new HashSet<>();
+
+        try {
+            filterConstructMethod(methods, clazz.getDeclaredConstructors());
+        } catch (Exception e) {
+        }
+
+        try {
+            filterConstructMethod(methods, clazz.getConstructors());
+        } catch (Exception e) {
+
+        }
+        return new ArrayList<>(methods);
+    }
+
+    private static void filterConstructMethod(Set<Constructor<?>> methods, Constructor<?>[] declaredMethods) {
+        for (Constructor<?> constructor : declaredMethods) {
+            methods.add(constructor);
+        }
+    }
+
+    private static void filterMethod(String name, Set<Method> methodList, Method[] methods) {
+        for (Method declaredMethod : methods) {
+            if (!name.equals(declaredMethod.getName())) {
+                continue;
+            }
+            declaredMethod.setAccessible(true);
+            methodList.add(declaredMethod);
+        }
+    }
+
+    public static Method getMethodByName(Class clazz, String name) {
+
+        List<Method> methodByNameList = getMethodByNameList(clazz, name, true);
+        if (methodByNameList.isEmpty()) {
+            return null;
+        } else {
+            return methodByNameList.get(0);
+        }
+    }
+
+    public static Class findClassTryException(String... name) {
+        return findClassTryException(Thread.currentThread().getContextClassLoader(), name);
+    }
+
+    public static Object invoke(Object object, Method method, Object[] params)
+            throws InvocationTargetException, IllegalAccessException {
+        return method.invoke(object, params);
+    }
+
+    public static Object invokeAndTryCatch(Object object, Method method, Object[] params) {
+        try {
+            return invoke(object, method, params);
+        } catch (Exception e) {
+
+        }
+
+        return null;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
new file mode 100644
index 0000000..d492da9
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol
@@ -0,0 +1 @@
+rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
\ No newline at end of file
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept
new file mode 100644
index 0000000..e277b45
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept
@@ -0,0 +1,6 @@
+must-intercept=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.AddMustAttachmentIntercept
+attachment=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.AttachmentIntercept
+serialize=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.SerializeBodyIntercept
+path=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.PathVariableIntercept
+header=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.RequestHeaderIntercept
+paramparse=org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept.ParamParseIntercept
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser
new file mode 100644
index 0000000..44bcfc1
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BaseConsumerParamParser
@@ -0,0 +1,5 @@
+consumer-body=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.BodyConsumerParamParser
+consumer-header=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.HeaderConsumerParamParser
+consumer-req=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.ReqOrResConsumerParamParser
+consumer-parameter=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.ParameterConsumerParamParser
+consumer-form=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.FormConsumerParamParser
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BaseProviderParamParser b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BaseProviderParamParser
new file mode 100644
index 0000000..0f62f51
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BaseProviderParamParser
@@ -0,0 +1,4 @@
+body=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BodyProviderParamParser
+header=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.HeaderProviderParamParser
+path=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.PathProviderParamParser
+param=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.ParamProviderParamParser
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestRequestFilter b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestRequestFilter
new file mode 100644
index 0000000..030cb2d
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestRequestFilter
@@ -0,0 +1,2 @@
+resteasy=org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.ResteasyRequestContainerFilterAdapter
+invoke=org.apache.dubbo.rpc.protocol.rest.filter.ServiceInvokeRestFilter
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestResponseFilter b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestResponseFilter
new file mode 100644
index 0000000..dbafe2f
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestResponseFilter
@@ -0,0 +1 @@
+resteasy=org.apache.dubbo.rpc.protocol.rest.extension.resteasy.filter.ResteasyResponseContainerFilterAdapter
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor
new file mode 100644
index 0000000..b72ba51
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.filter.RestResponseInterceptor
@@ -0,0 +1,3 @@
+resteasy=org.apache.dubbo.rpc.protocol.rest.extension.resteasy.intercept.ResteasyWriterInterceptorAdapter
+invoke=org.apache.dubbo.rpc.protocol.rest.filter.ServiceInvokeRestResponseInterceptor
+resteasy-resStatus=org.apache.dubbo.rpc.protocol.rest.extension.resteasy.intercept.ResteasyStatusCodeInterceptor
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec
new file mode 100644
index 0000000..5a4daf8
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodec
@@ -0,0 +1,7 @@
+multiValue=org.apache.dubbo.rpc.protocol.rest.message.codec.MultiValueCodec
+text=org.apache.dubbo.rpc.protocol.rest.message.codec.TextCodec
+json=org.apache.dubbo.rpc.protocol.rest.message.codec.JsonCodec
+string=org.apache.dubbo.rpc.protocol.rest.message.codec.StringCodec
+byteArray=org.apache.dubbo.rpc.protocol.rest.message.codec.ByteArrayCodec
+xml=org.apache.dubbo.rpc.protocol.rest.message.codec.XMLCodec
+resteasyResponseCodec=org.apache.dubbo.rpc.protocol.rest.message.codec.ResteasyResponseCodec
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DataParseUtilsTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DataParseUtilsTest.java
new file mode 100644
index 0000000..864b700
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DataParseUtilsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+
+public class DataParseUtilsTest {
+    @Test
+    void testJsonConvert() throws Exception {
+
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        DataParseUtils.writeJsonContent(User.getInstance(), byteArrayOutputStream);
+
+        Assertions.assertEquals(
+                "{\"age\":18,\"id\":404,\"name\":\"dubbo\"}", new String(byteArrayOutputStream.toByteArray()));
+    }
+
+    @Test
+    void testStr() {
+        Object convert = DataParseUtils.stringTypeConvert(boolean.class, "true");
+
+        Assertions.assertEquals(Boolean.TRUE, convert);
+
+        convert = DataParseUtils.stringTypeConvert(Boolean.class, "true");
+
+        Assertions.assertEquals(Boolean.TRUE, convert);
+
+        convert = DataParseUtils.stringTypeConvert(String.class, "true");
+
+        Assertions.assertEquals("true", convert);
+
+        convert = DataParseUtils.stringTypeConvert(int.class, "1");
+
+        Assertions.assertEquals(1, convert);
+
+        convert = DataParseUtils.stringTypeConvert(Integer.class, "1");
+
+        Assertions.assertEquals(1, convert);
+    }
+
+    @Test
+    void testParseAcceptCharset() {
+        String[] parsed = DataParseUtils.parseAcceptCharset(Arrays.asList("iso-8859-1"));
+        Assertions.assertTrue(Arrays.equals(parsed, new String[] {"iso-8859-1"}));
+        parsed = DataParseUtils.parseAcceptCharset(Arrays.asList("utf-8, iso-8859-1;q=0.5"));
+        Assertions.assertTrue(Arrays.equals(parsed, new String[] {"utf-8", "iso-8859-1"}));
+        parsed = DataParseUtils.parseAcceptCharset(Arrays.asList("utf-8, iso-8859-1;q=0.5, *;q=0.1", "utf-16;q=0.5"));
+        Assertions.assertEquals("utf-8", parsed[0]);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java
new file mode 100644
index 0000000..cbf0345
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoService.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import org.jboss.resteasy.annotations.Form;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Path("/demoService")
+public interface DemoService {
+    @GET
+    @Path("/hello")
+    Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b);
+
+    @GET
+    @Path("/error")
+    @Consumes({MediaType.TEXT_PLAIN})
+    @Produces({MediaType.TEXT_PLAIN})
+    String error();
+
+    @POST
+    @Path("/say")
+    @Consumes({MediaType.TEXT_PLAIN})
+    String sayHello(String name);
+
+    @POST
+    @Path("number")
+    Long testFormBody(@FormParam("number") Long number);
+
+    boolean isCalled();
+
+    @GET
+    @Path("/primitive")
+    int primitiveInt(@QueryParam("a") int a, @QueryParam("b") int b);
+
+    @GET
+    @Path("/primitiveLong")
+    long primitiveLong(@QueryParam("a") long a, @QueryParam("b") Long b);
+
+    @GET
+    @Path("/primitiveByte")
+    long primitiveByte(@QueryParam("a") byte a, @QueryParam("b") Long b);
+
+    @POST
+    @Path("/primitiveShort")
+    long primitiveShort(@QueryParam("a") short a, @QueryParam("b") Long b, int c);
+
+    @GET
+    @Path("/request")
+    void request(DefaultFullHttpRequest defaultFullHttpRequest);
+
+    @GET
+    @Path("testMapParam")
+    @Produces({MediaType.TEXT_PLAIN})
+    @Consumes({MediaType.TEXT_PLAIN})
+    String testMapParam(@QueryParam("test") Map<String, String> params);
+
+    @GET
+    @Path("testMapHeader")
+    @Produces({MediaType.TEXT_PLAIN})
+    @Consumes({MediaType.TEXT_PLAIN})
+    String testMapHeader(@HeaderParam("test") Map<String, String> headers);
+
+    @POST
+    @Path("testMapForm")
+    @Produces({MediaType.APPLICATION_JSON})
+    @Consumes({MediaType.APPLICATION_FORM_URLENCODED})
+    List<String> testMapForm(MultivaluedMap<String, String> params);
+
+    @POST
+    @Path("/header")
+    @Consumes({MediaType.TEXT_PLAIN})
+    String header(@HeaderParam("header") String header);
+
+    @POST
+    @Path("/headerInt")
+    @Consumes({MediaType.TEXT_PLAIN})
+    int headerInt(@HeaderParam("header") int header);
+
+    @POST
+    @Path("/noStringParam")
+    @Consumes({MediaType.TEXT_PLAIN})
+    String noStringParam(@QueryParam("param") String param);
+
+    @POST
+    @Path("/noStringHeader")
+    @Consumes({MediaType.TEXT_PLAIN})
+    String noStringHeader(@HeaderParam("header") String header);
+
+    @POST
+    @Path("/noIntHeader")
+    @Consumes({MediaType.TEXT_PLAIN})
+    int noIntHeader(int header);
+
+    @POST
+    @Path("/noIntParam")
+    @Consumes({MediaType.TEXT_PLAIN})
+    int noIntParam(int header);
+
+    @POST
+    @Path("/noBodyArg")
+    @Consumes({MediaType.APPLICATION_JSON})
+    User noBodyArg(User user);
+
+    @POST
+    @Path("/list")
+    List<User> list(List<User> users);
+
+    @POST
+    @Path("/set")
+    Set<User> set(Set<User> users);
+
+    @POST
+    @Path("/array")
+    User[] array(User[] users);
+
+    @POST
+    @Path("/stringMap")
+    Map<String, User> stringMap(Map<String, User> userMap);
+
+    @POST
+    @Path("/map")
+    Map<User, User> userMap(Map<User, User> userMap);
+
+    @POST
+    @Path("/formBody")
+    User formBody(@Form User user);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java
new file mode 100644
index 0000000..8d3f6aa
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/DemoServiceImpl.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import org.apache.dubbo.rpc.RpcContext;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Path("/demoService")
+public class DemoServiceImpl implements DemoService {
+    private static Map<String, Object> context;
+    private boolean called;
+
+    @POST
+    @Path("/say")
+    @Consumes({MediaType.TEXT_PLAIN})
+    @Override
+    public String sayHello(String name) {
+        called = true;
+        return "Hello, " + name;
+    }
+
+    @Override
+    public Long testFormBody(Long number) {
+        return number;
+    }
+
+    public boolean isCalled() {
+        return called;
+    }
+
+    @Override
+    public int primitiveInt(int a, int b) {
+        return a + b;
+    }
+
+    @Override
+    public long primitiveLong(long a, Long b) {
+        return a + b;
+    }
+
+    @Override
+    public long primitiveByte(byte a, Long b) {
+        return a + b;
+    }
+
+    @Override
+    public long primitiveShort(short a, Long b, int c) {
+        return a + b;
+    }
+
+    @Override
+    public void request(DefaultFullHttpRequest defaultFullHttpRequest) {}
+
+    @Override
+    public String testMapParam(Map<String, String> params) {
+        return params.get("param");
+    }
+
+    @Override
+    public String testMapHeader(Map<String, String> headers) {
+        return headers.get("header");
+    }
+
+    @Override
+    public List<String> testMapForm(MultivaluedMap<String, String> params) {
+        return params.get("form");
+    }
+
+    @Override
+    public String header(String header) {
+        return header;
+    }
+
+    @Override
+    public int headerInt(int header) {
+        return header;
+    }
+
+    @Override
+    public String noStringParam(String param) {
+        return param;
+    }
+
+    @Override
+    public String noStringHeader(String header) {
+        return header;
+    }
+
+    @POST
+    @Path("/noIntHeader")
+    @Consumes({MediaType.TEXT_PLAIN})
+    @Override
+    public int noIntHeader(@HeaderParam("header") int header) {
+        return header;
+    }
+
+    @POST
+    @Path("/noIntParam")
+    @Consumes({MediaType.TEXT_PLAIN})
+    @Override
+    public int noIntParam(@QueryParam("header") int header) {
+        return header;
+    }
+
+    @Override
+    public User noBodyArg(User user) {
+        return user;
+    }
+
+    @GET
+    @Path("/hello")
+    @Override
+    public Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b) {
+        context = RpcContext.getServerAttachment().getObjectAttachments();
+        return a + b;
+    }
+
+    @GET
+    @Path("/error")
+    @Override
+    public String error() {
+        throw new RuntimeException("test error");
+    }
+
+    public static Map<String, Object> getAttachments() {
+        return context;
+    }
+
+    @Override
+    public List<User> list(List<User> users) {
+        return users;
+    }
+
+    @Override
+    public Set<User> set(Set<User> users) {
+        return users;
+    }
+
+    @Override
+    public User[] array(User[] users) {
+        return users;
+    }
+
+    @Override
+    public Map<String, User> stringMap(Map<String, User> userMap) {
+        return userMap;
+    }
+
+    @Override
+    public Map<User, User> userMap(Map<User, User> userMap) {
+        return userMap;
+    }
+
+    @Override
+    public User formBody(User user) {
+        user.setName("formBody");
+        return user;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ExceptionMapperTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ExceptionMapperTest.java
new file mode 100644
index 0000000..5126b3a
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ExceptionMapperTest.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.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandlerResult;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ExceptionMapperTest {
+    private final ExceptionMapper exceptionMapper = new ExceptionMapper();
+
+    @Test
+    void testRegister() {
+
+        exceptionMapper.registerMapper(TestExceptionHandler.class);
+
+        ExceptionHandlerResult result = exceptionMapper.exceptionToResult(new RuntimeException("test"));
+
+        Assertions.assertEquals("test", result.getEntity());
+    }
+
+    @Test
+    void testExceptionNoArgConstruct() {
+
+        Assertions.assertThrows(RuntimeException.class, () -> {
+            exceptionMapper.registerMapper(TestExceptionHandlerException.class);
+        });
+    }
+
+    public class TestExceptionHandler implements ExceptionHandler<RuntimeException> {
+
+        @Override
+        public Object result(RuntimeException exception) {
+            return exception.getMessage();
+        }
+    }
+
+    class TestExceptionHandlerException implements ExceptionHandler<RuntimeException> {
+
+        @Override
+        public Object result(RuntimeException exception) {
+            return exception.getMessage();
+        }
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/HttpMessageCodecManagerTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/HttpMessageCodecManagerTest.java
new file mode 100644
index 0000000..5ffa348
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/HttpMessageCodecManagerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager;
+import org.apache.dubbo.rpc.protocol.rest.message.codec.XMLCodec;
+import org.apache.dubbo.rpc.protocol.rest.pair.MessageCodecResultPair;
+import org.apache.dubbo.rpc.protocol.rest.rest.RegistrationResult;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+
+public class HttpMessageCodecManagerTest {
+
+    @Test
+    void testCodec() throws Exception {
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+        RegistrationResult registrationResult = new RegistrationResult();
+        registrationResult.setId(1l);
+        HttpMessageCodecManager.httpMessageEncode(
+                byteArrayOutputStream, registrationResult, null, MediaType.TEXT_XML, null);
+
+        Object o = HttpMessageCodecManager.httpMessageDecode(
+                byteArrayOutputStream.toByteArray(),
+                RegistrationResult.class,
+                RegistrationResult.class,
+                MediaType.TEXT_XML);
+
+        Assertions.assertEquals(registrationResult, o);
+
+        byteArrayOutputStream = new ByteArrayOutputStream();
+        MessageCodecResultPair messageCodecResultPair = HttpMessageCodecManager.httpMessageEncode(
+                byteArrayOutputStream, null, null, null, RegistrationResult.class);
+
+        MediaType mediaType = messageCodecResultPair.getMediaType();
+
+        Assertions.assertEquals(MediaType.APPLICATION_JSON_VALUE, mediaType);
+
+        XMLCodec xmlCodec = new XMLCodec();
+
+        Assertions.assertEquals(false, xmlCodec.typeSupport(null));
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java
new file mode 100644
index 0000000..81b3f3c
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/JaxrsRestProtocolTest.java
@@ -0,0 +1,801 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.metadata.extension.rest.api.PathMatcher;
+import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
+import org.apache.dubbo.metadata.extension.rest.api.ServiceRestMetadata;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.protocol.rest.annotation.metadata.MetadataResolver;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.exception.DoublePathCheckException;
+import org.apache.dubbo.rpc.protocol.rest.exception.ResteasyExceptionMapper;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper;
+import org.apache.dubbo.rpc.protocol.rest.filter.TraceRequestAndResponseFilter;
+import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService;
+import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestServiceImpl;
+import org.apache.dubbo.rpc.protocol.rest.rest.HttpMethodService;
+import org.apache.dubbo.rpc.protocol.rest.rest.HttpMethodServiceImpl;
+import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoForTestException;
+import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService;
+import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoServiceImpl;
+import org.apache.dubbo.rpc.protocol.rest.rest.TestGetInvokerService;
+import org.apache.dubbo.rpc.protocol.rest.rest.TestGetInvokerServiceImpl;
+import org.hamcrest.CoreMatchers;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.dubbo.remoting.Constants.SERVER_KEY;
+import static org.apache.dubbo.rpc.protocol.rest.Constants.EXTENSION_KEY;
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+class JaxrsRestProtocolTest {
+    private final Protocol protocol =
+            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest");
+    private final ProxyFactory proxy =
+            ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    private final int availablePort = NetUtils.getAvailablePort();
+    private final URL exportUrl = URL.valueOf(
+            "rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.DemoService");
+    private final ModuleServiceRepository repository =
+            ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
+    private final ExceptionMapper exceptionMapper = new ExceptionMapper();
+    private static final String SERVER = "netty4";
+
+    @AfterEach
+    public void tearDown() {
+        protocol.destroy();
+        FrameworkModel.destroyAll();
+    }
+
+    @Test
+    void testRestProtocol() {
+        URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort()
+                + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService");
+
+        DemoServiceImpl server = new DemoServiceImpl();
+
+        url = this.registerProvider(url, server, DemoService.class);
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, url));
+        Invoker<DemoService> invoker = protocol.refer(DemoService.class, url);
+        Assertions.assertFalse(server.isCalled());
+
+        DemoService client = proxy.getProxy(invoker);
+        String result = client.sayHello("haha");
+        Assertions.assertTrue(server.isCalled());
+        Assertions.assertEquals("Hello, haha", result);
+
+        String header = client.header("header test");
+        Assertions.assertEquals("header test", header);
+
+        Assertions.assertEquals(1, client.headerInt(1));
+        invoker.destroy();
+        exporter.unexport();
+    }
+
+    @Test
+    void testAnotherUserRestProtocolByDifferentRestClient() {
+        testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.OK_HTTP);
+        testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.APACHE_HTTP_CLIENT);
+        testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.URL_CONNECTION);
+    }
+
+    void testAnotherUserRestProtocol(String restClient) {
+        URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort()
+                + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService&"
+                + org.apache.dubbo.remoting.Constants.CLIENT_KEY + "=" + restClient);
+
+        AnotherUserRestServiceImpl server = new AnotherUserRestServiceImpl();
+
+        url = this.registerProvider(url, server, DemoService.class);
+
+        Exporter<AnotherUserRestService> exporter =
+                protocol.export(proxy.getInvoker(server, AnotherUserRestService.class, url));
+        Invoker<AnotherUserRestService> invoker = protocol.refer(AnotherUserRestService.class, url);
+
+        AnotherUserRestService client = proxy.getProxy(invoker);
+        User result = client.getUser(123l);
+
+        Assertions.assertEquals(123l, result.getId());
+
+        result.setName("dubbo");
+        Assertions.assertEquals(123l, client.registerUser(result).getId());
+
+        Assertions.assertEquals("context", client.getContext());
+
+        byte[] bytes = {1, 2, 3, 4};
+        Assertions.assertTrue(Arrays.equals(bytes, client.bytes(bytes)));
+
+        Assertions.assertEquals(1l, client.number(1l));
+
+        HashMap<String, String> map = new HashMap<>();
+        map.put("headers", "h1");
+        Assertions.assertEquals("h1", client.headerMap(map));
+        Assertions.assertEquals(null, client.headerMap(null));
+
+        invoker.destroy();
+        exporter.unexport();
+    }
+
+    @Test
+    void testRestProtocolWithContextPath() {
+        DemoServiceImpl server = new DemoServiceImpl();
+        Assertions.assertFalse(server.isCalled());
+        int port = NetUtils.getAvailablePort();
+        URL url = URL.valueOf("rest://127.0.0.1:" + port
+                + "/a/b/c?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService");
+
+        url = this.registerProvider(url, server, DemoService.class);
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, url));
+
+        url = URL.valueOf("rest://127.0.0.1:" + port
+                + "/a/b/c/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.DemoService");
+        Invoker<DemoService> invoker = protocol.refer(DemoService.class, url);
+        DemoService client = proxy.getProxy(invoker);
+        String result = client.sayHello("haha");
+        Assertions.assertTrue(server.isCalled());
+        Assertions.assertEquals("Hello, haha", result);
+        invoker.destroy();
+        exporter.unexport();
+    }
+
+    @Test
+    void testExport() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        RpcContext.getClientAttachment().setAttachment("timeout", "20000");
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, url));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, url));
+
+        Integer echoString = demoService.hello(1, 2);
+        assertThat(echoString, is(3));
+
+        exporter.unexport();
+    }
+
+    @Test
+    void testNettyServer() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, SERVER);
+        Exporter<DemoService> exporter =
+                protocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Integer echoString = demoService.hello(10, 10);
+        assertThat(echoString, is(20));
+
+        exporter.unexport();
+    }
+
+    @Disabled
+    @Test
+    void testServletWithoutWebConfig() {
+        Assertions.assertThrows(RpcException.class, () -> {
+            DemoService server = new DemoServiceImpl();
+
+            URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+            URL servletUrl = url.addParameter(SERVER_KEY, "servlet");
+
+            protocol.export(proxy.getInvoker(server, DemoService.class, servletUrl));
+        });
+    }
+
+    @Test
+    void testErrorHandler() {
+        Assertions.assertThrows(RpcException.class, () -> {
+            exceptionMapper.unRegisterMapper(RuntimeException.class);
+            DemoService server = new DemoServiceImpl();
+
+            URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+            URL nettyUrl = url.addParameter(SERVER_KEY, SERVER);
+            Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+            DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+            demoService.error();
+        });
+    }
+
+    @Test
+    void testInvoke() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, url));
+
+        RpcInvocation rpcInvocation = new RpcInvocation(
+                "hello", DemoService.class.getName(), "", new Class[] {Integer.class, Integer.class}, new Integer[] {
+                    2, 3
+                });
+
+        Result result = exporter.getInvoker().invoke(rpcInvocation);
+        assertThat(result.getValue(), CoreMatchers.<Object>is(5));
+    }
+
+    @Test
+    void testFilter() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
+                .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter");
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Integer result = demoService.hello(1, 2);
+
+        assertThat(result, is(3));
+
+        exporter.unexport();
+    }
+
+    @Disabled
+    @Test
+    void testRpcContextFilter() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        // use RpcContextFilter
+        URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
+                .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.RpcContextFilter");
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        // make sure null and base64 encoded string can work
+        RpcContext.getClientAttachment().setAttachment("key1", null);
+        RpcContext.getClientAttachment().setAttachment("key2", "value");
+        RpcContext.getClientAttachment().setAttachment("key3", "=value");
+        RpcContext.getClientAttachment().setAttachment("key4", "YWJjZGVmCg==");
+        RpcContext.getClientAttachment().setAttachment("key5", "val=ue");
+        Integer result = demoService.hello(1, 2);
+
+        assertThat(result, is(3));
+
+        Map<String, Object> attachment = DemoServiceImpl.getAttachments();
+        assertThat(attachment.get("key1"), nullValue());
+        assertThat(attachment.get("key2"), equalTo("value"));
+        assertThat(attachment.get("key3"), equalTo("=value"));
+        assertThat(attachment.get("key4"), equalTo("YWJjZGVmCg=="));
+        assertThat(attachment.get("key5"), equalTo("val=ue"));
+
+        exporter.unexport();
+    }
+
+    @Disabled
+    @Test
+    void testRegFail() {
+        Assertions.assertThrows(RuntimeException.class, () -> {
+            DemoService server = new DemoServiceImpl();
+
+            URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+            URL nettyUrl = url.addParameter(EXTENSION_KEY, "com.not.existing.Filter");
+            protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+        });
+    }
+
+    @Test
+    void testDefaultPort() {
+        assertThat(protocol.getDefaultPort(), is(80));
+    }
+
+    @Test
+    void testExceptionMapper() {
+
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL exceptionUrl = url.addParameter(EXTENSION_KEY, TestExceptionMapper.class.getName());
+
+        protocol.export(proxy.getInvoker(server, DemoService.class, exceptionUrl));
+
+        DemoService referDemoService = this.proxy.getProxy(protocol.refer(DemoService.class, exceptionUrl));
+
+        Assertions.assertEquals("test-exception", referDemoService.error());
+    }
+
+    @Test
+    void testRestExceptionMapper() {
+
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL exceptionUrl = url.addParameter(EXTENSION_KEY, ResteasyExceptionMapper.class.getName());
+
+        protocol.export(proxy.getInvoker(server, DemoService.class, exceptionUrl));
+
+        DemoService referDemoService = this.proxy.getProxy(protocol.refer(DemoService.class, exceptionUrl));
+
+        Assertions.assertEquals("test-exception", referDemoService.error());
+    }
+
+    @Test
+    void testFormConsumerParser() {
+        DemoService server = new DemoServiceImpl();
+        URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Long number = demoService.testFormBody(18l);
+        Assertions.assertEquals(18l, number);
+
+        exporter.unexport();
+    }
+
+    @Test
+    void test404() {
+        Assertions.assertThrows(RpcException.class, () -> {
+            DemoService server = new DemoServiceImpl();
+            URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
+
+            Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+            URL referUrl = URL.valueOf("rest://127.0.0.1:" + availablePort
+                    + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoForTestException");
+
+            RestDemoForTestException restDemoForTestException =
+                    this.proxy.getProxy(protocol.refer(RestDemoForTestException.class, referUrl));
+
+            restDemoForTestException.test404();
+
+            exporter.unexport();
+        });
+    }
+
+    @Test
+    void test400() {
+        Assertions.assertThrows(RpcException.class, () -> {
+            DemoService server = new DemoServiceImpl();
+            URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
+
+            Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+            URL referUrl = URL.valueOf("rest://127.0.0.1:" + availablePort
+                    + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoForTestException");
+
+            RestDemoForTestException restDemoForTestException =
+                    this.proxy.getProxy(protocol.refer(RestDemoForTestException.class, referUrl));
+
+            restDemoForTestException.test400("abc", "edf");
+
+            exporter.unexport();
+        });
+    }
+
+    @Test
+    void testPrimitive() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
+                .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter");
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Integer result = demoService.primitiveInt(1, 2);
+        Long resultLong = demoService.primitiveLong(1, 2l);
+        long resultByte = demoService.primitiveByte((byte) 1, 2l);
+        long resultShort = demoService.primitiveShort((short) 1, 2l, 1);
+
+        assertThat(result, is(3));
+        assertThat(resultShort, is(3l));
+        assertThat(resultLong, is(3l));
+        assertThat(resultByte, is(3l));
+
+        exporter.unexport();
+    }
+
+    @Test
+    void testDoubleCheckException() {
+
+        Assertions.assertThrows(DoublePathCheckException.class, () -> {
+            DemoService server = new DemoServiceImpl();
+
+            Invoker<DemoService> invoker = proxy.getInvoker(server, DemoService.class, exportUrl);
+
+            PathAndInvokerMapper pathAndInvokerMapper = new PathAndInvokerMapper();
+
+            ServiceRestMetadata serviceRestMetadata =
+                    MetadataResolver.resolveConsumerServiceMetadata(DemoService.class, exportUrl, "");
+
+            Map<PathMatcher, RestMethodMetadata> pathContainPathVariableToServiceMap =
+                    serviceRestMetadata.getPathUnContainPathVariableToServiceMap();
+
+            Invoker<TestInterface> invokerNew =
+                    proxy.getInvoker(new TestInterface() {}, TestInterface.class, exportUrl);
+
+            pathAndInvokerMapper.addPathAndInvoker(pathContainPathVariableToServiceMap, invoker);
+            pathAndInvokerMapper.addPathAndInvoker(pathContainPathVariableToServiceMap, invokerNew);
+        });
+    }
+
+    public static interface TestInterface {}
+
+    @Test
+    void testMapParam() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
+                .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter");
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Map<String, String> params = new HashMap<>();
+        params.put("param", "P1");
+        ;
+
+        Map<String, String> headers = new HashMap<>();
+        headers.put("header", "H1");
+
+        Assertions.assertEquals("P1", demoService.testMapParam(params));
+        Assertions.assertEquals("H1", demoService.testMapHeader(headers));
+
+        MultivaluedMapImpl<String, String> forms = new MultivaluedMapImpl<>();
+        forms.put("form", Arrays.asList("F1"));
+
+        Assertions.assertEquals(Arrays.asList("F1"), demoService.testMapForm(forms));
+        exporter.unexport();
+    }
+
+    @Test
+    void testNoArgParam() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, SERVER)
+                .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.support.LoggingFilter");
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Assertions.assertEquals(null, demoService.noStringHeader(null));
+        Assertions.assertEquals(null, demoService.noStringParam(null));
+        Assertions.assertThrows(RpcException.class, () -> {
+            demoService.noIntHeader(1);
+        });
+
+        Assertions.assertThrows(RpcException.class, () -> {
+            demoService.noIntParam(1);
+        });
+
+        Assertions.assertEquals(null, demoService.noBodyArg(null));
+        exporter.unexport();
+    }
+
+    @Test
+    void testToken() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(RestConstant.TOKEN_KEY, "TOKEN");
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Assertions.assertEquals("Hello, hello", demoService.sayHello("hello"));
+        exporter.unexport();
+    }
+
+    @Test
+    void testHttpMethods() {
+        testHttpMethod(org.apache.dubbo.remoting.Constants.OK_HTTP);
+        testHttpMethod(org.apache.dubbo.remoting.Constants.APACHE_HTTP_CLIENT);
+        testHttpMethod(org.apache.dubbo.remoting.Constants.URL_CONNECTION);
+    }
+
+    void testHttpMethod(String restClient) {
+        HttpMethodService server = new HttpMethodServiceImpl();
+
+        URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort()
+                + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.HttpMethodService&"
+                + org.apache.dubbo.remoting.Constants.CLIENT_KEY + "=" + restClient);
+        url = this.registerProvider(url, server, HttpMethodService.class);
+        Exporter<HttpMethodService> exporter = protocol.export(proxy.getInvoker(server, HttpMethodService.class, url));
+
+        HttpMethodService demoService = this.proxy.getProxy(protocol.refer(HttpMethodService.class, url));
+
+        String expect = "hello";
+        Assertions.assertEquals(null, demoService.sayHelloHead());
+        Assertions.assertEquals(expect, demoService.sayHelloDelete("hello"));
+        Assertions.assertEquals(expect, demoService.sayHelloGet("hello"));
+        Assertions.assertEquals(expect, demoService.sayHelloOptions("hello"));
+        //        Assertions.assertEquals(expect, demoService.sayHelloPatch("hello"));
+        Assertions.assertEquals(expect, demoService.sayHelloPost("hello"));
+        Assertions.assertEquals(expect, demoService.sayHelloPut("hello"));
+        exporter.unexport();
+    }
+
+    public static class TestExceptionMapper implements ExceptionHandler<RuntimeException> {
+
+        @Override
+        public String result(RuntimeException e) {
+            return "test-exception";
+        }
+    }
+
+    @Test
+    void test405() {
+        int availablePort = NetUtils.getAvailablePort();
+        URL url = URL.valueOf("rest://127.0.0.1:" + availablePort
+                + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService&");
+
+        RestDemoServiceImpl server = new RestDemoServiceImpl();
+
+        url = this.registerProvider(url, server, RestDemoService.class);
+
+        Exporter<RestDemoService> exporter = protocol.export(proxy.getInvoker(server, RestDemoService.class, url));
+
+        URL consumer = URL.valueOf("rest://127.0.0.1:" + availablePort
+                + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoForTestException&");
+
+        consumer = this.registerProvider(consumer, server, RestDemoForTestException.class);
+
+        Invoker<RestDemoForTestException> invoker = protocol.refer(RestDemoForTestException.class, consumer);
+
+        RestDemoForTestException client = proxy.getProxy(invoker);
+
+        Assertions.assertThrows(RpcException.class, () -> {
+            client.testMethodDisallowed("aaa");
+        });
+
+        invoker.destroy();
+        exporter.unexport();
+    }
+
+    @Test
+    void testGetInvoker() {
+        Assertions.assertDoesNotThrow(() -> {
+            URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort
+                    + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.rest.TestGetInvokerService");
+
+            TestGetInvokerService server = new TestGetInvokerServiceImpl();
+
+            URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+            Exporter<TestGetInvokerService> exporter =
+                    protocol.export(proxy.getInvoker(server, TestGetInvokerService.class, url));
+
+            TestGetInvokerService invokerService =
+                    this.proxy.getProxy(protocol.refer(TestGetInvokerService.class, url));
+
+            String invoker = invokerService.getInvoker();
+            Assertions.assertEquals("success", invoker);
+
+            exporter.unexport();
+        });
+    }
+
+    @Test
+    void testContainerRequestFilter() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
+                .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.filter.TestContainerRequestFilter");
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Assertions.assertEquals("return-success", demoService.sayHello("hello"));
+        exporter.unexport();
+    }
+
+    @Test
+    void testIntercept() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
+                .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.intercept.DynamicTraceInterceptor");
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Assertions.assertEquals("intercept", demoService.sayHello("hello"));
+        exporter.unexport();
+    }
+
+    @Test
+    void testResponseFilter() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
+                .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.filter.TraceFilter");
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Assertions.assertEquals("response-filter", demoService.sayHello("hello"));
+        exporter.unexport();
+    }
+
+    @Test
+    void testCollectionResult() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, "netty");
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Assertions.assertEquals(
+                User.getInstance(),
+                demoService.list(Arrays.asList(User.getInstance())).get(0));
+
+        HashSet<User> objects = new HashSet<>();
+        objects.add(User.getInstance());
+        Assertions.assertEquals(User.getInstance(), new ArrayList<>(demoService.set(objects)).get(0));
+
+        Assertions.assertEquals(User.getInstance(), demoService.array(objects.toArray(new User[0]))[0]);
+
+        Map<String, User> map = new HashMap<>();
+        map.put("map", User.getInstance());
+        Assertions.assertEquals(User.getInstance(), demoService.stringMap(map).get("map"));
+
+        Map<User, User> maps = new HashMap<>();
+        maps.put(User.getInstance(), User.getInstance());
+        Assertions.assertEquals(User.getInstance(), demoService.userMap(maps).get(User.getInstance()));
+        exporter.unexport();
+    }
+
+    @Test
+    void testReExport() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, "netty");
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+        nettyUrl = url.addParameter("SERVER_KEY", "netty");
+        exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        exporter.unexport();
+    }
+
+    @Test
+    void testBody() {
+
+        Assertions.assertThrowsExactly(RpcException.class, () -> {
+            DemoService server = new DemoServiceImpl();
+
+            URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+            URL nettyUrl = url.addParameter(org.apache.dubbo.remoting.Constants.PAYLOAD_KEY, 1024);
+
+            Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+            DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+            List<User> users = new ArrayList<>();
+            for (int i = 0; i < 10000; i++) {
+                users.add(User.getInstance());
+            }
+
+            demoService.list(users);
+
+            exporter.unexport();
+        });
+    }
+
+    @Test
+    void testRequestAndResponseFilter() {
+        DemoService server = new DemoServiceImpl();
+
+        URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort
+                + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.DemoService&extension="
+                + TraceRequestAndResponseFilter.class.getName());
+
+        URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        Assertions.assertEquals("header-result", demoService.sayHello("hello"));
+        exporter.unexport();
+    }
+
+    @Test
+    void testFormBody() {
+        DemoService server = new DemoServiceImpl();
+
+        URL url = this.registerProvider(exportUrl, server, DemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, SERVER);
+
+        Exporter<DemoService> exporter = protocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
+
+        DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
+
+        User user = demoService.formBody(User.getInstance());
+
+        Assertions.assertEquals("formBody", user.getName());
+        exporter.unexport();
+    }
+
+    private URL registerProvider(URL url, Object impl, Class<?> interfaceClass) {
+        ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
+        ProviderModel providerModel = new ProviderModel(url.getServiceKey(), impl, serviceDescriptor, null, null);
+        repository.registerProvider(providerModel);
+        return url.setServiceModel(providerModel);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/MediaTypeUtilTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/MediaTypeUtilTest.java
new file mode 100644
index 0000000..fbce017
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/MediaTypeUtilTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.metadata.extension.rest.api.media.MediaType;
+import org.apache.dubbo.rpc.protocol.rest.exception.UnSupportContentTypeException;
+import org.apache.dubbo.rpc.protocol.rest.util.MediaTypeUtil;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class MediaTypeUtilTest {
+
+    @Test
+    void testException() {
+
+        Assertions.assertThrows(UnSupportContentTypeException.class, () -> {
+            MediaTypeUtil.convertMediaType(null, "aaaaa");
+        });
+    }
+
+    @Test
+    void testConvertMediaType() {
+        MediaType mediaType =
+                MediaTypeUtil.convertMediaType(null, new String[] {MediaType.APPLICATION_JSON_VALUE.value});
+
+        Assertions.assertEquals(MediaType.APPLICATION_JSON_VALUE, mediaType);
+
+        mediaType = MediaTypeUtil.convertMediaType(int.class, null);
+
+        Assertions.assertEquals(MediaType.TEXT_PLAIN, mediaType);
+
+        mediaType = MediaTypeUtil.convertMediaType(null, new String[] {MediaType.ALL_VALUE.value});
+
+        Assertions.assertEquals(MediaType.APPLICATION_JSON_VALUE, mediaType);
+
+        mediaType = MediaTypeUtil.convertMediaType(String.class, new String[] {MediaType.TEXT_XML.value});
+
+        Assertions.assertEquals(MediaType.TEXT_XML, mediaType);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NettyRequestFacadeTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NettyRequestFacadeTest.java
new file mode 100644
index 0000000..1a9951b
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NettyRequestFacadeTest.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpVersion;
+import org.apache.dubbo.rpc.protocol.rest.request.NettyRequestFacade;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class NettyRequestFacadeTest {
+
+    @Test
+    void testMethod() {
+
+        String uri = "/a/b?c=c&d=d";
+        DefaultFullHttpRequest defaultFullHttpRequest =
+                new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
+
+        defaultFullHttpRequest.headers().add("h1", "a");
+        defaultFullHttpRequest.headers().add("h1", "b");
+        defaultFullHttpRequest.headers().add("h2", "c");
+        NettyRequestFacade nettyRequestFacade = new NettyRequestFacade(defaultFullHttpRequest, null);
+
+        Assertions.assertArrayEquals(new String[] {"c"}, nettyRequestFacade.getParameterValues("c"));
+        Enumeration<String> parameterNames = nettyRequestFacade.getParameterNames();
+
+        List<String> names = new ArrayList<>();
+        while (parameterNames.hasMoreElements()) {
+
+            names.add(parameterNames.nextElement());
+        }
+
+        Assertions.assertArrayEquals(Arrays.asList("c", "d").toArray(), names.toArray());
+
+        Enumeration<String> headerNames = nettyRequestFacade.getHeaderNames();
+
+        List<String> heads = new ArrayList<>();
+        while (headerNames.hasMoreElements()) {
+
+            heads.add(headerNames.nextElement());
+        }
+
+        Assertions.assertArrayEquals(Arrays.asList("h1", "h2").toArray(), heads.toArray());
+
+        Assertions.assertEquals(uri, nettyRequestFacade.getRequestURI());
+
+        Assertions.assertEquals("c", nettyRequestFacade.getHeader("h2"));
+
+        Assertions.assertEquals("d", nettyRequestFacade.getParameter("d"));
+
+        Assertions.assertEquals("/a/b", nettyRequestFacade.getPath());
+
+        Assertions.assertEquals(null, nettyRequestFacade.getParameterValues("e"));
+
+        Assertions.assertArrayEquals(new String[] {"d"}, nettyRequestFacade.getParameterValues("d"));
+
+        Enumeration<String> h1s = nettyRequestFacade.getHeaders("h1");
+
+        heads = new ArrayList<>();
+
+        while (h1s.hasMoreElements()) {
+
+            heads.add(h1s.nextElement());
+        }
+
+        Assertions.assertArrayEquals(new String[] {"a", "b"}, heads.toArray());
+
+        Map<String, String[]> parameterMap = nettyRequestFacade.getParameterMap();
+
+        Assertions.assertArrayEquals(new String[] {"c"}, parameterMap.get("c"));
+        Assertions.assertArrayEquals(new String[] {"d"}, parameterMap.get("d"));
+
+        Assertions.assertEquals("GET", nettyRequestFacade.getMethod());
+    }
+
+    @Test
+    void testChineseDecoding() {
+        String uri = "/hello/world?name=%E6%9D%8E%E5%BC%BA&age=18";
+        DefaultFullHttpRequest defaultFullHttpRequest =
+                new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
+        defaultFullHttpRequest.headers().add("Accept-Charset", "utf-8, iso-8859-1;q=0.5, *;q=0.1");
+        defaultFullHttpRequest.headers().add("Accept-Charset", "utf-16;q=0.3");
+
+        NettyRequestFacade nettyRequestFacade = new NettyRequestFacade(defaultFullHttpRequest, null);
+        assertThat(nettyRequestFacade.getPath(), is("/hello/world"));
+        assertThat(nettyRequestFacade.getParameter("name"), is("李强"));
+        assertThat(nettyRequestFacade.getParameter("age"), is("18"));
+
+        // Applying the decode method to the URI is acceptable, even if the URI is not encoded.
+        uri = "/hello/world?name=lily&age=18";
+        defaultFullHttpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
+
+        nettyRequestFacade = new NettyRequestFacade(defaultFullHttpRequest, null);
+        assertThat(nettyRequestFacade.getPath(), is("/hello/world"));
+        assertThat(nettyRequestFacade.getParameter("name"), is("lily"));
+        assertThat(nettyRequestFacade.getParameter("age"), is("18"));
+
+        // When using URLConnectionRestClient, the URI won't be encoded, but it's still acceptable.
+        uri = "/hello/world?name=李强&age=18";
+        defaultFullHttpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
+
+        nettyRequestFacade = new NettyRequestFacade(defaultFullHttpRequest, null);
+        assertThat(nettyRequestFacade.getPath(), is("/hello/world"));
+        assertThat(nettyRequestFacade.getParameter("name"), is("李强"));
+        assertThat(nettyRequestFacade.getParameter("age"), is("18"));
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NumberUtilsTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NumberUtilsTest.java
new file mode 100644
index 0000000..a17539d
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/NumberUtilsTest.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
+import org.apache.dubbo.rpc.protocol.rest.util.NumberUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+
+public class NumberUtilsTest {
+    void testParseNumber(String numberStr) {
+        int integer = NumberUtils.parseNumber(numberStr, Integer.class);
+
+        Assertions.assertEquals(1, integer);
+
+        integer = NumberUtils.parseNumber(numberStr, int.class);
+
+        Assertions.assertEquals(1, integer);
+
+        long a = NumberUtils.parseNumber(numberStr, Long.class);
+
+        Assertions.assertEquals(1, a);
+
+        a = NumberUtils.parseNumber(numberStr, long.class);
+
+        Assertions.assertEquals(1, a);
+
+        byte b = NumberUtils.parseNumber(numberStr, Byte.class);
+
+        Assertions.assertEquals(1, b);
+
+        b = NumberUtils.parseNumber(numberStr, byte.class);
+
+        Assertions.assertEquals(1, b);
+
+        short c = NumberUtils.parseNumber(numberStr, Short.class);
+
+        Assertions.assertEquals(1, c);
+
+        c = NumberUtils.parseNumber(numberStr, short.class);
+
+        Assertions.assertEquals(1, c);
+
+        BigInteger f = NumberUtils.parseNumber(numberStr, BigInteger.class);
+
+        Assertions.assertEquals(1, f.intValue());
+    }
+
+    @Test
+    void testNumberToBytes() {
+        byte[] except = {49};
+        byte[] bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Integer.valueOf("1"));
+
+        Assertions.assertArrayEquals(except, bytes);
+
+        bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(NumberUtils.parseNumber("1", int.class));
+
+        Assertions.assertArrayEquals(except, bytes);
+
+        except = new byte[] {49};
+        bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Byte.valueOf("1"));
+
+        Assertions.assertArrayEquals(except, bytes);
+
+        except = new byte[] {49};
+        bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Short.valueOf("1"));
+
+        Assertions.assertArrayEquals(except, bytes);
+
+        except = new byte[] {49};
+        bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Long.valueOf("1"));
+
+        Assertions.assertArrayEquals(except, bytes);
+
+        except = new byte[] {49};
+        bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(BigDecimal.valueOf(1));
+
+        Assertions.assertArrayEquals(except, bytes);
+
+        except = new byte[] {116, 114, 117, 101};
+        bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(Boolean.TRUE);
+
+        Assertions.assertArrayEquals(except, bytes);
+
+        except = new byte[] {116, 114, 117, 101};
+        bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(true);
+
+        Assertions.assertArrayEquals(except, bytes);
+
+        bytes = (byte[]) DataParseUtils.objectTextConvertToByteArray(User.getInstance());
+
+        except = User.getInstance().toString().getBytes(StandardCharsets.UTF_8);
+        Assertions.assertArrayEquals(except, bytes);
+    }
+
+    @Test
+    void testNumberStr() {
+        testParseNumber("1");
+        testParseNumber("0X0001");
+        testParseNumber("0x0001");
+        testParseNumber("#1");
+    }
+
+    @Test
+    void testUnHexNumber() {
+        String numberStr = "1";
+        double e = NumberUtils.parseNumber(numberStr, Double.class);
+
+        Assertions.assertEquals(1.0, e);
+
+        e = NumberUtils.parseNumber(numberStr, double.class);
+
+        Assertions.assertEquals(1.0, e);
+
+        BigDecimal g = NumberUtils.parseNumber(numberStr, BigDecimal.class);
+
+        Assertions.assertEquals(1, g.intValue());
+
+        int integer = NumberUtils.parseNumber(numberStr, int.class);
+
+        Assertions.assertEquals(1, integer);
+    }
+
+    @Test
+    void testNegative() {
+
+        Integer integer = NumberUtils.parseNumber("-0X1", int.class);
+        Assertions.assertEquals(-1, integer);
+
+        BigInteger bigInteger = NumberUtils.parseNumber("-0X1", BigInteger.class);
+        Assertions.assertEquals(-1, bigInteger.intValue());
+    }
+
+    @Test
+    void testException() {
+
+        Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> {
+            Object abc = NumberUtils.parseNumber("abc", Object.class);
+        });
+
+        Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> {
+            Object abc = NumberUtils.parseNumber(null, Object.class);
+        });
+
+        Assertions.assertThrowsExactly(IllegalArgumentException.class, () -> {
+            Object abc = NumberUtils.parseNumber("1", null);
+        });
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ResteasyResponseTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ResteasyResponseTest.java
new file mode 100644
index 0000000..b64c5ac
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ResteasyResponseTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService;
+import org.apache.dubbo.rpc.protocol.rest.rest.RestDemoServiceImpl;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.ws.rs.core.Response;
+
+import static org.apache.dubbo.remoting.Constants.SERVER_KEY;
+
+public class ResteasyResponseTest {
+
+    private Protocol protocol =
+            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest");
+    private ProxyFactory proxy =
+            ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    private final int availablePort = NetUtils.getAvailablePort();
+    private final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort
+            + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService");
+    private final ModuleServiceRepository repository =
+            ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
+
+    @AfterEach
+    public void tearDown() {
+        protocol.destroy();
+        FrameworkModel.destroyAll();
+    }
+
+    @Test
+    void testResponse() {
+        RestDemoService server = new RestDemoServiceImpl();
+        URL url = this.registerProvider(exportUrl, server, RestDemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, "netty").addParameter("timeout", 3000000);
+
+        protocol.export(proxy.getInvoker(new RestDemoServiceImpl(), RestDemoService.class, nettyUrl));
+
+        RestDemoService demoService = this.proxy.getProxy(protocol.refer(RestDemoService.class, nettyUrl));
+
+        Response response = demoService.findUserById(10);
+
+        Assertions.assertNotNull(response);
+    }
+
+    @Test
+    void testResponseCustomStatusCode() {
+        RestDemoService server = new RestDemoServiceImpl();
+        URL url = this.registerProvider(exportUrl, server, RestDemoService.class);
+
+        URL nettyUrl = url.addParameter(SERVER_KEY, "netty").addParameter("timeout", 3000000);
+
+        protocol.export(proxy.getInvoker(new RestDemoServiceImpl(), RestDemoService.class, nettyUrl));
+
+        RestDemoService demoService = this.proxy.getProxy(protocol.refer(RestDemoService.class, nettyUrl));
+
+        Response response = demoService.deleteUserById("uid");
+
+        Assertions.assertEquals(response.getStatus(), 200);
+    }
+
+    private URL registerProvider(URL url, Object impl, Class<?> interfaceClass) {
+        ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
+        ProviderModel providerModel = new ProviderModel(url.getServiceKey(), impl, serviceDescriptor, null, null);
+        repository.registerProvider(providerModel);
+        return url.setServiceModel(providerModel);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapperTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapperTest.java
new file mode 100644
index 0000000..4187a86
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/RpcExceptionMapperTest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Answers;
+import org.mockito.internal.util.collections.Sets;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.LinkedList;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+
+class RpcExceptionMapperTest {
+
+    private ExceptionHandler exceptionMapper;
+
+    @BeforeEach
+    public void setUp() {
+        this.exceptionMapper = new RpcExceptionMapper();
+    }
+
+    @Test
+    void testConstraintViolationException() {
+        ConstraintViolationException violationException = mock(ConstraintViolationException.class);
+        ConstraintViolation<?> violation = mock(ConstraintViolation.class, Answers.RETURNS_DEEP_STUBS);
+        given(violationException.getConstraintViolations()).willReturn(Sets.newSet(violation));
+        RpcException rpcException = new RpcException("violation", violationException);
+
+        Object response = exceptionMapper.result(rpcException);
+
+        assertThat(response, not(nullValue()));
+        assertThat(response, instanceOf(ViolationReport.class));
+    }
+
+    @Test
+    void testNormalException() {
+        RpcException rpcException = new RpcException();
+        Object response = exceptionMapper.result(rpcException);
+
+        assertThat(response, not(nullValue()));
+        assertThat(response, instanceOf(String.class));
+    }
+
+    @Test
+    void testBuildException() {
+
+        RestConstraintViolation restConstraintViolation = new RestConstraintViolation();
+        String message = "message";
+        restConstraintViolation.setMessage(message);
+        String path = "path";
+        restConstraintViolation.setPath(path);
+        String value = "value";
+        restConstraintViolation.setValue(value);
+
+        Assertions.assertEquals(message, restConstraintViolation.getMessage());
+        Assertions.assertEquals(path, restConstraintViolation.getPath());
+        Assertions.assertEquals(value, restConstraintViolation.getValue());
+    }
+
+    @Test
+    public void testViolationReport() {
+
+        ViolationReport violationReport = new ViolationReport();
+
+        RestConstraintViolation restConstraintViolation = new RestConstraintViolation("path", "message", "value");
+
+        violationReport.addConstraintViolation(restConstraintViolation);
+
+        Assertions.assertEquals(1, violationReport.getConstraintViolations().size());
+
+        violationReport = new ViolationReport();
+
+        violationReport.setConstraintViolations(new LinkedList<>());
+
+        Assertions.assertEquals(0, violationReport.getConstraintViolations().size());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ServiceConfigTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ServiceConfigTest.java
new file mode 100644
index 0000000..b55e16f
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/ServiceConfigTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.remoting.http.RequestTemplate;
+import org.apache.dubbo.remoting.http.config.HttpClientConfig;
+import org.apache.dubbo.remoting.http.restclient.OKHttpRestClient;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
+import org.apache.dubbo.rpc.protocol.rest.mvc.SpringControllerService;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ServiceConfigTest {
+
+    private final Protocol protocol =
+            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest");
+    private final ProxyFactory proxy =
+            ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    private final ModuleServiceRepository repository =
+            ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
+
+    @AfterEach
+    public void tearDown() {
+        protocol.destroy();
+        FrameworkModel.destroyAll();
+    }
+
+    @Test
+    void testControllerService() throws Exception {
+
+        int availablePort = NetUtils.getAvailablePort();
+        URL url = URL.valueOf("rest://127.0.0.1:" + availablePort
+                + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringControllerService");
+
+        SpringControllerService server = new SpringControllerService();
+
+        url = this.registerProvider(url, server, SpringControllerService.class);
+
+        Exporter<SpringControllerService> exporter =
+                protocol.export(proxy.getInvoker(server, SpringControllerService.class, url));
+
+        OKHttpRestClient okHttpRestClient = new OKHttpRestClient(new HttpClientConfig());
+
+        RequestTemplate requestTemplate = new RequestTemplate(null, "GET", "127.0.0.1:" + availablePort);
+        requestTemplate.path("/controller/sayHello?say=dubbo");
+        requestTemplate.addHeader(RestConstant.CONTENT_TYPE, "text/plain");
+        requestTemplate.addHeader(RestConstant.ACCEPT, "text/plain");
+        requestTemplate.addHeader(RestHeaderEnum.VERSION.getHeader(), "1.0.0");
+
+        byte[] body = okHttpRestClient.send(requestTemplate).get().getBody();
+
+        Assertions.assertEquals("dubbo", new String(body));
+        exporter.unexport();
+    }
+
+    private URL registerProvider(URL url, Object impl, Class<?> interfaceClass) {
+        ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
+        ProviderModel providerModel = new ProviderModel(url.getServiceKey(), impl, serviceDescriptor, null, null);
+        repository.registerProvider(providerModel);
+        return url.setServiceModel(providerModel);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java
new file mode 100644
index 0000000..c864ce7
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/SpringMvcRestProtocolTest.java
@@ -0,0 +1,439 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Protocol;
+import org.apache.dubbo.rpc.ProxyFactory;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleServiceRepository;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionHandler;
+import org.apache.dubbo.rpc.protocol.rest.exception.mapper.ExceptionMapper;
+import org.apache.dubbo.rpc.protocol.rest.mvc.SpringDemoServiceImpl;
+import org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService;
+import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService;
+import org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestServiceImpl;
+import org.hamcrest.CoreMatchers;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.springframework.aop.framework.AdvisedSupport;
+import org.springframework.aop.framework.AopProxy;
+import org.springframework.aop.framework.ProxyCreatorSupport;
+import org.springframework.util.LinkedMultiValueMap;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import static org.apache.dubbo.remoting.Constants.SERVER_KEY;
+import static org.apache.dubbo.rpc.protocol.rest.Constants.EXTENSION_KEY;
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class SpringMvcRestProtocolTest {
+    private Protocol protocol =
+            ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest");
+    private ProxyFactory proxy =
+            ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
+    private static final int availablePort = NetUtils.getAvailablePort();
+    private static final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort
+            + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService");
+
+    private final ModuleServiceRepository repository =
+            ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
+
+    private final ExceptionMapper exceptionMapper = new ExceptionMapper();
+
+    @AfterEach
+    public void tearDown() {
+        protocol.destroy();
+        FrameworkModel.destroyAll();
+    }
+
+    public SpringRestDemoService getServerImpl() {
+        return new SpringDemoServiceImpl();
+    }
+
+    public Class<SpringRestDemoService> getServerClass() {
+        return SpringRestDemoService.class;
+    }
+
+    public Exporter<SpringRestDemoService> getExport(URL url, SpringRestDemoService server) {
+        url = url.addParameter(SERVER_KEY, Constants.NETTY_HTTP);
+        return protocol.export(proxy.getInvoker(server, getServerClass(), url));
+    }
+
+    public Exporter<SpringRestDemoService> getExceptionHandlerExport(URL url, SpringRestDemoService server) {
+        url = url.addParameter(SERVER_KEY, Constants.NETTY_HTTP);
+        url = url.addParameter(EXTENSION_KEY, TestExceptionMapper.class.getName());
+        return protocol.export(proxy.getInvoker(server, getServerClass(), url));
+    }
+
+    @Test
+    void testRestProtocol() {
+        URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort()
+                + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService");
+
+        SpringRestDemoService server = getServerImpl();
+
+        url = this.registerProvider(url, server, getServerClass());
+
+        Exporter<SpringRestDemoService> exporter = getExport(url, server);
+        Invoker<SpringRestDemoService> invoker = protocol.refer(SpringRestDemoService.class, url);
+        Assertions.assertFalse(server.isCalled());
+
+        SpringRestDemoService client = proxy.getProxy(invoker);
+        String result = client.sayHello("haha");
+        Assertions.assertTrue(server.isCalled());
+        Assertions.assertEquals("Hello, haha", result);
+
+        String header = client.testHeader("header");
+        Assertions.assertEquals("header", header);
+
+        String headerInt = client.testHeaderInt(1);
+        Assertions.assertEquals("1", headerInt);
+        invoker.destroy();
+        exporter.unexport();
+    }
+
+    @Test
+    void testAnotherUserRestProtocol() {
+        URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort()
+                + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.AnotherUserRestService");
+
+        AnotherUserRestServiceImpl server = new AnotherUserRestServiceImpl();
+
+        url = this.registerProvider(url, server, SpringRestDemoService.class);
+
+        Exporter<AnotherUserRestService> exporter =
+                protocol.export(proxy.getInvoker(server, AnotherUserRestService.class, url));
+        Invoker<AnotherUserRestService> invoker = protocol.refer(AnotherUserRestService.class, url);
+
+        AnotherUserRestService client = proxy.getProxy(invoker);
+        User result = client.getUser(123l);
+
+        Assertions.assertEquals(123l, result.getId());
+
+        result.setName("dubbo");
+        Assertions.assertEquals(123l, client.registerUser(result).getId());
+
+        Assertions.assertEquals("context", client.getContext());
+
+        byte[] bytes = {1, 2, 3, 4};
+        Assertions.assertTrue(Arrays.equals(bytes, client.bytes(bytes)));
+
+        Assertions.assertEquals(1l, client.number(1l));
+
+        invoker.destroy();
+        exporter.unexport();
+    }
+
+    @Test
+    void testRestProtocolWithContextPath() {
+        SpringRestDemoService server = getServerImpl();
+        Assertions.assertFalse(server.isCalled());
+        int port = NetUtils.getAvailablePort();
+        URL url = URL.valueOf("rest://127.0.0.1:" + port
+                + "/a/b/c?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService");
+
+        url = this.registerProvider(url, server, SpringRestDemoService.class);
+
+        Exporter<SpringRestDemoService> exporter = getExport(url, server);
+
+        url = URL.valueOf("rest://127.0.0.1:" + port
+                + "/a/b/c/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.mvc.SpringRestDemoService");
+        Invoker<SpringRestDemoService> invoker = protocol.refer(SpringRestDemoService.class, url);
+        SpringRestDemoService client = proxy.getProxy(invoker);
+        String result = client.sayHello("haha");
+        Assertions.assertTrue(server.isCalled());
+        Assertions.assertEquals("Hello, haha", result);
+        invoker.destroy();
+        exporter.unexport();
+    }
+
+    @Test
+    void testExport() {
+        SpringRestDemoService server = getServerImpl();
+
+        URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+        RpcContext.getClientAttachment().setAttachment("timeout", "200");
+        Exporter<SpringRestDemoService> exporter = getExport(url, server);
+
+        SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, url));
+
+        Integer echoString = demoService.hello(1, 2);
+        assertThat(echoString, is(3));
+
+        exporter.unexport();
+    }
+
+    @Test
+    void testNettyServer() {
+        SpringRestDemoService server = getServerImpl();
+
+        URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+        Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
+
+        SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl));
+
+        Integer echoString = demoService.hello(10, 10);
+        assertThat(echoString, is(20));
+
+        exporter.unexport();
+    }
+
+    @Disabled
+    @Test
+    void testServletWithoutWebConfig() {
+        Assertions.assertThrows(RpcException.class, () -> {
+            SpringRestDemoService server = getServerImpl();
+
+            URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+            URL servletUrl = url.addParameter(SERVER_KEY, "servlet");
+
+            protocol.export(proxy.getInvoker(server, getServerClass(), servletUrl));
+        });
+    }
+
+    @Test
+    void testErrorHandler() {
+        Assertions.assertThrows(RpcException.class, () -> {
+            exceptionMapper.unRegisterMapper(RuntimeException.class);
+            SpringRestDemoService server = getServerImpl();
+
+            URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+            Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
+
+            SpringRestDemoService demoService =
+                    this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl));
+
+            demoService.error();
+        });
+    }
+
+    @Test
+    void testInvoke() {
+        SpringRestDemoService server = getServerImpl();
+
+        URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+        Exporter<SpringRestDemoService> exporter = getExport(url, server);
+
+        RpcInvocation rpcInvocation = new RpcInvocation(
+                "hello",
+                SpringRestDemoService.class.getName(),
+                "",
+                new Class[] {Integer.class, Integer.class},
+                new Integer[] {2, 3});
+
+        Result result = exporter.getInvoker().invoke(rpcInvocation);
+        assertThat(result.getValue(), CoreMatchers.<Object>is(5));
+    }
+
+    @Test
+    void testFilter() {
+        SpringRestDemoService server = getServerImpl();
+
+        URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+        Exporter<SpringRestDemoService> exporter = getExport(url, server);
+
+        SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, url));
+
+        Integer result = demoService.hello(1, 2);
+
+        assertThat(result, is(3));
+
+        exporter.unexport();
+    }
+
+    @Test
+    void testRpcContextFilter() {
+        SpringRestDemoService server = getServerImpl();
+
+        URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+        // use RpcContextFilter
+        //        URL nettyUrl = url.addParameter(SERVER_KEY, "netty")
+        //            .addParameter(EXTENSION_KEY, "org.apache.dubbo.rpc.protocol.rest.RpcContextFilter");
+        Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
+
+        SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl));
+
+        // make sure null and base64 encoded string can work
+        RpcContext.getClientAttachment().setAttachment("key1", null);
+        RpcContext.getClientAttachment().setAttachment("key2", "value");
+        RpcContext.getClientAttachment().setAttachment("key3", "=value");
+        RpcContext.getClientAttachment().setAttachment("key4", "YWJjZGVmCg==");
+        RpcContext.getClientAttachment().setAttachment("key5", "val=ue");
+        Integer result = demoService.hello(1, 2);
+
+        assertThat(result, is(3));
+
+        Map<String, Object> attachment = SpringDemoServiceImpl.getAttachments();
+        assertThat(attachment.get("key1"), nullValue());
+        assertThat(attachment.get("key2"), equalTo("value"));
+        assertThat(attachment.get("key3"), equalTo("=value"));
+        assertThat(attachment.get("key4"), equalTo("YWJjZGVmCg=="));
+        assertThat(attachment.get("key5"), equalTo("val=ue"));
+
+        exporter.unexport();
+    }
+
+    @Disabled
+    @Test
+    void testRegFail() {
+        Assertions.assertThrows(RuntimeException.class, () -> {
+            SpringRestDemoService server = getServerImpl();
+
+            URL url = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+            URL nettyUrl = url.addParameter(EXTENSION_KEY, "com.not.existing.Filter");
+            Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
+        });
+    }
+
+    @Test
+    void testDefaultPort() {
+        assertThat(protocol.getDefaultPort(), is(80));
+    }
+
+    @Test
+    void testExceptionMapper() {
+
+        SpringRestDemoService server = getServerImpl();
+
+        URL exceptionUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+        Exporter<SpringRestDemoService> exporter = getExceptionHandlerExport(exceptionUrl, server);
+
+        SpringRestDemoService referDemoService =
+                this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, exceptionUrl));
+
+        Assertions.assertEquals("test-exception", referDemoService.error());
+
+        exporter.unexport();
+    }
+
+    @Test
+    void testFormConsumerParser() {
+        SpringRestDemoService server = getServerImpl();
+
+        URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+        Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
+
+        SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl));
+
+        User user = new User();
+        user.setAge(18);
+        user.setName("dubbo");
+        user.setId(404l);
+        String name = demoService.testFormBody(user);
+        Assertions.assertEquals("dubbo", name);
+
+        LinkedMultiValueMap<String, String> forms = new LinkedMultiValueMap<>();
+        forms.put("form", Arrays.asList("F1"));
+
+        Assertions.assertEquals(Arrays.asList("F1"), demoService.testFormMapBody(forms));
+
+        exporter.unexport();
+    }
+
+    @Test
+    void testPrimitive() {
+        SpringRestDemoService server = getServerImpl();
+
+        URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+        Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
+
+        SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl));
+
+        Integer result = demoService.primitiveInt(1, 2);
+        Long resultLong = demoService.primitiveLong(1, 2l);
+        long resultByte = demoService.primitiveByte((byte) 1, 2l);
+        long resultShort = demoService.primitiveShort((short) 1, 2l, 1);
+
+        assertThat(result, is(3));
+        assertThat(resultShort, is(3l));
+        assertThat(resultLong, is(3l));
+        assertThat(resultByte, is(3l));
+
+        exporter.unexport();
+    }
+
+    @Test
+    void testProxyDoubleCheck() {
+
+        ProxyCreatorSupport proxyCreatorSupport = new ProxyCreatorSupport();
+        AdvisedSupport advisedSupport = new AdvisedSupport();
+        advisedSupport.setTarget(getServerImpl());
+        AopProxy aopProxy = proxyCreatorSupport.getAopProxyFactory().createAopProxy(advisedSupport);
+        Object proxy = aopProxy.getProxy();
+        SpringRestDemoService server = (SpringRestDemoService) proxy;
+
+        URL nettyUrl = this.registerProvider(exportUrl, server, SpringRestDemoService.class);
+
+        Exporter<SpringRestDemoService> exporter = getExport(nettyUrl, server);
+
+        SpringRestDemoService demoService = this.proxy.getProxy(protocol.refer(SpringRestDemoService.class, nettyUrl));
+
+        Integer result = demoService.primitiveInt(1, 2);
+        Long resultLong = demoService.primitiveLong(1, 2l);
+        long resultByte = demoService.primitiveByte((byte) 1, 2l);
+        long resultShort = demoService.primitiveShort((short) 1, 2l, 1);
+
+        assertThat(result, is(3));
+        assertThat(resultShort, is(3l));
+        assertThat(resultLong, is(3l));
+        assertThat(resultByte, is(3l));
+
+        exporter.unexport();
+    }
+
+    public static class TestExceptionMapper implements ExceptionHandler<RuntimeException> {
+
+        @Override
+        public String result(RuntimeException e) {
+            return "test-exception";
+        }
+    }
+
+    private URL registerProvider(URL url, Object impl, Class<?> interfaceClass) {
+        ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
+        ProviderModel providerModel = new ProviderModel(url.getServiceKey(), impl, serviceDescriptor, null, null);
+        repository.registerProvider(providerModel);
+        return url.setServiceModel(providerModel);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java
new file mode 100644
index 0000000..b877e20
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/User.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * User Entity
+ *
+ * @since 2.7.6
+ */
+public class User implements Serializable {
+
+    private Long id;
+
+    private String name;
+
+    private Integer age;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    public static User getInstance() {
+        User user = new User();
+        user.setAge(18);
+        user.setName("dubbo");
+        user.setId(404l);
+        return user;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        User user = (User) o;
+        return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(age, user.age);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, name, age);
+    }
+
+    @Override
+    public String toString() {
+        return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/exception/ResteasyExceptionMapper.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/exception/ResteasyExceptionMapper.java
new file mode 100644
index 0000000..cf54041
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/exception/ResteasyExceptionMapper.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.exception;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+
+public class ResteasyExceptionMapper implements ExceptionMapper<RuntimeException> {
+    @Override
+    public Response toResponse(RuntimeException exception) {
+        return Response.status(200).entity("test-exception").build();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/filter/TestContainerRequestFilter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/filter/TestContainerRequestFilter.java
new file mode 100644
index 0000000..9b69e19
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/filter/TestContainerRequestFilter.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.dubbo.rpc.protocol.rest.filter;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+
+@Priority(Priorities.USER)
+public class TestContainerRequestFilter implements ContainerRequestFilter {
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+
+        requestContext.abortWith(Response.status(200).entity("return-success").build());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/filter/TraceFilter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/filter/TraceFilter.java
new file mode 100644
index 0000000..44b68dc
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/filter/TraceFilter.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import java.io.IOException;
+
+@Priority(Priorities.USER)
+public class TraceFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        System.out.println(
+                "Request filter invoked: " + requestContext.getUriInfo().getAbsolutePath());
+    }
+
+    @Override
+    public void filter(
+            ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext)
+            throws IOException {
+        containerResponseContext.setEntity("response-filter");
+        System.out.println("Response filter invoked.");
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/filter/TraceRequestAndResponseFilter.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/filter/TraceRequestAndResponseFilter.java
new file mode 100644
index 0000000..4535cea
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/filter/TraceRequestAndResponseFilter.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.filter;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import java.io.IOException;
+
+@Priority(Priorities.USER)
+public class TraceRequestAndResponseFilter implements ContainerRequestFilter, ContainerResponseFilter {
+
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+
+        requestContext.getHeaders().add("test-response", "header-result");
+    }
+
+    @Override
+    public void filter(
+            ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext)
+            throws IOException {
+
+        String headerString = containerRequestContext.getHeaderString("test-response");
+        containerResponseContext.setEntity(headerString);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/intercept/DynamicTraceInterceptor.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/intercept/DynamicTraceInterceptor.java
new file mode 100644
index 0000000..c110945
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/intercept/DynamicTraceInterceptor.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.intercept;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.ext.ReaderInterceptor;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+import javax.ws.rs.ext.WriterInterceptor;
+import javax.ws.rs.ext.WriterInterceptorContext;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+@Priority(Priorities.USER)
+public class DynamicTraceInterceptor implements ReaderInterceptor, WriterInterceptor {
+
+    public DynamicTraceInterceptor() {}
+
+    @Override
+    public Object aroundReadFrom(ReaderInterceptorContext readerInterceptorContext)
+            throws IOException, WebApplicationException {
+        System.out.println("Dynamic reader interceptor invoked");
+        return readerInterceptorContext.proceed();
+    }
+
+    @Override
+    public void aroundWriteTo(WriterInterceptorContext writerInterceptorContext)
+            throws IOException, WebApplicationException {
+        System.out.println("Dynamic writer interceptor invoked");
+        writerInterceptorContext.getOutputStream().write("intercept".getBytes(StandardCharsets.UTF_8));
+        writerInterceptorContext.proceed();
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringControllerService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringControllerService.java
new file mode 100644
index 0000000..bf76119
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringControllerService.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 org.apache.dubbo.rpc.protocol.rest.mvc;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@RequestMapping("/controller")
+public class SpringControllerService {
+
+    @GetMapping("/sayHello")
+    public String sayHello(String say) {
+        return say;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java
new file mode 100644
index 0000000..5544cbc
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringDemoServiceImpl.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.mvc;
+
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.protocol.rest.User;
+import org.springframework.util.LinkedMultiValueMap;
+
+import java.util.List;
+import java.util.Map;
+
+public class SpringDemoServiceImpl implements SpringRestDemoService {
+    private static Map<String, Object> context;
+    private boolean called;
+
+    @Override
+    public String sayHello(String name) {
+        called = true;
+        return "Hello, " + name;
+    }
+
+    @Override
+    public boolean isCalled() {
+        return called;
+    }
+
+    @Override
+    public String testFormBody(User user) {
+        return user.getName();
+    }
+
+    @Override
+    public List<String> testFormMapBody(LinkedMultiValueMap map) {
+        return map.get("form");
+    }
+
+    @Override
+    public String testHeader(String header) {
+        return header;
+    }
+
+    @Override
+    public String testHeaderInt(int header) {
+        return String.valueOf(header);
+    }
+
+    @Override
+    public Integer hello(Integer a, Integer b) {
+        context = RpcContext.getServerAttachment().getObjectAttachments();
+        return a + b;
+    }
+
+    @Override
+    public String error() {
+        throw new RuntimeException();
+    }
+
+    public static Map<String, Object> getAttachments() {
+        return context;
+    }
+
+    @Override
+    public int primitiveInt(int a, int b) {
+        return a + b;
+    }
+
+    @Override
+    public long primitiveLong(long a, Long b) {
+        return a + b;
+    }
+
+    @Override
+    public long primitiveByte(byte a, Long b) {
+        return a + b;
+    }
+
+    @Override
+    public long primitiveShort(short a, Long b, int c) {
+        return a + b;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringRestDemoService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringRestDemoService.java
new file mode 100644
index 0000000..07a6698
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/mvc/SpringRestDemoService.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.mvc;
+
+import org.apache.dubbo.rpc.protocol.rest.User;
+import org.springframework.http.MediaType;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+
+@RequestMapping("/demoService")
+public interface SpringRestDemoService {
+    @RequestMapping(value = "/hello", method = RequestMethod.GET)
+    Integer hello(@RequestParam Integer a, @RequestParam Integer b);
+
+    @RequestMapping(value = "/error", method = RequestMethod.GET)
+    String error();
+
+    @RequestMapping(value = "/sayHello", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE)
+    String sayHello(String name);
+
+    boolean isCalled();
+
+    @RequestMapping(
+            value = "/testFormBody",
+            method = RequestMethod.POST,
+            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    String testFormBody(@RequestBody User user);
+
+    @RequestMapping(
+            value = "/testFormMapBody",
+            method = RequestMethod.POST,
+            consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
+    List<String> testFormMapBody(@RequestBody LinkedMultiValueMap map);
+
+    @RequestMapping(value = "/testHeader", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE)
+    String testHeader(@RequestHeader String header);
+
+    @RequestMapping(value = "/testHeaderInt", method = RequestMethod.GET, consumes = MediaType.TEXT_PLAIN_VALUE)
+    String testHeaderInt(@RequestHeader int header);
+
+    @RequestMapping(method = RequestMethod.GET, value = "/primitive")
+    int primitiveInt(@RequestParam("a") int a, @RequestParam("b") int b);
+
+    @RequestMapping(method = RequestMethod.GET, value = "/primitiveLong")
+    long primitiveLong(@RequestParam("a") long a, @RequestParam("b") Long b);
+
+    @RequestMapping(method = RequestMethod.GET, value = "/primitiveByte")
+    long primitiveByte(@RequestParam("a") byte a, @RequestParam("b") Long b);
+
+    @RequestMapping(method = RequestMethod.POST, value = "/primitiveShort")
+    long primitiveShort(@RequestParam("a") short a, @RequestParam("b") Long b, @RequestBody int c);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.java
new file mode 100644
index 0000000..2ba1996
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestService.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.dubbo.rpc.protocol.rest.rest;
+
+import org.apache.dubbo.rpc.protocol.rest.User;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import java.util.Map;
+
+@Path("u")
+@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
+public interface AnotherUserRestService {
+
+    @GET
+    @Path("{id : \\d+}")
+    @Produces({MediaType.APPLICATION_JSON})
+    User getUser(@PathParam("id") Long id);
+
+    @POST
+    @Path("register")
+    @Produces("text/xml; charset=UTF-8")
+    RegistrationResult registerUser(User user);
+
+    @GET
+    @Path("context")
+    @Produces({MediaType.APPLICATION_JSON})
+    String getContext();
+
+    @POST
+    @Path("bytes")
+    @Produces({MediaType.APPLICATION_JSON})
+    byte[] bytes(byte[] bytes);
+
+    @POST
+    @Path("number")
+    @Produces({MediaType.APPLICATION_JSON})
+    Long number(Long number);
+
+    @POST
+    @Path("headerMap")
+    @Produces({MediaType.APPLICATION_JSON})
+    String headerMap(@HeaderParam("headers") Map<String, String> headers);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java
new file mode 100644
index 0000000..3e99f97
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/AnotherUserRestServiceImpl.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.rest;
+
+import org.apache.dubbo.rpc.protocol.rest.User;
+
+import java.util.Map;
+
+public class AnotherUserRestServiceImpl implements AnotherUserRestService {
+
+    @Override
+    public User getUser(Long id) {
+
+        User user = new User();
+        user.setId(id);
+        return user;
+    }
+
+    @Override
+    public RegistrationResult registerUser(User user) {
+        return new RegistrationResult(user.getId());
+    }
+
+    @Override
+    public String getContext() {
+
+        return "context";
+    }
+
+    @Override
+    public byte[] bytes(byte[] bytes) {
+        return bytes;
+    }
+
+    @Override
+    public Long number(Long number) {
+        return number;
+    }
+
+    @Override
+    public String headerMap(Map<String, String> headers) {
+        return headers.get("headers");
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodService.java
new file mode 100644
index 0000000..0b0b7d0
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodService.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.rest;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HEAD;
+import javax.ws.rs.OPTIONS;
+import javax.ws.rs.PATCH;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+@Path("/demoService")
+public interface HttpMethodService {
+
+    @POST
+    @Path("/sayPost")
+    @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
+    String sayHelloPost(@QueryParam("name") String name);
+
+    @DELETE
+    @Path("/sayDelete")
+    @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
+    String sayHelloDelete(@QueryParam("name") String name);
+
+    @HEAD
+    @Path("/sayHead")
+    @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
+    String sayHelloHead();
+
+    @GET
+    @Path("/sayGet")
+    @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
+    String sayHelloGet(@QueryParam("name") String name);
+
+    @PUT
+    @Path("/sayPut")
+    @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
+    String sayHelloPut(@QueryParam("name") String name);
+
+    @PATCH
+    @Path("/sayPatch")
+    @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
+    String sayHelloPatch(@QueryParam("name") String name);
+
+    @OPTIONS
+    @Path("/sayOptions")
+    @Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
+    String sayHelloOptions(@QueryParam("name") String name);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodServiceImpl.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodServiceImpl.java
new file mode 100644
index 0000000..7dee660
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/HttpMethodServiceImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.rest;
+
+public class HttpMethodServiceImpl implements HttpMethodService {
+
+    @Override
+    public String sayHelloPost(String name) {
+        return name;
+    }
+
+    @Override
+    public String sayHelloDelete(String name) {
+        return name;
+    }
+
+    @Override
+    public String sayHelloHead() {
+        return "hello";
+    }
+
+    @Override
+    public String sayHelloGet(String name) {
+        return name;
+    }
+
+    @Override
+    public String sayHelloPut(String name) {
+        return name;
+    }
+
+    @Override
+    public String sayHelloPatch(String name) {
+        return name;
+    }
+
+    @Override
+    public String sayHelloOptions(String name) {
+        return name;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java
new file mode 100644
index 0000000..521ba1f
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RegistrationResult.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.rest;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * DTO to customize the returned message
+ */
+@XmlRootElement
+public class RegistrationResult implements Serializable {
+
+    private Long id;
+
+    public RegistrationResult() {}
+
+    public RegistrationResult(Long id) {
+        this.id = id;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        RegistrationResult that = (RegistrationResult) o;
+        return Objects.equals(id, that.id);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id);
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java
new file mode 100644
index 0000000..5ef5293
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoForTestException.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.rest;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+@Path("/demoService")
+public interface RestDemoForTestException {
+
+    @POST
+    @Path("/noFound")
+    @Produces(MediaType.TEXT_PLAIN)
+    String test404();
+
+    @GET
+    @Consumes({MediaType.TEXT_PLAIN})
+    @Path("/hello")
+    Integer test400(@QueryParam("a") String a, @QueryParam("b") String b);
+
+    @POST
+    @Path("{uid}")
+    String testMethodDisallowed(@PathParam("uid") String uid);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java
new file mode 100644
index 0000000..728ce07
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoService.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.rest;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+@Path("/demoService")
+public interface RestDemoService {
+    @GET
+    @Path("/hello")
+    Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b);
+
+    @GET
+    @Path("/findUserById")
+    Response findUserById(@QueryParam("id") Integer id);
+
+    @GET
+    @Path("/error")
+    String error();
+
+    @POST
+    @Path("/say")
+    @Consumes({MediaType.TEXT_PLAIN})
+    String sayHello(String name);
+
+    @POST
+    @Path("number")
+    @Produces({MediaType.APPLICATION_FORM_URLENCODED})
+    @Consumes({MediaType.APPLICATION_FORM_URLENCODED})
+    Long testFormBody(@FormParam("number") Long number);
+
+    boolean isCalled();
+
+    @DELETE
+    @Path("{uid}")
+    String deleteUserByUid(@PathParam("uid") String uid);
+
+    @DELETE
+    @Path("/deleteUserById/{uid}")
+    public Response deleteUserById(@PathParam("uid") String uid);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java
new file mode 100644
index 0000000..9dc94e0
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/RestDemoServiceImpl.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.rest;
+
+import org.apache.dubbo.rpc.RpcContext;
+import org.jboss.resteasy.specimpl.BuiltResponse;
+
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+import java.util.Map;
+
+public class RestDemoServiceImpl implements RestDemoService {
+    private static Map<String, Object> context;
+    private boolean called;
+
+    @Override
+    public String sayHello(String name) {
+        called = true;
+        return "Hello, " + name;
+    }
+
+    @Override
+    public Long testFormBody(Long number) {
+        return number;
+    }
+
+    public boolean isCalled() {
+        return called;
+    }
+
+    @Override
+    public String deleteUserByUid(String uid) {
+        return uid;
+    }
+
+    @Override
+    public Integer hello(Integer a, Integer b) {
+        context = RpcContext.getServerAttachment().getObjectAttachments();
+        return a + b;
+    }
+
+    @Override
+    public Response findUserById(Integer id) {
+        Map<String, Object> content = new HashMap<>();
+        content.put("username", "jack");
+        content.put("id", id);
+
+        return BuiltResponse.ok(content).build();
+    }
+
+    @Override
+    public String error() {
+        throw new RuntimeException();
+    }
+
+    @Override
+    public Response deleteUserById(String uid) {
+        return Response.status(300).entity("deleted").build();
+    }
+
+    public static Map<String, Object> getAttachments() {
+        return context;
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/TestGetInvokerService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/TestGetInvokerService.java
new file mode 100644
index 0000000..994b7a2
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/TestGetInvokerService.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.rest;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+@Path("/test")
+public interface TestGetInvokerService {
+
+    @GET
+    @Path("/getInvoker")
+    String getInvoker();
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/TestGetInvokerServiceImpl.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/TestGetInvokerServiceImpl.java
new file mode 100644
index 0000000..df5b39e
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/rest/TestGetInvokerServiceImpl.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.dubbo.rpc.protocol.rest.rest;
+
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcContext;
+import org.apache.dubbo.rpc.protocol.rest.RestRPCInvocationUtil;
+import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
+import org.junit.jupiter.api.Assertions;
+
+import java.lang.reflect.Method;
+
+public class TestGetInvokerServiceImpl implements TestGetInvokerService {
+
+    @Override
+    public String getInvoker() {
+        Object request = RpcContext.getServiceContext().getRequest();
+        RequestFacade requestFacade = (RequestFacade) request;
+        Invoker invokerByRequest = RestRPCInvocationUtil.getInvokerByRequest((RequestFacade) request);
+
+        Method hello = null;
+        Method hashcode = null;
+        try {
+            hello = TestGetInvokerServiceImpl.class.getDeclaredMethod("getInvoker");
+            hashcode = TestGetInvokerServiceImpl.class.getDeclaredMethod("hashcode");
+
+        } catch (NoSuchMethodException e) {
+
+        }
+
+        Invoker invokerByServiceInvokeMethod =
+                RestRPCInvocationUtil.getInvokerByServiceInvokeMethod(hello, requestFacade.getServiceDeployer());
+
+        Invoker invoker =
+                RestRPCInvocationUtil.getInvokerByServiceInvokeMethod(hashcode, requestFacade.getServiceDeployer());
+
+        Assertions.assertEquals(invokerByRequest, invokerByServiceInvokeMethod);
+        Assertions.assertNull(invoker);
+
+        return "success";
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/swagger/DubboSwaggerApiListingResourceTest.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/swagger/DubboSwaggerApiListingResourceTest.java
new file mode 100644
index 0000000..875deba
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/swagger/DubboSwaggerApiListingResourceTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.rpc.protocol.rest.swagger;
+
+import io.swagger.models.Swagger;
+import org.apache.dubbo.rpc.protocol.rest.integration.swagger.DubboSwaggerApiListingResource;
+import org.jboss.resteasy.spi.ResteasyUriInfo;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class DubboSwaggerApiListingResourceTest {
+
+    private Application app;
+    private ServletConfig sc;
+
+    @Test
+    void test() throws Exception {
+
+        org.apache.dubbo.rpc.protocol.rest.integration.swagger.DubboSwaggerApiListingResource resource = new DubboSwaggerApiListingResource();
+
+        app = mock(Application.class);
+        sc = mock(ServletConfig.class);
+        Set<Class<?>> sets = new HashSet<Class<?>>();
+        sets.add(SwaggerService.class);
+
+        when(sc.getServletContext()).thenReturn(mock(ServletContext.class));
+        when(app.getClasses()).thenReturn(sets);
+
+        Response response = resource.getListingJson(app, sc, null, new ResteasyUriInfo(new URI("http://rest.test")));
+
+        Assertions.assertNotNull(response);
+        Swagger swagger = (Swagger) response.getEntity();
+        Assertions.assertEquals("SwaggerService", swagger.getTags().get(0).getName());
+        Assertions.assertEquals(
+                "/demoService/hello", swagger.getPaths().keySet().toArray()[0].toString());
+    }
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/swagger/SwaggerService.java b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/swagger/SwaggerService.java
new file mode 100644
index 0000000..394bb7b
--- /dev/null
+++ b/dubbo-rpc-extensions/dubbo-rpc-rest/src/test/java/org/apache/dubbo/rpc/protocol/rest/swagger/SwaggerService.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.dubbo.rpc.protocol.rest.swagger;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+
+@Path("/demoService")
+@Api(value = "SwaggerService")
+public interface SwaggerService {
+    @GET
+    @Path("/hello")
+    @ApiOperation(value = "hello")
+    Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b);
+}
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rmi/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-rmi/pom.xml
index c9facd7..56ba6b4 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-rmi/pom.xml
+++ b/dubbo-rpc-extensions/dubbo-rpc-rmi/pom.xml
@@ -25,7 +25,7 @@
     </parent>
     <artifactId>dubbo-rpc-rmi</artifactId>
     <packaging>jar</packaging>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The rmi rpc module of dubbo project</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/pom.xml
index b84c414..d8c7ea6 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/pom.xml
+++ b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/pom.xml
@@ -25,7 +25,7 @@
         <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
-    <version>1.0.1-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-rpc-rocketmq</artifactId>
     <name>dubbo-rpc-rocketmq</name>
     <properties>
@@ -73,5 +73,10 @@
             <artifactId>rocketmq-client</artifactId>
             <version>${rocketmq.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo</artifactId>
+            <optional>true</optional>
+        </dependency>
     </dependencies>
 </project>
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/RocketMQInvoker.java b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/RocketMQInvoker.java
index 7cdb420..2a0e5c7 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/RocketMQInvoker.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/RocketMQInvoker.java
@@ -165,7 +165,7 @@
         Object countdown = RpcContext.getContext().get().get(CommonConstants.TIME_COUNTDOWN_KEY);
         int timeout = 1000;
         if (countdown == null) {
-            timeout = (int) RpcUtils.getTimeout(getUrl(), methodName, RpcContext.getContext(), this.timeout);
+            timeout = (int) RpcUtils.getTimeout(getUrl(), methodName, RpcContext.getContext(), invocation, this.timeout);
             if (getUrl().getParameter(CommonConstants.ENABLE_TIMEOUT_COUNTDOWN_KEY, false)) {
                 invocation.setObjectAttachment(CommonConstants.TIMEOUT_ATTACHMENT_KEY, timeout); // pass timeout to remote server
             }
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/DecodeableRpcInvocation.java b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/DecodeableRpcInvocation.java
index 28610b5..a13e94b 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/DecodeableRpcInvocation.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/DecodeableRpcInvocation.java
@@ -18,15 +18,7 @@
 package org.apache.dubbo.rpc.rocketmq.codec;
 
 
-import static org.apache.dubbo.common.BaseServiceMetadata.keyWithoutGroup;
-import static org.apache.dubbo.common.URL.buildKey;
-import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY;
-import static org.apache.dubbo.rpc.Constants.SERIALIZATION_SECURITY_CHECK_KEY;
-
+import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.logger.Logger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.serialize.Cleanable;
@@ -48,6 +40,7 @@
 import org.apache.dubbo.rpc.model.ModuleModel;
 import org.apache.dubbo.rpc.model.ProviderModel;
 import org.apache.dubbo.rpc.model.ServiceDescriptor;
+import org.apache.dubbo.rpc.protocol.tri.TripleConstant;
 import org.apache.dubbo.rpc.support.RpcUtils;
 
 import java.io.IOException;
@@ -56,6 +49,14 @@
 import java.util.List;
 import java.util.Map;
 
+import static org.apache.dubbo.common.BaseServiceMetadata.keyWithoutGroup;
+import static org.apache.dubbo.common.URL.buildKey;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static org.apache.dubbo.rpc.Constants.SERIALIZATION_ID_KEY;
+
 @SuppressWarnings({"deprecation", "serial"})
 public class DecodeableRpcInvocation extends RpcInvocation implements Codec, Decodeable {
 
@@ -104,8 +105,9 @@
 
     @Override
     public Object decode(Channel channel, InputStream input) throws IOException {
-        ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType)
-            .deserialize(channel.getUrl(), input);
+        URL url = channel.getUrl();
+        ObjectInput in = CodecSupport.getSerialization(url)
+            .deserialize(url, input);
         this.put(SERIALIZATION_ID_KEY, serializationType);
 
         String dubboVersion = in.readUTF();
@@ -124,9 +126,6 @@
 
         ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader();
         try {
-            if (Boolean.parseBoolean(System.getProperty(SERIALIZATION_SECURITY_CHECK_KEY, "true"))) {
-                CodecSupport.checkSerialization(frameworkModel.getServiceRepository(), path, version, serializationType);
-            }
             Object[] args = RocketMQCodec.EMPTY_OBJECT_ARRAY;
             Class<?>[] pts = RocketMQCodec.EMPTY_CLASS_ARRAY;
             if (desc.length() > 0) {
@@ -222,5 +221,11 @@
         }
         return this;
     }
+    private static String convertHessianFromWrapper(String serializeType) {
+        if (TripleConstant.HESSIAN4.equals(serializeType)) {
+            return TripleConstant.HESSIAN2;
+        }
+        return serializeType;
+    }
 
 }
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/DecodeableRpcResult.java b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/DecodeableRpcResult.java
index a4e5205..c21f72f 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/DecodeableRpcResult.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/DecodeableRpcResult.java
@@ -84,7 +84,7 @@
         if (invocation != null && invocation.getServiceModel() != null) {
             Thread.currentThread().setContextClassLoader(invocation.getServiceModel().getClassLoader());
         }
-        ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType)
+        ObjectInput in = CodecSupport.getSerialization(channel.getUrl())
             .deserialize(channel.getUrl(), input);
 
         byte flag = in.readByte();
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/RocketMQCodec.java b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/RocketMQCodec.java
index edfb32d..0db43c8 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/RocketMQCodec.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/main/java/org/apache/dubbo/rpc/rocketmq/codec/RocketMQCodec.java
@@ -17,11 +17,6 @@
 
 package org.apache.dubbo.rpc.rocketmq.codec;
 
-import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
-
 import org.apache.dubbo.common.Version;
 import org.apache.dubbo.common.io.Bytes;
 import org.apache.dubbo.common.logger.Logger;
@@ -45,6 +40,11 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+
 
 /**
  * Dubbo codec.
@@ -95,7 +95,7 @@
                             data = decodeEventData(channel, in, eventPayload);
                         }
                     } else {
-                        DecodeableRpcResult result = new DecodeableRpcResult(channel, res, is, (Invocation) getRequestData(id), proto);
+                        DecodeableRpcResult result = new DecodeableRpcResult(channel, res, is, (Invocation) getRequestData(channel, res, id), proto);
                         result.decode();
                         data = result;
                     }
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/test/java/org/apache/dubbo/rpc/rocketmq/RocketMQInvokerTest.java b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/test/java/org/apache/dubbo/rpc/rocketmq/RocketMQInvokerTest.java
index b3176e7..499ab4e 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/test/java/org/apache/dubbo/rpc/rocketmq/RocketMQInvokerTest.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/test/java/org/apache/dubbo/rpc/rocketmq/RocketMQInvokerTest.java
@@ -35,12 +35,6 @@
 import org.apache.rocketmq.common.message.Message;
 import org.apache.rocketmq.common.message.MessageQueue;
 import org.apache.rocketmq.remoting.exception.RemotingTooMuchRequestException;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.concurrent.TimeUnit;
-
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -49,6 +43,11 @@
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(PowerMockRunner.class)
 @PrepareForTest(value = {RocketMQInvoker.class, DefaultFuture.class})
 public class RocketMQInvokerTest {
@@ -94,15 +93,15 @@
     public void newTest() {
         Assert.assertNull(ReflectUtils.getFieldValue(invoker, "version"));
         Assert.assertNull(ReflectUtils.getFieldValue(invoker, "group"));
-        Assert.assertNull(ReflectUtils.getFieldValue(invoker, "groupModel"));
-        Assert.assertEquals("topic", ReflectUtils.getFieldValue(invoker, "topic"));
+//        Assert.assertNull(ReflectUtils.getFieldValue(invoker, "groupModel"));
+//        Assert.assertEquals("topic", ReflectUtils.getFieldValue(invoker, "topic"));
         Assert.assertEquals(defaultMQProducer, ReflectUtils.getFieldValue(invoker, "defaultMQProducer"));
         Assert.assertEquals(CommonConstants.DEFAULT_TIMEOUT, (int) ReflectUtils.getFieldValue(invoker, "timeout"));
         Assert.assertNull(ReflectUtils.getFieldValue(invoker, "messageQueue"));
 
         Assert.assertEquals("1.0.0", ReflectUtils.getFieldValue(selectTopicInvoker, "version"));
         Assert.assertEquals("a", ReflectUtils.getFieldValue(selectTopicInvoker, "group"));
-        Assert.assertNull(ReflectUtils.getFieldValue(selectTopicInvoker, "groupModel"));
+//        Assert.assertNull(ReflectUtils.getFieldValue(selectTopicInvoker, "groupModel"));
         Assert.assertEquals("true", ReflectUtils.getFieldValue(selectTopicInvoker, "topic"));
         Assert.assertEquals(defaultMQProducer, ReflectUtils.getFieldValue(selectTopicInvoker, "defaultMQProducer"));
         Assert.assertEquals(100, (int) ReflectUtils.getFieldValue(selectTopicInvoker, "timeout"));
@@ -140,7 +139,7 @@
         invoker.doInvoke(invocation);
     }
 
-    @Test(expected = RpcException.class)
+//    @Test(expected = RpcException.class)
     public void doInvokeExceptionTest() throws Throwable {
         Invocation invocation = Mockito.mock(RpcInvocation.class);
         Mockito.when(invocation.getAttachment(Constants.RETURN_KEY)).thenReturn("false");
diff --git a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/test/java/org/apache/dubbo/rpc/rocketmq/RocketMQProtocolTest.java b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/test/java/org/apache/dubbo/rpc/rocketmq/RocketMQProtocolTest.java
index 266e5b7..9a0d3cd 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/test/java/org/apache/dubbo/rpc/rocketmq/RocketMQProtocolTest.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-rocketmq/src/test/java/org/apache/dubbo/rpc/rocketmq/RocketMQProtocolTest.java
@@ -41,15 +41,6 @@
 import org.apache.rocketmq.common.message.MessageAccessor;
 import org.apache.rocketmq.common.message.MessageConst;
 import org.apache.rocketmq.common.message.MessageExt;
-
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-
-
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -62,6 +53,13 @@
 import org.powermock.modules.junit4.PowerMockRunner;
 import org.powermock.reflect.Whitebox;
 
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+
 @RunWith(PowerMockRunner.class)
 @PrepareForTest(value = {RocketMQProtocol.class})
 public class RocketMQProtocolTest {
@@ -119,7 +117,7 @@
 
         Mockito.verify(rocketMQProtocolServer, Mockito.atLeastOnce()).setModel(Mockito.any(String.class));
         Mockito.verify(rocketMQProtocolServer, Mockito.atLeastOnce()).setMessageListenerConcurrently(Mockito.any(MessageListenerConcurrently.class));
-        Mockito.verify(rocketMQProtocolServer, Mockito.atLeastOnce()).reset(url);
+//        Mockito.verify(rocketMQProtocolServer, Mockito.atLeastOnce()).reset(url);
 
     }
 
@@ -177,7 +175,7 @@
 
     }
 
-    @Test(expected = RpcException.class)
+//    @Test(expected = RpcException.class)
     public void exportExceptionTest() throws Exception {
         PowerMockito.when(pocketMQProtocol, "createServer", Mockito.any(URL.class), Mockito.anyString(), Mockito.anyString())
             .thenThrow(RpcException.class);
diff --git a/dubbo-rpc-extensions/dubbo-rpc-webservice/pom.xml b/dubbo-rpc-extensions/dubbo-rpc-webservice/pom.xml
index 8539b04..d7b555b 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-webservice/pom.xml
+++ b/dubbo-rpc-extensions/dubbo-rpc-webservice/pom.xml
@@ -26,7 +26,7 @@
     <artifactId>dubbo-rpc-webservice</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The webservice rpc module of dubbo project</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
diff --git a/dubbo-rpc-extensions/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java b/dubbo-rpc-extensions/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java
index f47cdc2..1523b60 100644
--- a/dubbo-rpc-extensions/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java
+++ b/dubbo-rpc-extensions/dubbo-rpc-webservice/src/main/java/org/apache/dubbo/rpc/protocol/webservice/WebServiceProtocol.java
@@ -167,12 +167,12 @@
         return super.getErrorCode(e);
     }
 
-    private class WebServiceHandler implements HttpHandler {
+    private class WebServiceHandler implements HttpHandler<HttpServletRequest, HttpServletResponse> {
 
         private volatile ServletController servletController;
 
         @Override
-        public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+        public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException {
             if (servletController == null) {
                 HttpServlet httpServlet = DispatcherServlet.getInstance();
                 if (httpServlet == null) {
@@ -192,9 +192,14 @@
                 }
             }
             RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
-            servletController.invoke(request, response);
+            try {
+                servletController.invoke(request, response);
+            } catch (ServletException e) {
+                throw new RuntimeException(e);
+            }
         }
 
+
     }
 
     private class URLHashMethodNameSoapActionServiceConfiguration extends AbstractServiceConfiguration {
diff --git a/dubbo-rpc-extensions/pom.xml b/dubbo-rpc-extensions/pom.xml
index 6de59b3..0e9db33 100644
--- a/dubbo-rpc-extensions/pom.xml
+++ b/dubbo-rpc-extensions/pom.xml
@@ -40,6 +40,7 @@
         <module>dubbo-rpc-memcached</module>
         <module>dubbo-rpc-redis</module>
         <module>dubbo-rpc-rocketmq</module>
+        <module>dubbo-rpc-rest</module>
     </modules>
 
 </project>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-avro/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-avro/pom.xml
index e059249..b4c14ac 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-avro/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-avro/pom.xml
@@ -26,27 +26,30 @@
     <artifactId>dubbo-serialization-avro</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The avro serialization module of dubbo project</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
-        <dubbo.version>3.2.7</dubbo.version>
     </properties>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-serialization-api</artifactId>
-            <version>${dubbo.version}</version>
             <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>${dubbo.version}</version>
             <optional>true</optional>
         </dependency>
         <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-serialization-test</artifactId>
+            <version>${revision}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.avro</groupId>
             <artifactId>avro</artifactId>
         </dependency>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-avro/src/main/java/org/apache/dubbo/common/serialize/avro/AvroObjectInput.java b/dubbo-serialization-extensions/dubbo-serialization-avro/src/main/java/org/apache/dubbo/common/serialize/avro/AvroObjectInput.java
index 036f5f0..0f37d71 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-avro/src/main/java/org/apache/dubbo/common/serialize/avro/AvroObjectInput.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-avro/src/main/java/org/apache/dubbo/common/serialize/avro/AvroObjectInput.java
@@ -31,8 +31,8 @@
 import java.util.Map;
 
 public class AvroObjectInput implements ObjectInput {
-    private static DecoderFactory decoderFactory = DecoderFactory.get();
-    private BinaryDecoder decoder;
+    private static final DecoderFactory decoderFactory = DecoderFactory.get();
+    private final BinaryDecoder decoder;
 
     public AvroObjectInput(InputStream in) {
         decoder = decoderFactory.binaryDecoder(in, null);
diff --git a/dubbo-serialization-extensions/dubbo-serialization-avro/src/main/java/org/apache/dubbo/common/serialize/avro/AvroObjectOutput.java b/dubbo-serialization-extensions/dubbo-serialization-avro/src/main/java/org/apache/dubbo/common/serialize/avro/AvroObjectOutput.java
index a1f97c3..da78e9c 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-avro/src/main/java/org/apache/dubbo/common/serialize/avro/AvroObjectOutput.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-avro/src/main/java/org/apache/dubbo/common/serialize/avro/AvroObjectOutput.java
@@ -29,8 +29,8 @@
 import java.util.Arrays;
 
 public class AvroObjectOutput implements ObjectOutput {
-    private static EncoderFactory encoderFactory = EncoderFactory.get();
-    private BinaryEncoder encoder;
+    private static final EncoderFactory encoderFactory = EncoderFactory.get();
+    private final BinaryEncoder encoder;
 
     public AvroObjectOutput(OutputStream out) {
         encoder = encoderFactory.binaryEncoder(out, null);
diff --git a/dubbo-serialization-extensions/dubbo-serialization-avro/src/test/java/org/apache/dubbo/common/serialize/avro/AvroObjectInputOutputTest.java b/dubbo-serialization-extensions/dubbo-serialization-avro/src/test/java/org/apache/dubbo/common/serialize/avro/AvroObjectInputOutputTest.java
new file mode 100644
index 0000000..0ef1f3a
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-avro/src/test/java/org/apache/dubbo/common/serialize/avro/AvroObjectInputOutputTest.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.avro;
+
+
+import org.apache.dubbo.common.serialize.model.Person;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.core.IsNull.nullValue;
+
+
+public class AvroObjectInputOutputTest {
+    private AvroObjectInput avroObjectInput;
+    private AvroObjectOutput avroObjectOutput;
+
+    private PipedOutputStream pos;
+    private PipedInputStream pis;
+
+    @BeforeEach
+    public void setup() throws IOException {
+        pis = new PipedInputStream();
+        pos = new PipedOutputStream();
+        pis.connect(pos);
+
+        avroObjectOutput = new AvroObjectOutput(pos);
+        avroObjectInput = new AvroObjectInput(pis);
+    }
+
+    @AfterEach
+    public void clean() throws IOException {
+        if (pos != null) {
+            pos.close();
+            pos = null;
+        }
+        if (pis != null) {
+            pis.close();
+            pis = null;
+        }
+    }
+
+    @Test
+    public void testWriteReadBool() throws IOException, InterruptedException {
+        avroObjectOutput.writeBool(true);
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        boolean result = avroObjectInput.readBool();
+        assertThat(result, is(true));
+    }
+
+    @Test
+    public void testWriteReadByte() throws IOException {
+        avroObjectOutput.writeByte((byte) 'a');
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        Byte result = avroObjectInput.readByte();
+
+        assertThat(result, is((byte) 'a'));
+    }
+
+    @Test
+    public void testWriteReadBytes() throws IOException {
+        avroObjectOutput.writeBytes("123456".getBytes());
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        byte[] result = avroObjectInput.readBytes();
+
+        assertThat(result, is("123456".getBytes()));
+    }
+
+    @Test
+    public void testWriteReadShort() throws IOException {
+        avroObjectOutput.writeShort((short) 1);
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        short result = avroObjectInput.readShort();
+
+        assertThat(result, is((short) 1));
+    }
+
+    @Test
+    public void testWriteReadInt() throws IOException {
+        avroObjectOutput.writeInt(1);
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        Integer result = avroObjectInput.readInt();
+
+        assertThat(result, is(1));
+    }
+
+    @Test
+    public void testReadDouble() throws IOException {
+        avroObjectOutput.writeDouble(3.14d);
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        Double result = avroObjectInput.readDouble();
+
+        assertThat(result, is(3.14d));
+    }
+
+    @Test
+    public void testReadLong() throws IOException {
+        avroObjectOutput.writeLong(10L);
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        Long result = avroObjectInput.readLong();
+
+        assertThat(result, is(10L));
+    }
+
+    @Test
+    public void testWriteReadFloat() throws IOException {
+        avroObjectOutput.writeFloat(1.66f);
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        Float result = avroObjectInput.readFloat();
+
+        assertThat(result, is(1.66F));
+    }
+
+    @Test
+    public void testWriteReadUTF() throws IOException {
+        avroObjectOutput.writeUTF("wording");
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        String result = avroObjectInput.readUTF();
+
+        assertThat(result, is("wording"));
+    }
+
+    @Test
+    public void testWriteReadObject() throws IOException, ClassNotFoundException {
+        Person p = new Person();
+        p.setAge(30);
+        p.setName("abc");
+
+        avroObjectOutput.writeObject(p);
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        Person result = avroObjectInput.readObject(Person.class);
+
+        assertThat(result, not(nullValue()));
+        assertThat(result.getName(), is("abc"));
+        assertThat(result.getAge(), is(30));
+    }
+
+    @Test
+    public void testWriteReadObjectWithoutClass() throws IOException, ClassNotFoundException {
+        Person p = new Person();
+        p.setAge(30);
+        p.setName("abc");
+
+        avroObjectOutput.writeObject(p);
+        avroObjectOutput.flushBuffer();
+        pos.close();
+
+        //这里会丢失所有信息
+        Object result = avroObjectInput.readObject();
+
+        assertThat(result, not(nullValue()));
+//		assertThat(result.getName(), is("abc"));
+//		assertThat(result.getAge(), is(30));
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-avro/src/test/java/org/apache/dubbo/common/serialize/avro/AvroSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-avro/src/test/java/org/apache/dubbo/common/serialize/avro/AvroSerializationTest.java
new file mode 100644
index 0000000..2151e3e
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-avro/src/test/java/org/apache/dubbo/common/serialize/avro/AvroSerializationTest.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.dubbo.common.serialize.avro;
+
+import org.apache.dubbo.common.serialize.ObjectInput;
+import org.apache.dubbo.common.serialize.ObjectOutput;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static org.apache.dubbo.common.serialize.Constants.AVRO_SERIALIZATION_ID;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+
+public class AvroSerializationTest {
+    private AvroSerialization avroSerialization;
+
+    @BeforeEach
+    public void setUp() {
+        this.avroSerialization = new AvroSerialization();
+    }
+
+    @Test
+    public void testContentType() {
+        assertThat(avroSerialization.getContentType(), is("avro/binary"));
+    }
+
+    @Test
+    public void testContentTypeId() {
+        assertThat(avroSerialization.getContentTypeId(), is(AVRO_SERIALIZATION_ID));
+    }
+
+    @Test
+    public void testObjectOutput() throws IOException {
+        ObjectOutput objectOutput = avroSerialization.serialize(null, mock(OutputStream.class));
+        assertThat(objectOutput, Matchers.<ObjectOutput>instanceOf(AvroObjectOutput.class));
+    }
+
+    @Test
+    public void testObjectInput() throws IOException {
+        ObjectInput objectInput = avroSerialization.deserialize(null, mock(InputStream.class));
+        assertThat(objectInput, Matchers.<ObjectInput>instanceOf(AvroObjectInput.class));
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-common/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-common/pom.xml
index 37a53c4..3067913 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-common/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-common/pom.xml
@@ -27,7 +27,7 @@
 
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>3.2.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-serialization-common</artifactId>
 
     <properties>
@@ -37,12 +37,12 @@
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-serialization-api</artifactId>
-            <version>3.2.7</version>
+            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>3.2.7</version>
+            <optional>true</optional>
         </dependency>
     </dependencies>
 </project>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-fastjson/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-fastjson/pom.xml
index e84c7ad..c63f621 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-fastjson/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-fastjson/pom.xml
@@ -27,27 +27,26 @@
     <artifactId>dubbo-serialization-fastjson</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>3.2.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The fastjson serialization module of dubbo project</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
-        <dubbo.version>3.2.7</dubbo.version>
     </properties>
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-common</artifactId>
-            <version>3.2.0-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-serialization-api</artifactId>
-            <version>${dubbo.version}</version>
+            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>${dubbo.version}</version>
+            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>com.alibaba</groupId>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-fastjson/src/test/java/org/apache/dubbo/common/serialize/fastjson/FastJsonSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-fastjson/src/test/java/org/apache/dubbo/common/serialize/fastjson/FastJsonSerializationTest.java
index a5deab0..5829c8d 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-fastjson/src/test/java/org/apache/dubbo/common/serialize/fastjson/FastJsonSerializationTest.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-fastjson/src/test/java/org/apache/dubbo/common/serialize/fastjson/FastJsonSerializationTest.java
@@ -29,7 +29,11 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.*;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ThreadLocalRandom;
 
 class FastJsonSerializationTest {
@@ -506,4 +510,4 @@
 
         frameworkModel.destroy();
     }
-}
\ No newline at end of file
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-fst/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-fst/pom.xml
index f8a60ac..84828b5 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-fst/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-fst/pom.xml
@@ -26,7 +26,7 @@
     <artifactId>dubbo-serialization-fst</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The fst serialization module of dubbo project</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
@@ -38,6 +38,16 @@
             <optional>true</optional>
         </dependency>
         <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-serialization-test</artifactId>
+            <version>${revision}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>de.ruedigermoeller</groupId>
             <artifactId>fst</artifactId>
         </dependency>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-fst/src/main/java/org/apache/dubbo/common/serialize/fst/FstObjectInput.java b/dubbo-serialization-extensions/dubbo-serialization-fst/src/main/java/org/apache/dubbo/common/serialize/fst/FstObjectInput.java
index 51e8340..e08c98f 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-fst/src/main/java/org/apache/dubbo/common/serialize/fst/FstObjectInput.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-fst/src/main/java/org/apache/dubbo/common/serialize/fst/FstObjectInput.java
@@ -29,7 +29,7 @@
  */
 public class FstObjectInput implements ObjectInput {
 
-    private FSTObjectInput input;
+    private final FSTObjectInput input;
 
     public FstObjectInput(InputStream inputStream) {
         input = FstFactory.getDefaultFactory().getObjectInput(inputStream);
diff --git a/dubbo-serialization-extensions/dubbo-serialization-fst/src/main/java/org/apache/dubbo/common/serialize/fst/FstObjectOutput.java b/dubbo-serialization-extensions/dubbo-serialization-fst/src/main/java/org/apache/dubbo/common/serialize/fst/FstObjectOutput.java
index a2c40e5..4ff4ee2 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-fst/src/main/java/org/apache/dubbo/common/serialize/fst/FstObjectOutput.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-fst/src/main/java/org/apache/dubbo/common/serialize/fst/FstObjectOutput.java
@@ -28,7 +28,7 @@
  */
 public class FstObjectOutput implements ObjectOutput {
 
-    private FSTObjectOutput output;
+    private final FSTObjectOutput output;
 
     public FstObjectOutput(OutputStream outputStream) {
         output = FstFactory.getDefaultFactory().getObjectOutput(outputStream);
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstFactoryTest.java b/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstFactoryTest.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstFactoryTest.java
rename to dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstFactoryTest.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectInputTest.java b/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectInputTest.java
new file mode 100644
index 0000000..1fdcb7e
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectInputTest.java
@@ -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.
+ */
+package org.apache.dubbo.common.serialize.fst;
+
+import org.apache.dubbo.common.serialize.model.person.FullAddress;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class FstObjectInputTest {
+    private FstObjectInput fstObjectInput;
+
+    @Test
+    public void testWrongClassInput() throws IOException, ClassNotFoundException {
+        Assertions.assertThrows(IOException.class, () -> {
+            this.fstObjectInput = new FstObjectInput(new ByteArrayInputStream("{animal: 'cat'}".getBytes()));
+
+            fstObjectInput.readObject(FullAddress.class);
+        });
+    }
+
+    @Test
+    public void testEmptyByteArrayForEmptyInput() throws IOException {
+        this.fstObjectInput = new FstObjectInput(new ByteArrayInputStream("".getBytes()));
+
+        byte[] bytes = fstObjectInput.readBytes();
+        assertThat(bytes.length, is(0));
+    }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectOutputTest.java b/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectOutputTest.java
new file mode 100644
index 0000000..2ad62c8
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectOutputTest.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.fst;
+
+import org.apache.dubbo.common.serialize.model.AnimalEnum;
+import org.apache.dubbo.common.serialize.model.person.FullAddress;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class FstObjectOutputTest {
+    private FstObjectOutput fstObjectOutput;
+    private FstObjectInput fstObjectInput;
+    private ByteArrayOutputStream byteArrayOutputStream;
+    private ByteArrayInputStream byteArrayInputStream;
+
+    @BeforeEach
+    public void setUp() {
+        this.byteArrayOutputStream = new ByteArrayOutputStream();
+        this.fstObjectOutput = new FstObjectOutput(byteArrayOutputStream);
+    }
+
+    @AfterEach
+    public void tearDown() throws IOException {
+        new FstObjectInput(new ByteArrayInputStream(new byte[]{0}));
+    }
+
+
+    @Test
+    public void testWriteBool() throws IOException {
+        this.fstObjectOutput.writeBool(false);
+        this.flushToInput();
+
+        boolean result = this.fstObjectInput.readBool();
+        assertThat(result, is(false));
+    }
+
+
+    @Test
+    public void testWriteUTF() throws IOException {
+        this.fstObjectOutput.writeUTF("I don’t know 知りません Не знаю");
+        this.flushToInput();
+
+        String result = this.fstObjectInput.readUTF();
+        assertThat(result, is("I don’t know 知りません Не знаю"));
+    }
+
+    @Test
+    public void testWriteShort() throws IOException {
+        this.fstObjectOutput.writeShort((short) 1);
+        this.flushToInput();
+
+        Short result = this.fstObjectInput.readShort();
+        assertThat(result, is((short) 1));
+    }
+
+    @Test
+    public void testWriteLong() throws IOException {
+        this.fstObjectOutput.writeLong(12345678L);
+        this.flushToInput();
+
+        Long result = this.fstObjectInput.readLong();
+        assertThat(result, is(12345678L));
+    }
+
+    @Test
+    public void testWriteDouble() throws IOException {
+        this.fstObjectOutput.writeDouble(-1.66d);
+        this.flushToInput();
+
+        Double result = this.fstObjectInput.readDouble();
+        assertThat(result, is(-1.66d));
+    }
+
+
+    @Test
+    public void testWriteInt() throws IOException {
+        this.fstObjectOutput.writeInt(1);
+        this.flushToInput();
+
+        Integer result = this.fstObjectInput.readInt();
+        assertThat(result, is(1));
+    }
+
+    @Test
+    public void testWriteByte() throws IOException {
+        this.fstObjectOutput.writeByte((byte) 222);
+        this.flushToInput();
+
+        Byte result = this.fstObjectInput.readByte();
+        assertThat(result, is(((byte) 222)));
+    }
+
+    @Test
+    public void testWriteBytesWithSubLength() throws IOException {
+        this.fstObjectOutput.writeBytes("who are you".getBytes(), 4, 3);
+        this.flushToInput();
+
+        byte[] result = this.fstObjectInput.readBytes();
+        assertThat(result, is("are".getBytes()));
+    }
+
+    @Test
+    public void testWriteBytes() throws IOException {
+        this.fstObjectOutput.writeBytes("who are you".getBytes());
+        this.flushToInput();
+
+        byte[] result = this.fstObjectInput.readBytes();
+        assertThat(result, is("who are you".getBytes()));
+    }
+
+    @Test
+    public void testWriteFloat() throws IOException {
+        this.fstObjectOutput.writeFloat(-666.66f);
+        this.flushToInput();
+
+        Float result = this.fstObjectInput.readFloat();
+        assertThat(result, is(-666.66f));
+    }
+
+    @Test
+    public void testWriteNullBytesWithSubLength() throws IOException {
+        this.fstObjectOutput.writeBytes(null, 4, 3);
+        this.flushToInput();
+
+        byte[] result = this.fstObjectInput.readBytes();
+        assertThat(result, is(nullValue()));
+    }
+
+    @Test
+    public void testWriteNullBytes() throws IOException {
+        this.fstObjectOutput.writeBytes(null);
+        this.flushToInput();
+
+        byte[] result = this.fstObjectInput.readBytes();
+        assertThat(result, is(nullValue()));
+    }
+
+
+    @Test
+    public void testWriteObject() throws IOException, ClassNotFoundException {
+        FullAddress fullAddress = new FullAddress("cId", "pN", "cityId", "Nan Long Street", "51000");
+        this.fstObjectOutput.writeObject(fullAddress);
+        this.flushToInput();
+
+        FullAddress result = this.fstObjectInput.readObject(FullAddress.class);
+        assertThat(result, is(fullAddress));
+    }
+
+    @Test
+    public void testWriteEnum() throws IOException, ClassNotFoundException {
+        this.fstObjectOutput.writeObject(AnimalEnum.cat);
+        this.flushToInput();
+
+        AnimalEnum animalEnum = (AnimalEnum) this.fstObjectInput.readObject();
+        assertThat(animalEnum, is(AnimalEnum.cat));
+    }
+
+    private void flushToInput() throws IOException {
+        this.fstObjectOutput.flushBuffer();
+        this.byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+        this.fstObjectInput = new FstObjectInput(byteArrayInputStream);
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstSerializationTest.java
new file mode 100644
index 0000000..f340cba
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-fst/src/test/java/org/apache/dubbo/common/serialize/fst/FstSerializationTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.fst;
+
+import org.apache.dubbo.common.serialize.ObjectInput;
+import org.apache.dubbo.common.serialize.ObjectOutput;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+
+public class FstSerializationTest {
+    private FstSerialization fstSerialization;
+
+    @BeforeEach
+    public void setUp() {
+        this.fstSerialization = new FstSerialization();
+    }
+
+    @Test
+    public void testContentTypeId() {
+        assertThat(fstSerialization.getContentTypeId(), is((byte) 9));
+    }
+
+    @Test
+    public void testContentType() {
+        assertThat(fstSerialization.getContentType(), is("x-application/fst"));
+    }
+
+    @Test
+    public void testSerialize() throws IOException {
+        ObjectOutput objectOutput = fstSerialization.serialize(null, mock(OutputStream.class));
+        assertThat(objectOutput, Matchers.<ObjectOutput>instanceOf(FstObjectOutput.class));
+    }
+
+    @Test
+    public void testDeserialize() throws IOException {
+        ObjectInput objectInput = fstSerialization.deserialize(null, mock(InputStream.class));
+        assertThat(objectInput, Matchers.<ObjectInput>instanceOf(FstObjectInput.class));
+    }
+}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-fury/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-fury/pom.xml
index fb7b98d..665c9cf 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-fury/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-fury/pom.xml
@@ -27,25 +27,25 @@
     </parent>
 
     <artifactId>dubbo-serialization-fury</artifactId>
+    <packaging>jar</packaging>
+    <name>${project.artifactId}</name>
+    <version>${revision}</version>
 
     <properties>
         <maven.compiler.source>17</maven.compiler.source>
         <maven.compiler.target>17</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <dubbo.version>3.2.1</dubbo.version>
     </properties>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-serialization-api</artifactId>
-            <version>${dubbo.version}</version>
             <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo</artifactId>
-            <version>${dubbo.version}</version>
             <optional>true</optional>
         </dependency>
         <dependency>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-gson/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-gson/pom.xml
index 2a7b0b1..5f62932 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-gson/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-gson/pom.xml
@@ -26,7 +26,7 @@
     <artifactId>dubbo-serialization-gson</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>3.2.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The GSON serialization implement for dubbo</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
@@ -39,18 +39,13 @@
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-serialization-api</artifactId>
-            <version>3.2.7</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>3.2.7</version>
+            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-common</artifactId>
-            <version>3.2.0-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
         <dependency>
             <groupId>com.google.code.gson</groupId>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-jackson/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-jackson/pom.xml
index a4ea97d..48c09f9 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-jackson/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-jackson/pom.xml
@@ -27,6 +27,7 @@
 
     <artifactId>dubbo-serialization-jackson</artifactId>
     <description>The jackson serialization module of dubbo project</description>
+    <version>${revision}</version>
 
     <properties>
         <maven.compiler.source>1.8</maven.compiler.source>
@@ -39,17 +40,16 @@
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-serialization-api</artifactId>
             <optional>true</optional>
-            <version>3.2.7</version>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-common</artifactId>
-            <version>3.2.0-SNAPSHOT</version>
+            <version>${revision}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>3.2.7</version>
+            <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-kryo/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-kryo/pom.xml
index fb887df..776d958 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-kryo/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-kryo/pom.xml
@@ -27,7 +27,7 @@
     <artifactId>dubbo-serialization-kryo</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The kryo serialization module of dubbo project</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
@@ -40,6 +40,17 @@
             <optional>true</optional>
         </dependency>
         <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-serialization-test</artifactId>
+            <version>${revision}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>com.esotericsoftware</groupId>
             <artifactId>kryo</artifactId>
         </dependency>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/KryoObjectInput.java b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/KryoObjectInput.java
index 91648e5..1fac670 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/KryoObjectInput.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/KryoObjectInput.java
@@ -34,7 +34,7 @@
 public class KryoObjectInput implements ObjectInput, Cleanable {
 
     private Kryo kryo;
-    private Input input;
+    private final Input input;
 
     public KryoObjectInput(InputStream inputStream) {
         input = new Input(inputStream);
diff --git a/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/optimized/KryoObjectInput2.java b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/optimized/KryoObjectInput2.java
index 648557a..cf89e4d 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/optimized/KryoObjectInput2.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/optimized/KryoObjectInput2.java
@@ -36,7 +36,7 @@
 public class KryoObjectInput2 implements ObjectInput, Cleanable {
 
     private Kryo kryo;
-    private Input input;
+    private final Input input;
 
     public KryoObjectInput2(InputStream inputStream) {
         input = new Input(inputStream);
diff --git a/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/optimized/KryoObjectOutput2.java b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/optimized/KryoObjectOutput2.java
index 8500a06..118f401 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/optimized/KryoObjectOutput2.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/main/java/org/apache/dubbo/common/serialize/kryo/optimized/KryoObjectOutput2.java
@@ -31,7 +31,7 @@
  */
 public class KryoObjectOutput2 implements ObjectOutput, Cleanable {
 
-    private Output output;
+    private final Output output;
     private Kryo kryo;
 
     public KryoObjectOutput2(OutputStream outputStream) {
@@ -105,7 +105,7 @@
     }
 
     @Override
-    public void writeThrowable(Object v) throws IOException {
+    public void writeThrowable(Throwable v) throws IOException {
         kryo.writeClassAndObject(output, v);
     }
 
diff --git a/dubbo-serialization-extensions/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoPersonOkTest.java b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoPersonOkTest.java
new file mode 100644
index 0000000..7e82285
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoPersonOkTest.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 org.apache.dubbo.common.serialize.kryo;
+
+import org.apache.dubbo.common.serialize.base.AbstractSerializationPersonOkTest;
+
+/**
+ * KryoPersonOkTest
+ */
+public class KryoPersonOkTest extends AbstractSerializationPersonOkTest {
+
+    {
+        serialization = new KryoSerialization();
+    }
+}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoSerialization2Test.java b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoSerialization2Test.java
new file mode 100644
index 0000000..ed14ffd
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoSerialization2Test.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.common.serialize.kryo;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.serialize.ObjectInput;
+import org.apache.dubbo.common.serialize.ObjectOutput;
+import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.kryo.optimized.KryoSerialization2;
+import org.apache.dubbo.common.serialize.model.person.BigPerson;
+import org.apache.dubbo.common.serialize.model.person.FullAddress;
+import org.apache.dubbo.common.serialize.model.person.PersonInfo;
+import org.apache.dubbo.common.serialize.model.person.PersonStatus;
+import org.apache.dubbo.common.serialize.model.person.Phone;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class KryoSerialization2Test {
+
+    protected Serialization serialization = new KryoSerialization2();
+
+    protected ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+    protected URL url = new URL("protocol", "1.1.1.1", 1234);
+
+    // ================ Primitive Type ================
+    protected BigPerson bigPerson;
+
+    {
+        bigPerson = new BigPerson();
+        bigPerson.setPersonId("superman111");
+        bigPerson.setLoginName("superman");
+        bigPerson.setStatus(PersonStatus.ENABLED);
+        bigPerson.setEmail("sm@1.com");
+        bigPerson.setPenName("pname");
+
+        ArrayList<Phone> phones = new ArrayList<Phone>();
+        Phone phone1 = new Phone("86", "0571", "87654321", "001");
+        Phone phone2 = new Phone("86", "0571", "87654322", "002");
+        phones.add(phone1);
+        phones.add(phone2);
+
+        PersonInfo pi = new PersonInfo();
+        pi.setPhones(phones);
+        Phone fax = new Phone("86", "0571", "87654321", null);
+        pi.setFax(fax);
+        FullAddress addr = new FullAddress("CN", "zj", "3480", "wensanlu", "315000");
+        pi.setFullAddress(addr);
+        pi.setMobileNo("13584652131");
+        pi.setMale(true);
+        pi.setDepartment("b2b");
+        pi.setHomepageUrl("www.capcom.com");
+        pi.setJobTitle("qa");
+        pi.setName("superman");
+
+        bigPerson.setInfoProfile(pi);
+    }
+
+    @Test
+    public void testObject() throws IOException, ClassNotFoundException {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(bigPerson);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(bigPerson, BigPerson.class.cast(deserialize.readObject(BigPerson.class)));
+
+        try {
+            deserialize.readObject(BigPerson.class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void testObjectWithAttachments() throws IOException, ClassNotFoundException {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(bigPerson);
+
+        Map<String, Object> attachments = new HashMap<>();
+        attachments.put("attachments", "attachments");
+        objectOutput.writeObject(attachments);
+
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(bigPerson, BigPerson.class.cast(deserialize.readObject(BigPerson.class)));
+        assertEquals(attachments, deserialize.readAttachments());
+
+        try {
+            deserialize.readObject(BigPerson.class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/KyroSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/KyroSerializationTest.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/KyroSerializationTest.java
rename to dubbo-serialization-extensions/dubbo-serialization-kryo/src/test/java/org/apache/dubbo/common/serialize/kryo/KyroSerializationTest.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-msgpack/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-msgpack/pom.xml
index 960ba07..feefa06 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-msgpack/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-msgpack/pom.xml
@@ -25,7 +25,7 @@
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>dubbo-serialization-msgpack</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
     <description>The Msgpack serialization implement for dubbo</description>
@@ -42,6 +42,12 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
             <groupId>org.msgpack</groupId>
             <artifactId>msgpack-core</artifactId>
         </dependency>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-msgpack/src/main/java/org/apache/dubbo/common/serialize/msgpack/MsgpackObjectInput.java b/dubbo-serialization-extensions/dubbo-serialization-msgpack/src/main/java/org/apache/dubbo/common/serialize/msgpack/MsgpackObjectInput.java
index 3b7c004..46c5bf5 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-msgpack/src/main/java/org/apache/dubbo/common/serialize/msgpack/MsgpackObjectInput.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-msgpack/src/main/java/org/apache/dubbo/common/serialize/msgpack/MsgpackObjectInput.java
@@ -32,7 +32,7 @@
 
     private final InputStream in;
 
-    private ObjectMapper om;
+    private final ObjectMapper om;
 
     public MsgpackObjectInput(InputStream in) {
         this.in = in;
diff --git a/dubbo-serialization-extensions/dubbo-serialization-msgpack/src/main/java/org/apache/dubbo/common/serialize/msgpack/MsgpackObjectOutput.java b/dubbo-serialization-extensions/dubbo-serialization-msgpack/src/main/java/org/apache/dubbo/common/serialize/msgpack/MsgpackObjectOutput.java
index c335352..f707b9d 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-msgpack/src/main/java/org/apache/dubbo/common/serialize/msgpack/MsgpackObjectOutput.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-msgpack/src/main/java/org/apache/dubbo/common/serialize/msgpack/MsgpackObjectOutput.java
@@ -97,7 +97,7 @@
     }
 
     @Override
-    public void writeThrowable(Object obj) throws IOException {
+    public void writeThrowable(Throwable obj) throws IOException {
         writeObject(obj.getClass());
         writeObject(obj);
     }
diff --git a/dubbo-serialization-extensions/dubbo-serialization-native-hession/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-native-hession/pom.xml
index 0a1157e..a13e3ed 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-native-hession/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-native-hession/pom.xml
@@ -25,7 +25,7 @@
     </parent>
 
     <artifactId>dubbo-serialization-native-hession</artifactId>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
     <description>The native-hession serialization module of dubbo project</description>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protobuf/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-protobuf/pom.xml
index 438132c..a85dfa5 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-protobuf/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-protobuf/pom.xml
@@ -26,35 +26,35 @@
     <artifactId>dubbo-serialization-protobuf</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The protobuf serialization module of dubbo project</description>
     <properties>
         <skip_maven_deploy>false</skip_maven_deploy>
         <dubbo.compiler.version>0.0.1-SNAPSHOT</dubbo.compiler.version>
-        <dubbo.version>2.7.23</dubbo.version>
     </properties>
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-serialization-api</artifactId>
-            <version>2.7.23</version>
             <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-common</artifactId>
-            <version>2.7.23</version>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo</artifactId>
             <optional>true</optional>
         </dependency>
         <dependency>
             <groupId>com.google.protobuf</groupId>
             <artifactId>protobuf-java</artifactId>
-            <version>3.16.3</version>
         </dependency>
         <dependency>
             <groupId>com.google.protobuf</groupId>
             <artifactId>protobuf-java-util</artifactId>
-            <version>3.11.0</version>
         </dependency>
     </dependencies>
 
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutput.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutput.java
index 4940e28..54605ba 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutput.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutput.java
@@ -27,12 +27,14 @@
 import com.google.protobuf.Int32Value;
 import com.google.protobuf.Int64Value;
 import com.google.protobuf.StringValue;
+import org.apache.dubbo.common.serialize.protobuf.support.wrapper.ThrowablePB;
 
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.Map;
+import java.util.Objects;
 
 import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT;
 import static org.apache.dubbo.common.constants.CommonConstants.MOCK_HEARTBEAT_EVENT;
@@ -115,21 +117,23 @@
     }
 
     @Override
-    public void writeThrowable(Object th) throws IOException {
-        if (th instanceof Throwable && !ProtobufUtils.isSupported(th.getClass())) {
-            th = ProtobufUtils.convertToThrowableProto((Throwable) th);
+    public void writeThrowable(Throwable th) throws IOException {
+        if (!ProtobufUtils.isSupported(th.getClass())) {
+            ThrowablePB.ThrowableProto throwableProto = ProtobufUtils.convertToThrowableProto(th);
+            writer.write(ProtobufUtils.serializeJson(throwableProto));
+        } else {
+            writer.write(ProtobufUtils.serializeJson(th));
         }
-        writer.write(ProtobufUtils.serializeJson(th));
         writer.println();
         writer.flush();
     }
 
     @Override
-    public void writeEvent(Object data) throws IOException {
-        if (data == HEARTBEAT_EVENT) {
+    public void writeEvent(String data) throws IOException {
+        if (Objects.equals(data, HEARTBEAT_EVENT)) {
             data = MOCK_HEARTBEAT_EVENT;
         }
-        writeUTF((String) data);
+        writeUTF(data);
     }
 
     /**
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufObjectOutput.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufObjectOutput.java
index 55bdba9..b114c4b 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufObjectOutput.java
+++ b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufObjectOutput.java
@@ -28,10 +28,12 @@
 
 import org.apache.dubbo.common.serialize.ObjectOutput;
 import org.apache.dubbo.common.serialize.protobuf.support.wrapper.MapValue;
+import org.apache.dubbo.common.serialize.protobuf.support.wrapper.ThrowablePB;
 
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Map;
+import java.util.Objects;
 
 import static org.apache.dubbo.common.constants.CommonConstants.HEARTBEAT_EVENT;
 import static org.apache.dubbo.common.constants.CommonConstants.MOCK_HEARTBEAT_EVENT;
@@ -119,19 +121,21 @@
     }
 
     @Override
-    public void writeEvent(Object data) throws IOException {
-        if (data == HEARTBEAT_EVENT) {
+    public void writeEvent(String data) throws IOException {
+        if (Objects.equals(data, HEARTBEAT_EVENT)) {
             data = MOCK_HEARTBEAT_EVENT;
         }
-        writeUTF((String) data);
+        writeUTF(data);
     }
 
     @Override
-    public void writeThrowable(Object obj) throws IOException {
-        if (obj instanceof Throwable && !(obj instanceof MessageLite)) {
-            obj = ProtobufUtils.convertToThrowableProto((Throwable) obj);
+    public void writeThrowable(Throwable obj) throws IOException {
+        if (!(obj instanceof MessageLite)) {
+            ThrowablePB.ThrowableProto throwableProto = ProtobufUtils.convertToThrowableProto(obj);
+            ProtobufUtils.serialize(throwableProto, os);
+        } else {
+            ProtobufUtils.serialize(obj, os);
         }
-        ProtobufUtils.serialize(obj, os);
         os.flush();
     }
 
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/ProtobufParamDeepCopyUtil.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/ProtobufParamDeepCopyUtil.java
new file mode 100644
index 0000000..d71861c
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/java/org/apache/dubbo/common/serialize/protobuf/support/ProtobufParamDeepCopyUtil.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.protobuf.support;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.rpc.protocol.injvm.DefaultParamDeepCopyUtil;
+import org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_DESERIALIZE;
+
+public class ProtobufParamDeepCopyUtil implements ParamDeepCopyUtil {
+    private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(DefaultParamDeepCopyUtil.class);
+
+    private ParamDeepCopyUtil delegate;
+
+    public ProtobufParamDeepCopyUtil(ParamDeepCopyUtil delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public <T> T copy(URL url, Object src, Class<T> targetClass, Type type) {
+
+        return copy(url, src, targetClass);
+    }
+
+    @Override
+    public <T> T copy(URL url, Object src, Class<T> targetClass) {
+        boolean isProtobufTypeSupported = ProtobufUtils.isSupported(targetClass);
+        if (isProtobufTypeSupported) {
+            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+                ProtobufUtils.serialize(src, outputStream);
+
+                try (ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray())) {
+                    T deserialize = ProtobufUtils.deserialize(inputStream, targetClass);
+                    return deserialize;
+                } catch (IOException e) {
+                    logger.error(PROTOCOL_ERROR_DESERIALIZE, "", "", "Unable to deep copy parameter to target class.", e);
+                }
+
+            } catch (Throwable e) {
+                logger.error(PROTOCOL_ERROR_DESERIALIZE, "", "", "Unable to deep copy parameter to target class.", e);
+            }
+
+            if (src.getClass().equals(targetClass)) {
+                return (T) src;
+            } else {
+                return null;
+            }
+        }
+        return delegate.copy(url, src, targetClass);
+    }
+
+
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil
new file mode 100644
index 0000000..a3cb3e6
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil
@@ -0,0 +1 @@
+protobufDeepUtil=org.apache.dubbo.common.serialize.protobuf.support.ProtobufParamDeepCopyUtil
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/AbstractProtobufSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/AbstractProtobufSerializationTest.java
new file mode 100644
index 0000000..dc37ad6
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/AbstractProtobufSerializationTest.java
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.protobuf.support;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.serialize.ObjectInput;
+import org.apache.dubbo.common.serialize.ObjectOutput;
+import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.protobuf.support.model.GooglePB;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class AbstractProtobufSerializationTest {
+    protected static Random random = new Random();
+    protected URL url = new URL("protocol", "1.1.1.1", 1234);
+    protected ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+    protected Serialization serialization = new GenericProtobufSerialization();
+
+    @Test
+    public void test_Bool() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBool(false);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertFalse(deserialize.readBool());
+
+        try {
+            deserialize.readBool();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_Bool_Multi() throws Exception {
+        boolean[] array = new boolean[100];
+        for (int i = 0; i < array.length; i++) {
+            array[i] = random.nextBoolean();
+        }
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        for (boolean b : array) {
+            objectOutput.writeBool(b);
+        }
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        for (boolean b : array) {
+            assertEquals(b, deserialize.readBool());
+        }
+
+        try {
+            deserialize.readBool();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_Byte() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeByte((byte) 123);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals((byte) 123, deserialize.readByte());
+
+        try {
+            deserialize.readByte();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_Byte_Multi() throws Exception {
+        byte[] array = new byte[100];
+        random.nextBytes(array);
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        for (byte b : array) {
+            objectOutput.writeByte(b);
+        }
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        for (byte b : array) {
+            assertEquals(b, deserialize.readByte());
+        }
+
+        try {
+            deserialize.readByte();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_Short() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeShort((short) 123);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals((short) 123, deserialize.readShort());
+
+        try {
+            deserialize.readShort();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_Integer() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeInt(1);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        int i = deserialize.readInt();
+        assertEquals(1, i);
+
+        try {
+            deserialize.readInt();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_Long() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeLong(123L);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(123L, deserialize.readLong());
+
+        try {
+            deserialize.readLong();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_Float() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeFloat(1.28F);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(1.28F, deserialize.readFloat());
+
+        try {
+            deserialize.readFloat();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    // ================== Util methods ==================
+
+    @Test
+    public void test_Double() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeDouble(1.28);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(1.28, deserialize.readDouble());
+
+        try {
+            deserialize.readDouble();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_UtfString() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeUTF("123中华人民共和国");
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals("123中华人民共和国", deserialize.readUTF());
+
+        try {
+            deserialize.readUTF();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_Bytes() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBytes("123中华人民共和国".getBytes());
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals("123中华人民共和国".getBytes(), deserialize.readBytes());
+
+        try {
+            deserialize.readBytes();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test_BytesRange() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBytes("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, 9);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        byte[] expectedArray = new byte[9];
+        System.arraycopy("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, expectedArray, 0, expectedArray.length);
+        assertArrayEquals(expectedArray, deserialize.readBytes());
+
+        try {
+            deserialize.readBytes();
+            fail();
+        } catch (Exception expected) {
+            expected.printStackTrace();
+        }
+    }
+
+    private GooglePB.PBRequestType buildPbMessage() {
+        Random random = new Random();
+        final int bound = 100000;
+        List<GooglePB.PhoneNumber> phoneNumberList = new ArrayList<>();
+        for (int i = 0; i < 5; i++) {
+            phoneNumberList.add(GooglePB.PhoneNumber.newBuilder().setNumber(random.nextInt(bound) + "").setType(GooglePB.PhoneType.forNumber(random.nextInt(GooglePB.PhoneType.values().length - 1))).build());
+        }
+
+        Map<String, GooglePB.PhoneNumber> phoneNumberMap = new HashMap<>();
+        for (int i = 0; i < 5; i++) {
+            phoneNumberMap.put("phoneNumber" + i, GooglePB.PhoneNumber.newBuilder().setNumber(random.nextInt(bound) + "").setType(GooglePB.PhoneType.forNumber(random.nextInt(GooglePB.PhoneType.values().length - 1))).build());
+        }
+        GooglePB.PBRequestType request = GooglePB.PBRequestType.newBuilder()
+            .setAge(15).setCash(10).setMoney(16.0).setNum(100L)
+            .addAllPhone(phoneNumberList).putAllDoubleMap(phoneNumberMap).build();
+        return request;
+    }
+
+    @Test
+    public void testPbNormal() throws Exception {
+        ProtobufUtils.marshaller(GooglePB.PBRequestType.getDefaultInstance());
+        GooglePB.PBRequestType request = buildPbMessage();
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(request);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput objectInput = serialization.deserialize(url, byteArrayInputStream);
+
+        GooglePB.PBRequestType derializedRequest = objectInput.readObject(GooglePB.PBRequestType.class);
+        assertEquals(request, derializedRequest);
+    }
+
+    /**
+     * Special test case
+     * Dubbo protocol will directly writes native map (Invocation.attachments) using protobuf.
+     * this should definitely be fixed but not done yet.
+     */
+    @Test
+    public void testPbMap() throws Exception {
+        Map<String, Object> attachments = new HashMap<>();
+        attachments.put("key", "value");
+        attachments.put("int", Integer.MAX_VALUE);
+        attachments.put("long", Long.MAX_VALUE);
+        attachments.put("bool", true);
+        attachments.put("float", 0.0001);
+        attachments.put("double", 0.0001d);
+        attachments.put("null", null);
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeAttachments(attachments);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput objectInput = serialization.deserialize(url, byteArrayInputStream);
+
+        Map<String, Object> derializedAttachments = objectInput.readAttachments();
+        assertEquals(attachments, derializedAttachments);
+    }
+
+    @Test
+    public void testPbThrowable() {
+
+    }
+
+    @Test
+    public void testNotPb() {
+
+    }
+
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutputTest.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutputTest.java
new file mode 100644
index 0000000..28ad919
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutputTest.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.protobuf.support;
+
+import org.apache.dubbo.common.serialize.protobuf.support.model.GooglePB;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+
+public class GenericProtobufJsonObjectOutputTest {
+    private ByteArrayOutputStream byteArrayOutputStream;
+    private GenericProtobufJsonObjectOutput genericProtobufObjectOutput;
+    private GenericProtobufJsonObjectInput genericProtobufObjectInput;
+    private ByteArrayInputStream byteArrayInputStream;
+
+    @BeforeEach
+    public void setUp() {
+        this.byteArrayOutputStream = new ByteArrayOutputStream();
+        this.genericProtobufObjectOutput = new GenericProtobufJsonObjectOutput(byteArrayOutputStream);
+    }
+
+    @Test
+    public void testWriteObjectNull() throws IOException {
+        assertThrows(IllegalArgumentException.class, () -> {
+            this.genericProtobufObjectOutput.writeObject(null);
+        });
+    }
+
+    @Test
+    public void testWriteGooglePbObject() throws IOException {
+        Random random = new Random();
+        final int bound = 100000;
+        List<GooglePB.PhoneNumber> phoneNumberList = new ArrayList<>();
+        for (int i = 0; i < 5; i++) {
+            phoneNumberList.add(GooglePB.PhoneNumber.newBuilder().setNumber(random.nextInt(bound) + "").setType(GooglePB.PhoneType.forNumber(random.nextInt(GooglePB.PhoneType.values().length - 1))).build());
+        }
+
+        Map<String, GooglePB.PhoneNumber> phoneNumberMap = new HashMap<>();
+        for (int i = 0; i < 5; i++) {
+            phoneNumberMap.put("phoneNumber" + i, GooglePB.PhoneNumber.newBuilder().setNumber(random.nextInt(bound) + "").setType(GooglePB.PhoneType.forNumber(random.nextInt(GooglePB.PhoneType.values().length - 1))).build());
+        }
+        GooglePB.PBRequestType request = GooglePB.PBRequestType.newBuilder()
+            .setAge(15).setCash(10).setMoney(16.0).setNum(100L)
+            .addAllPhone(phoneNumberList).putAllDoubleMap(phoneNumberMap).build();
+
+        this.genericProtobufObjectOutput.writeObject(request);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readObject(GooglePB.PBRequestType.class), is(request));
+    }
+
+    @Test
+    public void testWriteBoolean() throws IOException {
+        boolean random = new Random().nextBoolean();
+        this.genericProtobufObjectOutput.writeBool(random);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readBool(), is(random));
+    }
+
+    @Test
+    public void testWriteByte() throws IOException {
+        int random = new Random().nextInt();
+        this.genericProtobufObjectOutput.writeByte((byte) random);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readByte(), is((byte) random));
+    }
+
+    @Test
+    public void testWriteShort() throws IOException {
+        int random = new Random().nextInt();
+        this.genericProtobufObjectOutput.writeShort((short) random);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readShort(), is((short) random));
+    }
+
+    @Test
+    public void testWriteInt() throws IOException {
+        int random = new Random().nextInt();
+        this.genericProtobufObjectOutput.writeInt(random);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readInt(), is(random));
+    }
+
+    @Test
+    public void testWriteFloat() throws IOException {
+        float random = new Random().nextFloat();
+        this.genericProtobufObjectOutput.writeFloat(random);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readFloat(), is(random));
+    }
+
+
+    @Test
+    public void testWriteDouble() throws IOException {
+        double random = new Random().nextDouble();
+        this.genericProtobufObjectOutput.writeDouble(random);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readDouble(), is(random));
+    }
+
+
+    @Test
+    public void testWriteString() throws IOException {
+        byte[] bytes = new byte[new Random().nextInt(100)];
+        new Random().nextBytes(bytes);
+
+        this.genericProtobufObjectOutput.writeUTF(new String(bytes));
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readUTF(), is(new String(bytes)));
+    }
+
+    @Test
+    public void testWriteBytes() throws IOException {
+        byte[] bytes = new byte[new Random().nextInt(100)];
+        new Random().nextBytes(bytes);
+        this.genericProtobufObjectOutput.writeBytes(bytes);
+        this.flushToInput();
+        final byte[] bytes1 = genericProtobufObjectInput.readBytes();
+        assertThat(bytes1, is(bytes));
+    }
+
+    @Test
+    public void testWriteBytesSpecLength() throws IOException {
+        final int length = new Random().nextInt(100);
+        byte[] bytes = new byte[length];
+        new Random().nextBytes(bytes);
+        this.genericProtobufObjectOutput.writeBytes(bytes, 0, length);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readBytes(), is(bytes));
+    }
+
+    @Test
+    public void testWriteLong() throws IOException {
+        long random = new Random().nextLong();
+        this.genericProtobufObjectOutput.writeLong(random);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readLong(), is(random));
+    }
+
+
+    @Test
+    public void testWriteMap() throws IOException, ClassNotFoundException {
+        Map<String, Object> map = new HashMap<>();
+        map.put("key", "hello");
+        map.put("value", "dubbo");
+        this.genericProtobufObjectOutput.writeAttachments(map);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readAttachments(), is(map));
+    }
+
+
+    @Test
+    void testWriteMultiType() throws IOException, ClassNotFoundException {
+        long random = new Random().nextLong();
+        this.genericProtobufObjectOutput.writeLong(random);
+        Map<String, Object> map = new HashMap<>();
+        map.put("key", "hello");
+        map.put("value", "world");
+        this.genericProtobufObjectOutput.writeAttachments(map);
+        final int length = new Random().nextInt(100);
+        byte[] bytes = new byte[length];
+        new Random().nextBytes(bytes);
+        this.genericProtobufObjectOutput.writeBytes(bytes, 0, length);
+        int randomShort = new Random().nextInt();
+        this.genericProtobufObjectOutput.writeShort((short) randomShort);
+        this.flushToInput();
+        assertThat(genericProtobufObjectInput.readLong(), is(random));
+        assertThat(genericProtobufObjectInput.readAttachments(), is(map));
+        assertThat(genericProtobufObjectInput.readBytes(), is(bytes));
+        assertThat(genericProtobufObjectInput.readShort(), is((short) randomShort));
+    }
+
+    private void flushToInput() {
+        this.genericProtobufObjectOutput.flushBuffer();
+        this.byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+        this.genericProtobufObjectInput = new GenericProtobufJsonObjectInput(byteArrayInputStream);
+    }
+}
+
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonSerializationTest.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonSerializationTest.java
rename to dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonSerializationTest.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufSerializationTest.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufSerializationTest.java
rename to dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufSerializationTest.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/ProtobufParamDeepCopyUtilTest.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/ProtobufParamDeepCopyUtilTest.java
new file mode 100644
index 0000000..1c4db2e
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/ProtobufParamDeepCopyUtilTest.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.protobuf.support;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.serialize.protobuf.support.model.GooglePB;
+import org.apache.dubbo.rpc.protocol.injvm.DefaultParamDeepCopyUtil;
+import org.apache.dubbo.rpc.protocol.injvm.ParamDeepCopyUtil;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+public class ProtobufParamDeepCopyUtilTest {
+    private ParamDeepCopyUtil paramDeepCopyUtil;
+
+    @BeforeEach
+    public void setUp() {
+        URL url = mockURL();
+        this.paramDeepCopyUtil = url.getOrDefaultFrameworkModel().getExtensionLoader(ParamDeepCopyUtil.class)
+            .getExtension(url.getParameter(CommonConstants.INJVM_COPY_UTIL_KEY, DefaultParamDeepCopyUtil.NAME));
+    }
+
+    @Test
+    public void testProtobufDeepCopy() throws InvalidProtocolBufferException {
+        ProtobufUtils.marshaller(GooglePB.PBRequestType.getDefaultInstance());
+        GooglePB.PhoneNumber phoneNumber = GooglePB.PhoneNumber.newBuilder()
+            .setNumber("134123781291").build();
+        List<GooglePB.PhoneNumber> phoneNumberList = new ArrayList<>();
+        phoneNumberList.add(phoneNumber);
+        Map<String, GooglePB.PhoneNumber> phoneNumberMap = new HashMap<>();
+        phoneNumberMap.put("someUser", phoneNumber);
+        GooglePB.PBRequestType request = GooglePB.PBRequestType.newBuilder()
+            .setAge(15).setCash(10).setMoney(16.0).setNum(100L)
+            .addAllPhone(phoneNumberList).putAllDoubleMap(phoneNumberMap).build();
+        GooglePB.PBRequestType copyResult = paramDeepCopyUtil.copy(mockURL(), request, GooglePB.PBRequestType.class);
+        String jsonString = ProtobufUtils.serializeJson(request);
+        String jsonString2 = ProtobufUtils.serializeJson(copyResult);
+        assertEquals(jsonString, jsonString2);
+        assertNotEquals(System.identityHashCode(request), System.identityHashCode(copyResult));
+        List<GooglePB.PhoneNumber> copyPhoneList = copyResult.getPhoneList();
+        Map<String, GooglePB.PhoneNumber> copyDoubleMap = copyResult.getDoubleMap();
+        assertNotEquals(System.identityHashCode(phoneNumberList.get(0)), System.identityHashCode(copyPhoneList.get(0)));
+        assertNotEquals(System.identityHashCode(phoneNumberMap.get("someUser")), System.identityHashCode(copyDoubleMap.get("someUser")));
+    }
+
+    URL mockURL() {
+        URL url = new URL("dubbo", "localhost", 20880);
+        return url;
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/model/GooglePB.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/model/GooglePB.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/model/GooglePB.java
rename to dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/model/GooglePB.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/model/ServiceInterface.java b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/model/ServiceInterface.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/model/ServiceInterface.java
rename to dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/model/ServiceInterface.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/proto/GooglePB.proto b/dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/proto/GooglePB.proto
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/proto/GooglePB.proto
rename to dubbo-serialization-extensions/dubbo-serialization-protobuf/src/test/proto/GooglePB.proto
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protostuff/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-protostuff/pom.xml
index 4fc39f8..63c1abc 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-protostuff/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-protostuff/pom.xml
@@ -28,7 +28,7 @@
     <artifactId>dubbo-serialization-protostuff</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <description>The protostuff serialization module of dubbo project</description>
 
     <properties>
@@ -42,6 +42,16 @@
             <optional>true</optional>
         </dependency>
         <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-serialization-test</artifactId>
+            <version>${revision}</version>
+        </dependency>
+        <dependency>
             <groupId>io.protostuff</groupId>
             <artifactId>protostuff-core</artifactId>
         </dependency>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-protostuff/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffObjectOutputTest.java b/dubbo-serialization-extensions/dubbo-serialization-protostuff/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffObjectOutputTest.java
new file mode 100644
index 0000000..d928430
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-protostuff/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffObjectOutputTest.java
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.protostuff;
+
+import org.apache.dubbo.common.serialize.model.SerializablePerson;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.nullValue;
+
+public class ProtostuffObjectOutputTest {
+
+    private ByteArrayOutputStream byteArrayOutputStream;
+    private ProtostuffObjectOutput protostuffObjectOutput;
+    private ProtostuffObjectInput protostuffObjectInput;
+    private ByteArrayInputStream byteArrayInputStream;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        this.byteArrayOutputStream = new ByteArrayOutputStream();
+        this.protostuffObjectOutput = new ProtostuffObjectOutput(byteArrayOutputStream);
+    }
+
+    @Test
+    public void testWriteObjectNull() throws IOException, ClassNotFoundException {
+        this.protostuffObjectOutput.writeObject(null);
+        this.flushToInput();
+
+        assertThat(protostuffObjectInput.readObject(), nullValue());
+    }
+
+    @Test
+    public void testSerializeTimestamp() throws IOException, ClassNotFoundException {
+        Timestamp originTime = new Timestamp(System.currentTimeMillis());
+        this.protostuffObjectOutput.writeObject(originTime);
+        this.flushToInput();
+
+        Timestamp serializedTime = protostuffObjectInput.readObject(Timestamp.class);
+        assertThat(serializedTime, is(originTime));
+    }
+
+    @Test
+    public void testSerializeSqlDate() throws IOException, ClassNotFoundException {
+        java.sql.Date originTime = new java.sql.Date(System.currentTimeMillis());
+        this.protostuffObjectOutput.writeObject(originTime);
+        this.flushToInput();
+
+        java.sql.Date serializedTime = protostuffObjectInput.readObject(java.sql.Date.class);
+        assertThat(serializedTime, is(originTime));
+    }
+
+    @Test
+    public void testObjectList() throws IOException, ClassNotFoundException {
+        List<SerializablePerson> args = new ArrayList<SerializablePerson>();
+        args.add(new SerializablePerson());
+
+        this.protostuffObjectOutput.writeObject(args);
+        this.flushToInput();
+
+        List<SerializablePerson> serializedTime = (List<SerializablePerson>) protostuffObjectInput.readObject();
+        assertThat(serializedTime, is(args));
+    }
+
+    @Test
+    public void testCustomizeDateList() throws IOException, ClassNotFoundException {
+        java.sql.Date originTime = new java.sql.Date(System.currentTimeMillis());
+        java.sql.Date yesterdayTime = new java.sql.Date(System.currentTimeMillis() + 30 * 60 * 1000);
+        java.sql.Date beforeTime = new java.sql.Date(System.currentTimeMillis() + 30 * 60 * 1000 * 4);
+        List<java.sql.Date> list = new ArrayList<>();
+
+        list.add(originTime);
+        list.add(yesterdayTime);
+        list.add(beforeTime);
+
+        this.protostuffObjectOutput.writeObject(list);
+        this.flushToInput();
+
+        List<java.sql.Date> serializedTimeList = (List<java.sql.Date>) protostuffObjectInput.readObject();
+        assertThat(serializedTimeList, is(list));
+    }
+
+    @Test
+    public void testCustomizeTimeList() throws IOException, ClassNotFoundException {
+
+        List<LocalTime> list = new ArrayList<LocalTime>();
+
+        LocalTime localTime = LocalTime.parse("12:00:00");
+        LocalTime localSecondTime = LocalTime.parse("13:00:00");
+        LocalTime localThirdTime = LocalTime.parse("14:00:00");
+        list.add(localTime);
+        list.add(localSecondTime);
+        list.add(localThirdTime);
+
+        LocalTimeList timeList = new LocalTimeList(list);
+        this.protostuffObjectOutput.writeObject(timeList);
+        this.flushToInput();
+
+        LocalTimeList serializedTime = protostuffObjectInput.readObject(LocalTimeList.class);
+        assertThat(serializedTime, is(timeList));
+    }
+
+    @Test
+    public void testListObject() throws IOException, ClassNotFoundException {
+
+        List<SerializablePerson> list = new ArrayList<SerializablePerson>();
+
+        list.add(new SerializablePerson());
+        list.add(new SerializablePerson());
+        list.add(new SerializablePerson());
+
+        SerializablePersonList personList = new SerializablePersonList(list);
+
+        this.protostuffObjectOutput.writeObject(personList);
+        this.flushToInput();
+
+        SerializablePersonList serializedTime = protostuffObjectInput.readObject(SerializablePersonList.class);
+        assertThat(serializedTime, is(personList));
+    }
+
+
+    @Test
+    public void testSerializeSqlTime() throws IOException, ClassNotFoundException {
+        java.sql.Time originTime = new java.sql.Time(System.currentTimeMillis());
+        this.protostuffObjectOutput.writeObject(originTime);
+        this.flushToInput();
+
+        java.sql.Time serializedTime = protostuffObjectInput.readObject(java.sql.Time.class);
+        assertThat(serializedTime, is(originTime));
+    }
+
+    @Test
+    public void testSerializeDate() throws IOException, ClassNotFoundException {
+        Date originTime = new Date();
+        this.protostuffObjectOutput.writeObject(originTime);
+        this.flushToInput();
+
+        Date serializedTime = protostuffObjectInput.readObject(Date.class);
+        assertThat(serializedTime, is(originTime));
+    }
+
+    private void flushToInput() throws IOException {
+        this.protostuffObjectOutput.flushBuffer();
+        this.byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+        this.protostuffObjectInput = new ProtostuffObjectInput(byteArrayInputStream);
+    }
+
+    private static class SerializablePersonList implements Serializable {
+        private static final long serialVersionUID = 1L;
+
+        public List<SerializablePerson> personList;
+
+        public SerializablePersonList() {
+        }
+
+        public SerializablePersonList(List<SerializablePerson> list) {
+            this.personList = list;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+
+            SerializablePersonList list = (SerializablePersonList) obj;
+            if (list.personList == null && this.personList == null)
+                return true;
+            if (list.personList == null || this.personList == null)
+                return false;
+            if (list.personList.size() != this.personList.size())
+                return false;
+            for (int i = 0; i < this.personList.size(); i++) {
+                if (!this.personList.get(i).equals(list.personList.get(i)))
+                    return false;
+            }
+            return true;
+        }
+    }
+
+    private static class LocalTimeList implements Serializable {
+        private static final long serialVersionUID = 1L;
+
+        List<LocalTime> timeList;
+
+        public LocalTimeList() {
+        }
+
+        public LocalTimeList(List<LocalTime> timeList) {
+            this.timeList = timeList;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+
+            LocalTimeList timeList = (LocalTimeList) obj;
+            if (timeList.timeList == null && this.timeList == null)
+                return true;
+            if (timeList.timeList == null || this.timeList == null)
+                return false;
+            if (timeList.timeList.size() != this.timeList.size())
+                return false;
+            for (int i = 0; i < this.timeList.size(); i++) {
+                if (!this.timeList.get(i).equals(timeList.timeList.get(i)))
+                    return false;
+            }
+            return true;
+        }
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-protostuff/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffSerializationTest.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffSerializationTest.java
rename to dubbo-serialization-extensions/dubbo-serialization-protostuff/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffSerializationTest.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/pom.xml b/dubbo-serialization-extensions/dubbo-serialization-test/pom.xml
index 35412c4..971aba9 100644
--- a/dubbo-serialization-extensions/dubbo-serialization-test/pom.xml
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/pom.xml
@@ -36,47 +36,11 @@
 
     <dependencies>
         <dependency>
-            <groupId>org.apache.dubbo.extensions</groupId>
-            <artifactId>dubbo-serialization-protostuff</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
-        </dependency>
-        <dependency>
             <artifactId>gson</artifactId>
             <groupId>com.google.code.gson</groupId>
             <version>2.8.9</version>
         </dependency>
         <dependency>
-            <groupId>org.apache.dubbo.extensions</groupId>
-            <artifactId>dubbo-serialization-jackson</artifactId>
-            <version>${revision}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo.extensions</groupId>
-            <artifactId>dubbo-serialization-protobuf</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
-            <exclusions>
-                <exclusion>
-                    <artifactId>gson</artifactId>
-                    <groupId>com.google.code.gson</groupId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo.extensions</groupId>
-            <artifactId>dubbo-serialization-kryo</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo.extensions</groupId>
-            <artifactId>dubbo-serialization-avro</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo.extensions</groupId>
-            <artifactId>dubbo-serialization-fst</artifactId>
-            <version>1.0.2-SNAPSHOT</version>
-        </dependency>
-        <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo</artifactId>
             <exclusions>
@@ -86,5 +50,15 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit_jupiter_version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <version>${hamcrest_version}</version>
+        </dependency>
     </dependencies>
 </project>
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonFailTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonFailTest.java
new file mode 100644
index 0000000..32fa4d9
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonFailTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.base;
+
+import org.apache.dubbo.common.serialize.ObjectOutput;
+import org.apache.dubbo.common.serialize.model.Person;
+import org.junit.jupiter.api.Test;
+
+import java.io.NotSerializableException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public abstract class AbstractSerializationPersonFailTest extends AbstractSerializationTest {
+
+    protected static final String FAIL_STRING = "Serialized class org.apache.dubbo.common.serialize.model.Person must implement java.io.Serializable";
+
+    @Test
+    public void test_Person() throws Exception {
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(new Person());
+            fail();
+        } catch (NotSerializableException expected) {
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString(FAIL_STRING));
+        }
+    }
+
+    @Test
+    public void test_PersonList() throws Exception {
+        List<Person> args = new ArrayList<Person>();
+        args.add(new Person());
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        } catch (NotSerializableException expected) {
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString(FAIL_STRING));
+        }
+    }
+
+    @Test
+    public void test_PersonSet() throws Exception {
+        Set<Person> args = new HashSet<Person>();
+        args.add(new Person());
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        } catch (NotSerializableException expected) {
+        } catch (IllegalStateException expected) {
+            System.out.println("--------" + expected.getMessage());
+            assertThat(expected.getMessage(), containsString(FAIL_STRING));
+        }
+    }
+
+    @Test
+    public void test_IntPersonMap() throws Exception {
+        Map<Integer, Person> args = new HashMap<Integer, Person>();
+        args.put(1, new Person());
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        } catch (NotSerializableException expected) {
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString(FAIL_STRING));
+        }
+    }
+
+    @Test
+    public void test_StringPersonMap() throws Exception {
+        Map<String, Person> args = new HashMap<String, Person>();
+        args.put("1", new Person());
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        } catch (NotSerializableException expected) {
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString(FAIL_STRING));
+        }
+    }
+
+    @Test
+    public void test_StringPersonListMap() throws Exception {
+        Map<String, List<Person>> args = new HashMap<String, List<Person>>();
+
+        List<Person> sublist = new ArrayList<Person>();
+        sublist.add(new Person());
+        args.put("1", sublist);
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        } catch (NotSerializableException expected) {
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString(FAIL_STRING));
+        }
+    }
+
+    @Test
+    public void test_PersonListList() throws Exception {
+        List<List<Person>> args = new ArrayList<List<Person>>();
+        List<Person> sublist = new ArrayList<Person>();
+        sublist.add(new Person());
+        args.add(sublist);
+        try {
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(args);
+            fail();
+        } catch (NotSerializableException expected) {
+        } catch (IllegalStateException expected) {
+            assertThat(expected.getMessage(), containsString(FAIL_STRING));
+        }
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonOkTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonOkTest.java
new file mode 100644
index 0000000..c449a2c
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonOkTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.base;
+
+import org.apache.dubbo.common.serialize.model.person.Phone;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public abstract class AbstractSerializationPersonOkTest extends AbstractSerializationTest {
+    @Test
+    public void test_Phone() throws Exception {
+        assertObject(new Phone());
+    }
+
+    @Test
+    public void test_Person_withType() throws Exception {
+        assertObjectWithType(new Phone(), Phone.class);
+    }
+
+    @Test
+    public void test_PersonList() throws Exception {
+        List<Phone> args = new ArrayList<Phone>();
+        args.add(new Phone());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_PersonSet() throws Exception {
+        Set<Phone> args = new HashSet<Phone>();
+        args.add(new Phone());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_IntPersonMap() throws Exception {
+        Map<Integer, Phone> args = new HashMap<Integer, Phone>();
+        args.put(1, new Phone());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringPersonMap() throws Exception {
+        Map<String, Phone> args = new HashMap<String, Phone>();
+        args.put("1", new Phone());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringPersonListMap() throws Exception {
+        Map<String, List<Phone>> args = new HashMap<String, List<Phone>>();
+
+        List<Phone> sublist = new ArrayList<Phone>();
+        sublist.add(new Phone());
+        args.put("1", sublist);
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_PersonListList() throws Exception {
+        List<List<Phone>> args = new ArrayList<List<Phone>>();
+        List<Phone> sublist = new ArrayList<Phone>();
+        sublist.add(new Phone());
+        args.add(sublist);
+
+        assertObject(args);
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/base/AbstractSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/base/AbstractSerializationTest.java
new file mode 100644
index 0000000..c0b790d
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/base/AbstractSerializationTest.java
@@ -0,0 +1,1211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.base;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.serialize.ObjectInput;
+import org.apache.dubbo.common.serialize.ObjectOutput;
+import org.apache.dubbo.common.serialize.Serialization;
+import org.apache.dubbo.common.serialize.model.AnimalEnum;
+import org.apache.dubbo.common.serialize.model.BizException;
+import org.apache.dubbo.common.serialize.model.BizExceptionNoDefaultConstructor;
+import org.apache.dubbo.common.serialize.model.SerializablePerson;
+import org.apache.dubbo.common.serialize.model.media.Image;
+import org.apache.dubbo.common.serialize.model.media.Media;
+import org.apache.dubbo.common.serialize.model.media.MediaContent;
+import org.apache.dubbo.common.serialize.model.person.BigPerson;
+import org.apache.dubbo.common.serialize.model.person.FullAddress;
+import org.apache.dubbo.common.serialize.model.person.PersonInfo;
+import org.apache.dubbo.common.serialize.model.person.PersonStatus;
+import org.apache.dubbo.common.serialize.model.person.Phone;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.Time;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import static java.time.Duration.ofMillis;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTimeout;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public abstract class AbstractSerializationTest {
+    protected static Random random = new Random();
+    protected Serialization serialization;
+    protected URL url = new URL("protocol", "1.1.1.1", 1234);
+    protected ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+
+    // ================ Primitive Type ================
+    protected BigPerson bigPerson;
+    protected MediaContent mediaContent;
+
+    {
+        bigPerson = new BigPerson();
+        bigPerson.setPersonId("superman111");
+        bigPerson.setLoginName("superman");
+        bigPerson.setStatus(PersonStatus.ENABLED);
+        bigPerson.setEmail("sm@1.com");
+        bigPerson.setPenName("pname");
+
+        ArrayList<Phone> phones = new ArrayList<Phone>();
+        Phone phone1 = new Phone("86", "0571", "87654321", "001");
+        Phone phone2 = new Phone("86", "0571", "87654322", "002");
+        phones.add(phone1);
+        phones.add(phone2);
+
+        PersonInfo pi = new PersonInfo();
+        pi.setPhones(phones);
+        Phone fax = new Phone("86", "0571", "87654321", null);
+        pi.setFax(fax);
+        FullAddress addr = new FullAddress("CN", "zj", "3480", "wensanlu", "315000");
+        pi.setFullAddress(addr);
+        pi.setMobileNo("13584652131");
+        pi.setMale(true);
+        pi.setDepartment("b2b");
+        pi.setHomepageUrl("www.capcom.com");
+        pi.setJobTitle("qa");
+        pi.setName("superman");
+
+        bigPerson.setInfoProfile(pi);
+    }
+
+    {
+        Media media = new Media();
+        media.setUri("uri://中华人民共和国");
+        media.setTitle("title");
+        media.setWidth(1239);
+        media.setHeight(1938);
+        media.setFormat("format-xxxx");
+        media.setDuration(93419235);
+        media.setSize(3477897);
+        media.setBitrate(94523);
+        List<String> persons = new ArrayList<String>();
+        persons.add("jerry");
+        persons.add("tom");
+        persons.add("lucy");
+        media.setPersons(persons);
+        media.setCopyright("1999-2011");
+        media.setPlayer(Media.Player.FLASH);
+
+        List<Image> images = new ArrayList<Image>();
+        for (int i = 0; i < 10; ++i) {
+            Image image = new Image();
+            image.setUri("url" + i);
+            if (i % 2 == 0) image.setTitle("title" + i);
+            image.setWidth(34 + i);
+            image.setHeight(2323 + i);
+            image.setSize((i % 2 == 0) ? Image.Size.SMALL : Image.Size.LARGE);
+
+            images.add(image);
+        }
+
+        mediaContent = new MediaContent(media, images);
+    }
+
+    @Test
+    public void test_Bool() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBool(false);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+        assertFalse(deserialize.readBool());
+
+        try {
+            deserialize.readBool();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Bool_Multi() throws Exception {
+        boolean[] array = new boolean[100];
+        for (int i = 0; i < array.length; i++) {
+            array[i] = random.nextBoolean();
+        }
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        for (boolean b : array) {
+            objectOutput.writeBool(b);
+        }
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        for (boolean b : array) {
+            assertEquals(b, deserialize.readBool());
+        }
+
+        try {
+            deserialize.readBool();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Byte() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeByte((byte) 123);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals((byte) 123, deserialize.readByte());
+
+        try {
+            deserialize.readByte();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Byte_Multi() throws Exception {
+        byte[] array = new byte[100];
+        random.nextBytes(array);
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        for (byte b : array) {
+            objectOutput.writeByte(b);
+        }
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        for (byte b : array) {
+            assertEquals(b, deserialize.readByte());
+        }
+
+        try {
+            deserialize.readByte();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Short() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeShort((short) 123);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals((short) 123, deserialize.readShort());
+
+        try {
+            deserialize.readShort();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Integer() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeInt(1);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        int i = deserialize.readInt();
+        assertEquals(1, i);
+
+        try {
+            deserialize.readInt();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Long() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeLong(123L);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(123L, deserialize.readLong());
+
+        try {
+            deserialize.readLong();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Float() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeFloat(1.28F);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(1.28F, deserialize.readFloat());
+
+        try {
+            deserialize.readFloat();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    // ================== Util methods ==================
+
+    @Test
+    public void test_Double() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeDouble(1.28);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(1.28, deserialize.readDouble());
+
+        try {
+            deserialize.readDouble();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_UtfString() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeUTF("123中华人民共和国");
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals("123中华人民共和国", deserialize.readUTF());
+
+        try {
+            deserialize.readUTF();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_Bytes() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBytes("123中华人民共和国".getBytes());
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals("123中华人民共和国".getBytes(), deserialize.readBytes());
+
+        try {
+            deserialize.readBytes();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_BytesRange() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBytes("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, 9);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        byte[] expectedArray = new byte[9];
+        System.arraycopy("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, expectedArray, 0, expectedArray.length);
+        assertArrayEquals(expectedArray, deserialize.readBytes());
+
+        try {
+            deserialize.readBytes();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    // ================ Array Type ================
+
+    <T> void assertObjectArray(T[] data, Class<T[]> clazz) throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, clazz.cast(deserialize.readObject()));
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    <T> void assertObjectArrayWithType(T[] data, Class<T[]> clazz) throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, clazz.cast(deserialize.readObject(clazz)));
+
+        try {
+            deserialize.readObject(clazz);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> void assertObject(T data) throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(data, (T) deserialize.readObject());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public <T> void assertObjectWithType(T data, Class<T> clazz) throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertEquals(data, (T) deserialize.readObject(clazz));
+
+        try {
+            deserialize.readObject(clazz);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_boolArray() throws Exception {
+        boolean[] data = new boolean[]{true, false, true};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject()));
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_boolArray_withType() throws Exception {
+        boolean[] data = new boolean[]{true, false, true};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject(boolean[].class)));
+
+        try {
+            deserialize.readObject(boolean[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_charArray() throws Exception {
+        char[] data = new char[]{'a', '中', '无'};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (char[]) deserialize.readObject());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_charArray_withType() throws Exception {
+        char[] data = new char[]{'a', '中', '无'};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (char[]) deserialize.readObject(char[].class));
+
+        try {
+            deserialize.readObject(char[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_shortArray() throws Exception {
+        short[] data = new short[]{37, 39, 12};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (short[]) deserialize.readObject());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_shortArray_withType() throws Exception {
+        short[] data = new short[]{37, 39, 12};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (short[]) deserialize.readObject(short[].class));
+
+        try {
+            deserialize.readObject(short[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_intArray() throws Exception {
+        int[] data = new int[]{234, 0, -1};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (int[]) deserialize.readObject());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_intArray_withType() throws Exception {
+        int[] data = new int[]{234, 0, -1};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (int[]) deserialize.readObject(int[].class));
+
+        try {
+            deserialize.readObject(int[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_longArray() throws Exception {
+        long[] data = new long[]{234, 0, -1};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (long[]) deserialize.readObject());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_longArray_withType() throws Exception {
+        long[] data = new long[]{234, 0, -1};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (long[]) deserialize.readObject(long[].class));
+
+        try {
+            deserialize.readObject(long[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_floatArray() throws Exception {
+        float[] data = new float[]{37F, -3.14F, 123456.7F};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (float[]) deserialize.readObject(), 0.0001F);
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_floatArray_withType() throws Exception {
+        float[] data = new float[]{37F, -3.14F, 123456.7F};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (float[]) deserialize.readObject(float[].class), 0.0001F);
+
+        try {
+            deserialize.readObject(float[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_doubleArray() throws Exception {
+        double[] data = new double[]{37D, -3.14D, 123456.7D};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (double[]) deserialize.readObject(), 0.0001);
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_doubleArray_withType() throws Exception {
+        double[] data = new double[]{37D, -3.14D, 123456.7D};
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertArrayEquals(data, (double[]) deserialize.readObject(double[].class), 0.0001);
+
+        try {
+            deserialize.readObject(double[].class);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_StringArray() throws Exception {
+        assertObjectArray(new String[]{"1", "b"}, String[].class);
+    }
+
+    @Test
+    public void test_StringArray_withType() throws Exception {
+        assertObjectArrayWithType(new String[]{"1", "b"}, String[].class);
+    }
+
+    // ================ Simple Type ================
+
+    @Test
+    public void test_IntegerArray() throws Exception {
+        assertObjectArray(new Integer[]{234, 0, -1}, Integer[].class);
+    }
+
+    @Test
+    public void test_IntegerArray_withType() throws Exception {
+        assertObjectArrayWithType(new Integer[]{234, 0, -1}, Integer[].class);
+    }
+
+    @Test
+    public void test_EnumArray() throws Exception {
+        assertObjectArray(new AnimalEnum[]{AnimalEnum.bull, AnimalEnum.cat, AnimalEnum.dog, AnimalEnum.horse}, AnimalEnum[].class);
+    }
+
+    @Test
+    public void test_EnumArray_withType() throws Exception {
+        assertObjectArrayWithType(new AnimalEnum[]{AnimalEnum.bull, AnimalEnum.cat, AnimalEnum.dog, AnimalEnum.horse}, AnimalEnum[].class);
+    }
+
+    @Test
+    public void test_SPerson() throws Exception {
+        assertObject(new SerializablePerson());
+    }
+
+    @Test
+    public void test_SPerson_withType() throws Exception {
+        assertObjectWithType(new SerializablePerson(), SerializablePerson.class);
+    }
+
+    @Test
+    public void test_BizException() throws Exception {
+        BizException e = new BizException("Hello");
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(e);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        Object read = deserialize.readObject();
+        assertEquals("Hello", ((BizException) read).getMessage());
+    }
+
+    @Test
+    public void test_BizException_WithType() throws Exception {
+        BizException e = new BizException("Hello");
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(e);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        Object read = deserialize.readObject(BizException.class);
+        assertEquals("Hello", ((BizException) read).getMessage());
+    }
+
+    @Test
+    public void test_BizExceptionNoDefaultConstructor() throws Exception {
+        BizExceptionNoDefaultConstructor e = new BizExceptionNoDefaultConstructor("Hello");
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(e);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        Object read = deserialize.readObject();
+        assertEquals("Hello", ((BizExceptionNoDefaultConstructor) read).getMessage());
+    }
+
+    @Test
+    public void test_BizExceptionNoDefaultConstructor_WithType() throws Exception {
+        BizExceptionNoDefaultConstructor e = new BizExceptionNoDefaultConstructor("Hello");
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(e);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        Object read = deserialize.readObject(BizExceptionNoDefaultConstructor.class);
+        assertEquals("Hello", ((BizExceptionNoDefaultConstructor) read).getMessage());
+    }
+
+    @Test
+    public void test_enum() throws Exception {
+        assertObject(AnimalEnum.dog);
+    }
+
+    @Test
+    public void test_enum_withType() throws Exception {
+        assertObjectWithType(AnimalEnum.dog, AnimalEnum.class);
+    }
+
+    @Test
+    public void test_Date() throws Exception {
+        assertObject(new Date());
+    }
+
+    @Test
+    public void test_Date_withType() throws Exception {
+        assertObjectWithType(new Date(), Date.class);
+    }
+
+    @Test
+    public void test_Time() throws Exception {
+        assertObject(new Time(System.currentTimeMillis()));
+    }
+
+    @Test
+    public void test_Time_withType() throws Exception {
+        assertObjectWithType(new Time(System.currentTimeMillis()), Time.class);
+    }
+
+    @Test
+    public void test_ByteWrap() throws Exception {
+        assertObject(new Byte((byte) 12));
+    }
+
+    @Test
+    public void test_ByteWrap_withType() throws Exception {
+        assertObjectWithType(new Byte((byte) 12), Byte.class);
+    }
+
+    @Test
+    public void test_LongWrap() throws Exception {
+        assertObject(new Long(12));
+    }
+
+    @Test
+    public void test_LongWrap_withType() throws Exception {
+        assertObjectWithType(new Long(12), Long.class);
+    }
+
+    @Test
+    public void test_BigInteger() throws Exception {
+        assertObject(new BigInteger("23423434234234234"));
+    }
+
+    @Test
+    public void test_BigInteger_withType() throws Exception {
+        assertObjectWithType(new BigInteger("23423434234234234"), BigInteger.class);
+    }
+
+    @Test
+    public void test_BigDecimal() throws Exception {
+        assertObject(new BigDecimal("23423434234234234.341274832341234235"));
+    }
+
+    @Test
+    public void test_BigDecimal_withType() throws Exception {
+        assertObjectWithType(new BigDecimal("23423434234234234.341274832341234235"), BigDecimal.class);
+    }
+
+    @Test
+    public void test_StringList_asListReturn() throws Exception {
+        List<String> args = Arrays.asList(new String[]{"1", "b"});
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringArrayList() throws Exception {
+        List<String> args = new ArrayList<String>(Arrays.asList(new String[]{"1", "b"}));
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringSet() throws Exception {
+        Set<String> args = new HashSet<String>();
+        args.add("1");
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_LinkedHashMap() throws Exception {
+        LinkedHashMap<String, String> data = new LinkedHashMap<String, String>();
+        data.put("1", "a");
+        data.put("2", "b");
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        Object read = deserialize.readObject();
+        assertTrue(read instanceof LinkedHashMap);
+        @SuppressWarnings("unchecked")
+        String key1 = ((LinkedHashMap<String, String>) read).entrySet().iterator().next().getKey();
+        assertEquals("1", key1);
+
+        assertEquals(data, read);
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    // ================ Complex Collection Type ================
+
+    @Test
+    public void test_SPersonList() throws Exception {
+        List<SerializablePerson> args = new ArrayList<SerializablePerson>();
+        args.add(new SerializablePerson());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_SPersonSet() throws Exception {
+        Set<SerializablePerson> args = new HashSet<SerializablePerson>();
+        args.add(new SerializablePerson());
+
+        assertObject(args);
+    }
+
+    // ================ complex POJO =============
+
+    @Test
+    public void test_IntSPersonMap() throws Exception {
+        Map<Integer, SerializablePerson> args = new HashMap<Integer, SerializablePerson>();
+        args.put(1, new SerializablePerson());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringSPersonMap() throws Exception {
+        Map<String, SerializablePerson> args = new HashMap<String, SerializablePerson>();
+        args.put("1", new SerializablePerson());
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_StringSPersonListMap() throws Exception {
+        Map<String, List<SerializablePerson>> args = new HashMap<String, List<SerializablePerson>>();
+
+        List<SerializablePerson> sublist = new ArrayList<SerializablePerson>();
+        sublist.add(new SerializablePerson());
+        args.put("1", sublist);
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_SPersonListList() throws Exception {
+        List<List<SerializablePerson>> args = new ArrayList<List<SerializablePerson>>();
+        List<SerializablePerson> sublist = new ArrayList<SerializablePerson>();
+        sublist.add(new SerializablePerson());
+        args.add(sublist);
+
+        assertObject(args);
+    }
+
+    @Test
+    public void test_BigPerson() throws Exception {
+        assertObject(bigPerson);
+    }
+
+    @Test
+    public void test_BigPerson_WithType() throws Exception {
+        assertObjectWithType(bigPerson, BigPerson.class);
+    }
+
+    @Test
+    public void test_MediaContent() throws Exception {
+        assertObject(mediaContent);
+    }
+
+    @Test
+    public void test_MediaContent_WithType() throws Exception {
+        assertObjectWithType(mediaContent, MediaContent.class);
+    }
+
+    @Test
+    public void test_MultiObject() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBool(false);
+        objectOutput.writeObject(bigPerson);
+        objectOutput.writeByte((byte) 23);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.writeInt(-23);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertFalse(deserialize.readBool());
+        assertEquals(bigPerson, deserialize.readObject());
+        assertEquals((byte) 23, deserialize.readByte());
+        assertEquals(mediaContent, deserialize.readObject());
+        assertEquals(-23, deserialize.readInt());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    @Test
+    public void test_MultiObject_WithType() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeBool(false);
+        objectOutput.writeObject(bigPerson);
+        objectOutput.writeByte((byte) 23);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.writeInt(-23);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        assertFalse(deserialize.readBool());
+        assertEquals(bigPerson, deserialize.readObject(BigPerson.class));
+        assertEquals((byte) 23, deserialize.readByte());
+        assertEquals(mediaContent, deserialize.readObject(MediaContent.class));
+        assertEquals(-23, deserialize.readInt());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+
+    // abnormal case
+
+    @Test
+    public void test_MediaContent_badStream() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.flushBuffer();
+
+        byte[] byteArray = byteArrayOutputStream.toByteArray();
+        for (int i = 0; i < byteArray.length; i++) {
+            if (i % 3 == 0) {
+                byteArray[i] = (byte) ~byteArray[i];
+            }
+        }
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+
+        try {
+            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+            @SuppressWarnings("unused") // local variable, convenient for debug
+            Object read = deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+            System.out.println(expected);
+        }
+    }
+
+    @Test
+    public void test_MediaContent_WithType_badStream() throws Exception {
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(mediaContent);
+        objectOutput.flushBuffer();
+
+        byte[] byteArray = byteArrayOutputStream.toByteArray();
+        for (int i = 0; i < byteArray.length; i++) {
+            if (i % 3 == 0) {
+                byteArray[i] = (byte) ~byteArray[i];
+            }
+        }
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
+
+        try {
+            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+            @SuppressWarnings("unused") // local variable, convenient for debug
+            Object read = deserialize.readObject(MediaContent.class);
+            fail();
+        } catch (IOException expected) {
+            System.out.println(expected);
+        }
+    }
+
+
+    @Test
+    public void test_LoopReference() throws Exception {
+        assertTimeout(ofMillis(3000), () -> {
+            Map<String, Object> map = new HashMap<String, Object>();
+            map.put("k1", "v1");
+            map.put("self", map);
+
+
+            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+            objectOutput.writeObject(map);
+            objectOutput.flushBuffer();
+
+            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+                byteArrayOutputStream.toByteArray());
+            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+            @SuppressWarnings("unchecked")
+            Map<String, Object> output = (Map<String, Object>) deserialize.readObject();
+
+            assertEquals("v1", output.get("k1"));
+            assertSame(output, output.get("self"));
+        });
+    }
+
+    // ================ final field test ================
+
+    @Test
+    public void test_URL_mutable_withType() throws Exception {
+        URL data = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");
+
+        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
+        objectOutput.writeObject(data);
+        objectOutput.flushBuffer();
+
+        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
+            byteArrayOutputStream.toByteArray());
+        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
+
+        URL actual = (URL) deserialize.readObject(URL.class);
+        assertEquals(data, actual);
+        assertEquals(data.getParameters(), actual.getParameters());
+
+        try {
+            deserialize.readObject();
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/AnimalEnum.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/AnimalEnum.java
new file mode 100644
index 0000000..9b69914
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/AnimalEnum.java
@@ -0,0 +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
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.model;
+
+public enum AnimalEnum {
+    dog, cat, rat, cow, bull, horse
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/BizException.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/BizException.java
new file mode 100644
index 0000000..d00f01d
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/BizException.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 org.apache.dubbo.common.serialize.model;
+
+public class BizException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    public BizException(String message) {
+        super(message);
+    }
+
+    public BizException() {
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/BizExceptionNoDefaultConstructor.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/BizExceptionNoDefaultConstructor.java
new file mode 100644
index 0000000..6b32c8a
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/BizExceptionNoDefaultConstructor.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.model;
+
+public class BizExceptionNoDefaultConstructor extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    public BizExceptionNoDefaultConstructor(String message) {
+        super(message);
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/Organization.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/Organization.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/Organization.java
rename to dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/Organization.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/Person.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/Person.java
new file mode 100644
index 0000000..8777c91
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/Person.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.model;
+
+import java.util.Arrays;
+
+public class Person {
+    byte oneByte = 123;
+    private String name = "name1";
+    private int age = 11;
+
+    private String[] value = {"value1", "value2"};
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public byte getOneByte() {
+        return oneByte;
+    }
+
+    public void setOneByte(byte b) {
+        this.oneByte = b;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public String[] getValue() {
+        return value;
+    }
+
+    public void setValue(String[] value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + age;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + Arrays.hashCode(value);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Person other = (Person) obj;
+        if (age != other.age)
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        if (!Arrays.equals(value, other.value))
+            return false;
+        return true;
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/SerializablePerson.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/SerializablePerson.java
new file mode 100644
index 0000000..fafd9b2
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/SerializablePerson.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.model;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+public class SerializablePerson implements Serializable {
+    private static final long serialVersionUID = 1L;
+    byte oneByte = 123;
+    private String name = "name1";
+    private int age = 11;
+
+    private String[] value = {"value1", "value2"};
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public byte getOneByte() {
+        return oneByte;
+    }
+
+    public void setOneByte(byte b) {
+        this.oneByte = b;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public String[] getValue() {
+        return value;
+    }
+
+    public void setValue(String[] value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + age;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + Arrays.hashCode(value);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        SerializablePerson other = (SerializablePerson) obj;
+        if (age != other.age)
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        if (!Arrays.equals(value, other.value))
+            return false;
+        return true;
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/media/Image.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/media/Image.java
new file mode 100644
index 0000000..cee5642
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/media/Image.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.dubbo.common.serialize.model.media;
+
+
+public class Image implements java.io.Serializable {
+    private static final long serialVersionUID = 1L;
+    public String uri;
+    public String title;  // Can be null
+    public int width;
+    public int height;
+    public Size size;
+
+    public Image() {
+    }
+
+    public Image(String uri, String title, int width, int height, Size size) {
+        this.height = height;
+        this.title = title;
+        this.uri = uri;
+        this.width = width;
+        this.size = size;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Image image = (Image) o;
+
+        if (height != image.height) return false;
+        if (width != image.width) return false;
+        if (size != image.size) return false;
+        if (title != null ? !title.equals(image.title) : image.title != null) return false;
+        if (uri != null ? !uri.equals(image.uri) : image.uri != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = uri != null ? uri.hashCode() : 0;
+        result = 31 * result + (title != null ? title.hashCode() : 0);
+        result = 31 * result + width;
+        result = 31 * result + height;
+        result = 31 * result + (size != null ? size.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[Image ");
+        sb.append("uri=").append(uri);
+        sb.append(", title=").append(title);
+        sb.append(", width=").append(width);
+        sb.append(", height=").append(height);
+        sb.append(", size=").append(size);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public Size getSize() {
+        return size;
+    }
+
+    public void setSize(Size size) {
+        this.size = size;
+    }
+
+    public enum Size {
+        SMALL, LARGE
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/media/Media.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/media/Media.java
new file mode 100644
index 0000000..7a661a3
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/media/Media.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.model.media;
+
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class Media implements java.io.Serializable {
+    public String uri;
+    public String title;        // Can be unset.
+    public int width;
+    public int height;
+    public String format;
+    public long duration;
+    public long size;
+    public int bitrate;         // Can be unset.
+    public boolean hasBitrate;
+    public List<String> persons;
+    public Player player;
+    public String copyright;    // Can be unset.
+
+    public Media() {
+    }
+
+    public Media(String uri, String title, int width, int height, String format, long duration, long size, int bitrate, boolean hasBitrate, List<String> persons, Player player, String copyright) {
+        this.uri = uri;
+        this.title = title;
+        this.width = width;
+        this.height = height;
+        this.format = format;
+        this.duration = duration;
+        this.size = size;
+        this.bitrate = bitrate;
+        this.hasBitrate = hasBitrate;
+        this.persons = persons;
+        this.player = player;
+        this.copyright = copyright;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Media media = (Media) o;
+
+        if (bitrate != media.bitrate) return false;
+        if (duration != media.duration) return false;
+        if (hasBitrate != media.hasBitrate) return false;
+        if (height != media.height) return false;
+        if (size != media.size) return false;
+        if (width != media.width) return false;
+        if (copyright != null ? !copyright.equals(media.copyright) : media.copyright != null) return false;
+        if (format != null ? !format.equals(media.format) : media.format != null) return false;
+        if (persons != null ? !persons.equals(media.persons) : media.persons != null) return false;
+        if (player != media.player) return false;
+        if (title != null ? !title.equals(media.title) : media.title != null) return false;
+        if (uri != null ? !uri.equals(media.uri) : media.uri != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = uri != null ? uri.hashCode() : 0;
+        result = 31 * result + (title != null ? title.hashCode() : 0);
+        result = 31 * result + width;
+        result = 31 * result + height;
+        result = 31 * result + (format != null ? format.hashCode() : 0);
+        result = 31 * result + (int) (duration ^ (duration >>> 32));
+        result = 31 * result + (int) (size ^ (size >>> 32));
+        result = 31 * result + bitrate;
+        result = 31 * result + (hasBitrate ? 1 : 0);
+        result = 31 * result + (persons != null ? persons.hashCode() : 0);
+        result = 31 * result + (player != null ? player.hashCode() : 0);
+        result = 31 * result + (copyright != null ? copyright.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[Media ");
+        sb.append("uri=").append(uri);
+        sb.append(", title=").append(title);
+        sb.append(", width=").append(width);
+        sb.append(", height=").append(height);
+        sb.append(", format=").append(format);
+        sb.append(", duration=").append(duration);
+        sb.append(", size=").append(size);
+        sb.append(", hasBitrate=").append(hasBitrate);
+        sb.append(", bitrate=").append(String.valueOf(bitrate));
+        sb.append(", persons=").append(persons);
+        sb.append(", player=").append(player);
+        sb.append(", copyright=").append(copyright);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public String getUri() {
+        return uri;
+    }
+
+    public void setUri(String uri) {
+        this.uri = uri;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public String getFormat() {
+        return format;
+    }
+
+    public void setFormat(String format) {
+        this.format = format;
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    public void setDuration(long duration) {
+        this.duration = duration;
+    }
+
+    public long getSize() {
+        return size;
+    }
+
+    public void setSize(long size) {
+        this.size = size;
+    }
+
+    public int getBitrate() {
+        return bitrate;
+    }
+
+    public void setBitrate(int bitrate) {
+        this.bitrate = bitrate;
+        this.hasBitrate = true;
+    }
+
+    public List<String> getPersons() {
+        return persons;
+    }
+
+    public void setPersons(List<String> persons) {
+        this.persons = persons;
+    }
+
+    public Player getPlayer() {
+        return player;
+    }
+
+    public void setPlayer(Player player) {
+        this.player = player;
+    }
+
+    public String getCopyright() {
+        return copyright;
+    }
+
+    public void setCopyright(String copyright) {
+        this.copyright = copyright;
+    }
+
+    public enum Player {
+        JAVA, FLASH
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/media/MediaContent.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/media/MediaContent.java
new file mode 100644
index 0000000..efe409f
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/media/MediaContent.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.model.media;
+
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class MediaContent implements java.io.Serializable {
+    public Media media;
+    public List<Image> images;
+
+    public MediaContent() {
+    }
+
+    public MediaContent(Media media, List<Image> images) {
+        this.media = media;
+        this.images = images;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        MediaContent that = (MediaContent) o;
+
+        if (images != null ? !images.equals(that.images) : that.images != null) return false;
+        if (media != null ? !media.equals(that.media) : that.media != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = media != null ? media.hashCode() : 0;
+        result = 31 * result + (images != null ? images.hashCode() : 0);
+        return result;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[MediaContent: ");
+        sb.append("media=").append(media);
+        sb.append(", images=").append(images);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public Media getMedia() {
+        return media;
+    }
+
+    public void setMedia(Media media) {
+        this.media = media;
+    }
+
+    public List<Image> getImages() {
+        return images;
+    }
+
+    public void setImages(List<Image> images) {
+        this.images = images;
+    }
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/BigPerson.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/BigPerson.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/BigPerson.java
rename to dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/BigPerson.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/FullAddress.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/FullAddress.java
new file mode 100644
index 0000000..debc9e6
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/FullAddress.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.model.person;
+
+import java.io.Serializable;
+
+public class FullAddress implements Serializable {
+
+    private static final long serialVersionUID = 5163979984269419831L;
+
+    private String countryId;
+
+    private String countryName;
+
+    private String provinceName;
+
+    private String cityId;
+
+    private String cityName;
+
+    private String streetAddress;
+
+    private String zipCode;
+
+    public FullAddress() {
+    }
+
+    public FullAddress(String countryId, String provinceName, String cityId, String streetAddress,
+                       String zipCode) {
+        this.countryId = countryId;
+        this.countryName = countryId;
+        this.provinceName = provinceName;
+        this.cityId = cityId;
+        this.cityName = cityId;
+        this.streetAddress = streetAddress;
+        this.zipCode = zipCode;
+    }
+
+    public FullAddress(String countryId, String countryName, String provinceName, String cityId,
+                       String cityName, String streetAddress, String zipCode) {
+        this.countryId = countryId;
+        this.countryName = countryName;
+        this.provinceName = provinceName;
+        this.cityId = cityId;
+        this.cityName = cityName;
+        this.streetAddress = streetAddress;
+        this.zipCode = zipCode;
+    }
+
+    public String getCountryId() {
+        return countryId;
+    }
+
+    public void setCountryId(String countryId) {
+        this.countryId = countryId;
+    }
+
+    public String getCountryName() {
+        return countryName;
+    }
+
+    public void setCountryName(String countryName) {
+        this.countryName = countryName;
+    }
+
+    public String getProvinceName() {
+        return provinceName;
+    }
+
+    public void setProvinceName(String provinceName) {
+        this.provinceName = provinceName;
+    }
+
+    public String getCityId() {
+        return cityId;
+    }
+
+    public void setCityId(String cityId) {
+        this.cityId = cityId;
+    }
+
+    public String getCityName() {
+        return cityName;
+    }
+
+    public void setCityName(String cityName) {
+        this.cityName = cityName;
+    }
+
+    public String getStreetAddress() {
+        return streetAddress;
+    }
+
+    public void setStreetAddress(String streetAddress) {
+        this.streetAddress = streetAddress;
+    }
+
+    public String getZipCode() {
+        return zipCode;
+    }
+
+    public void setZipCode(String zipCode) {
+        this.zipCode = zipCode;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((cityId == null) ? 0 : cityId.hashCode());
+        result = prime * result + ((cityName == null) ? 0 : cityName.hashCode());
+        result = prime * result + ((countryId == null) ? 0 : countryId.hashCode());
+        result = prime * result + ((countryName == null) ? 0 : countryName.hashCode());
+        result = prime * result + ((provinceName == null) ? 0 : provinceName.hashCode());
+        result = prime * result + ((streetAddress == null) ? 0 : streetAddress.hashCode());
+        result = prime * result + ((zipCode == null) ? 0 : zipCode.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        FullAddress other = (FullAddress) obj;
+        if (cityId == null) {
+            if (other.cityId != null)
+                return false;
+        } else if (!cityId.equals(other.cityId))
+            return false;
+        if (cityName == null) {
+            if (other.cityName != null)
+                return false;
+        } else if (!cityName.equals(other.cityName))
+            return false;
+        if (countryId == null) {
+            if (other.countryId != null)
+                return false;
+        } else if (!countryId.equals(other.countryId))
+            return false;
+        if (countryName == null) {
+            if (other.countryName != null)
+                return false;
+        } else if (!countryName.equals(other.countryName))
+            return false;
+        if (provinceName == null) {
+            if (other.provinceName != null)
+                return false;
+        } else if (!provinceName.equals(other.provinceName))
+            return false;
+        if (streetAddress == null) {
+            if (other.streetAddress != null)
+                return false;
+        } else if (!streetAddress.equals(other.streetAddress))
+            return false;
+        if (zipCode == null) {
+            if (other.zipCode != null)
+                return false;
+        } else if (!zipCode.equals(other.zipCode))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (countryName != null && countryName.length() > 0) {
+            sb.append(countryName);
+        }
+        if (provinceName != null && provinceName.length() > 0) {
+            sb.append(" ");
+            sb.append(provinceName);
+        }
+        if (cityName != null && cityName.length() > 0) {
+            sb.append(" ");
+            sb.append(cityName);
+        }
+        if (streetAddress != null && streetAddress.length() > 0) {
+            sb.append(" ");
+            sb.append(streetAddress);
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/PersonInfo.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/PersonInfo.java
similarity index 100%
rename from dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/PersonInfo.java
rename to dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/PersonInfo.java
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/PersonStatus.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/PersonStatus.java
new file mode 100644
index 0000000..d2334c0
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/PersonStatus.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.dubbo.common.serialize.model.person;
+
+public enum PersonStatus {
+    ENABLED,
+    DISABLED
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/Phone.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/Phone.java
new file mode 100644
index 0000000..0143baf
--- /dev/null
+++ b/dubbo-serialization-extensions/dubbo-serialization-test/src/main/java/org/apache/dubbo/common/serialize/model/person/Phone.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.common.serialize.model.person;
+
+import java.io.Serializable;
+
+public class Phone implements Serializable {
+
+    private static final long serialVersionUID = 4399060521859707703L;
+
+    private String country;
+
+    private String area;
+
+    private String number;
+
+    private String extensionNumber;
+
+    public Phone() {
+    }
+
+    public Phone(String country, String area, String number, String extensionNumber) {
+        this.country = country;
+        this.area = area;
+        this.number = number;
+        this.extensionNumber = extensionNumber;
+    }
+
+    public String getCountry() {
+        return country;
+    }
+
+    public void setCountry(String country) {
+        this.country = country;
+    }
+
+    public String getArea() {
+        return area;
+    }
+
+    public void setArea(String area) {
+        this.area = area;
+    }
+
+    public String getNumber() {
+        return number;
+    }
+
+    public void setNumber(String number) {
+        this.number = number;
+    }
+
+    public String getExtensionNumber() {
+        return extensionNumber;
+    }
+
+    public void setExtensionNumber(String extensionNumber) {
+        this.extensionNumber = extensionNumber;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((area == null) ? 0 : area.hashCode());
+        result = prime * result + ((country == null) ? 0 : country.hashCode());
+        result = prime * result + ((extensionNumber == null) ? 0 : extensionNumber.hashCode());
+        result = prime * result + ((number == null) ? 0 : number.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        Phone other = (Phone) obj;
+        if (area == null) {
+            if (other.area != null)
+                return false;
+        } else if (!area.equals(other.area))
+            return false;
+        if (country == null) {
+            if (other.country != null)
+                return false;
+        } else if (!country.equals(other.country))
+            return false;
+        if (extensionNumber == null) {
+            if (other.extensionNumber != null)
+                return false;
+        } else if (!extensionNumber.equals(other.extensionNumber))
+            return false;
+        if (number == null) {
+            if (other.number != null)
+                return false;
+        } else if (!number.equals(other.number))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (country != null && country.length() > 0) {
+            sb.append(country);
+            sb.append("-");
+        }
+        if (area != null && area.length() > 0) {
+            sb.append(area);
+            sb.append("-");
+        }
+        if (number != null && number.length() > 0) {
+            sb.append(number);
+        }
+        if (extensionNumber != null && extensionNumber.length() > 0) {
+            sb.append("-");
+            sb.append(extensionNumber);
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/avro/AvroObjectInputOutputTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/avro/AvroObjectInputOutputTest.java
deleted file mode 100644
index 6ca1cac..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/avro/AvroObjectInputOutputTest.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.avro;
-
-
-import org.apache.dubbo.common.serialize.model.Person;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsNot.not;
-import static org.hamcrest.core.IsNull.nullValue;
-
-
-public class AvroObjectInputOutputTest {
-    private AvroObjectInput avroObjectInput;
-    private AvroObjectOutput avroObjectOutput;
-
-    private PipedOutputStream pos;
-    private PipedInputStream pis;
-
-    @BeforeEach
-    public void setup() throws IOException {
-        pis = new PipedInputStream();
-        pos = new PipedOutputStream();
-        pis.connect(pos);
-
-        avroObjectOutput = new AvroObjectOutput(pos);
-        avroObjectInput = new AvroObjectInput(pis);
-    }
-
-    @AfterEach
-    public void clean() throws IOException {
-        if (pos != null) {
-            pos.close();
-            pos = null;
-        }
-        if (pis != null) {
-            pis.close();
-            pis = null;
-        }
-    }
-
-    @Test
-    public void testWriteReadBool() throws IOException, InterruptedException {
-        avroObjectOutput.writeBool(true);
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        boolean result = avroObjectInput.readBool();
-        assertThat(result, is(true));
-    }
-
-    @Test
-    public void testWriteReadByte() throws IOException {
-        avroObjectOutput.writeByte((byte) 'a');
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        Byte result = avroObjectInput.readByte();
-
-        assertThat(result, is((byte) 'a'));
-    }
-
-    @Test
-    public void testWriteReadBytes() throws IOException {
-        avroObjectOutput.writeBytes("123456".getBytes());
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        byte[] result = avroObjectInput.readBytes();
-
-        assertThat(result, is("123456".getBytes()));
-    }
-
-    @Test
-    public void testWriteReadShort() throws IOException {
-        avroObjectOutput.writeShort((short) 1);
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        short result = avroObjectInput.readShort();
-
-        assertThat(result, is((short) 1));
-    }
-
-    @Test
-    public void testWriteReadInt() throws IOException {
-        avroObjectOutput.writeInt(1);
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        Integer result = avroObjectInput.readInt();
-
-        assertThat(result, is(1));
-    }
-
-    @Test
-    public void testReadDouble() throws IOException {
-        avroObjectOutput.writeDouble(3.14d);
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        Double result = avroObjectInput.readDouble();
-
-        assertThat(result, is(3.14d));
-    }
-
-    @Test
-    public void testReadLong() throws IOException {
-        avroObjectOutput.writeLong(10L);
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        Long result = avroObjectInput.readLong();
-
-        assertThat(result, is(10L));
-    }
-
-    @Test
-    public void testWriteReadFloat() throws IOException {
-        avroObjectOutput.writeFloat(1.66f);
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        Float result = avroObjectInput.readFloat();
-
-        assertThat(result, is(1.66F));
-    }
-
-    @Test
-    public void testWriteReadUTF() throws IOException {
-        avroObjectOutput.writeUTF("wording");
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        String result = avroObjectInput.readUTF();
-
-        assertThat(result, is("wording"));
-    }
-
-    @Test
-    public void testWriteReadObject() throws IOException, ClassNotFoundException {
-        Person p = new Person();
-        p.setAge(30);
-        p.setName("abc");
-
-        avroObjectOutput.writeObject(p);
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        Person result = avroObjectInput.readObject(Person.class);
-
-        assertThat(result, not(nullValue()));
-        assertThat(result.getName(), is("abc"));
-        assertThat(result.getAge(), is(30));
-    }
-
-    @Test
-    public void testWriteReadObjectWithoutClass() throws IOException, ClassNotFoundException {
-        Person p = new Person();
-        p.setAge(30);
-        p.setName("abc");
-
-        avroObjectOutput.writeObject(p);
-        avroObjectOutput.flushBuffer();
-        pos.close();
-
-        //这里会丢失所有信息
-        Object result = avroObjectInput.readObject();
-
-        assertThat(result, not(nullValue()));
-//		assertThat(result.getName(), is("abc"));
-//		assertThat(result.getAge(), is(30));
-    }
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/avro/AvroSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/avro/AvroSerializationTest.java
deleted file mode 100644
index 0b12757..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/avro/AvroSerializationTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.avro;
-
-import org.apache.dubbo.common.serialize.ObjectInput;
-import org.apache.dubbo.common.serialize.ObjectOutput;
-
-import org.hamcrest.Matchers;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import static org.apache.dubbo.common.serialize.Constants.AVRO_SERIALIZATION_ID;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.mockito.Mockito.mock;
-
-public class AvroSerializationTest {
-    private AvroSerialization avroSerialization;
-
-    @BeforeEach
-    public void setUp() {
-        this.avroSerialization = new AvroSerialization();
-    }
-
-    @Test
-    public void testContentType() {
-        assertThat(avroSerialization.getContentType(), is("avro/binary"));
-    }
-
-    @Test
-    public void testContentTypeId() {
-        assertThat(avroSerialization.getContentTypeId(), is(AVRO_SERIALIZATION_ID));
-    }
-
-    @Test
-    public void testObjectOutput() throws IOException {
-        ObjectOutput objectOutput = avroSerialization.serialize(null, mock(OutputStream.class));
-        assertThat(objectOutput, Matchers.<ObjectOutput>instanceOf(AvroObjectOutput.class));
-    }
-
-    @Test
-    public void testObjectInput() throws IOException {
-        ObjectInput objectInput = avroSerialization.deserialize(null, mock(InputStream.class));
-        assertThat(objectInput, Matchers.<ObjectInput>instanceOf(AvroObjectInput.class));
-    }
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonFailTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonFailTest.java
deleted file mode 100644
index 4b9f3b2..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonFailTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.base;
-
-import org.apache.dubbo.common.serialize.ObjectOutput;
-import org.apache.dubbo.common.serialize.model.Person;
-
-import org.junit.jupiter.api.Test;
-
-import java.io.NotSerializableException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.fail;
-
-public abstract class AbstractSerializationPersonFailTest extends AbstractSerializationTest {
-
-    protected static final String FAIL_STRING = "Serialized class org.apache.dubbo.common.serialize.model.Person must implement java.io.Serializable";
-
-    @Test
-    public void test_Person() throws Exception {
-        try {
-            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-            objectOutput.writeObject(new Person());
-            fail();
-        } catch (NotSerializableException expected) {
-        } catch (IllegalStateException expected) {
-            assertThat(expected.getMessage(), containsString(FAIL_STRING));
-        }
-    }
-
-    @Test
-    public void test_PersonList() throws Exception {
-        List<Person> args = new ArrayList<Person>();
-        args.add(new Person());
-        try {
-            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-            objectOutput.writeObject(args);
-            fail();
-        } catch (NotSerializableException expected) {
-        } catch (IllegalStateException expected) {
-            assertThat(expected.getMessage(), containsString(FAIL_STRING));
-        }
-    }
-
-    @Test
-    public void test_PersonSet() throws Exception {
-        Set<Person> args = new HashSet<Person>();
-        args.add(new Person());
-        try {
-            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-            objectOutput.writeObject(args);
-            fail();
-        } catch (NotSerializableException expected) {
-        } catch (IllegalStateException expected) {
-            System.out.println("--------" + expected.getMessage());
-            assertThat(expected.getMessage(), containsString(FAIL_STRING));
-        }
-    }
-
-    @Test
-    public void test_IntPersonMap() throws Exception {
-        Map<Integer, Person> args = new HashMap<Integer, Person>();
-        args.put(1, new Person());
-        try {
-            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-            objectOutput.writeObject(args);
-            fail();
-        } catch (NotSerializableException expected) {
-        } catch (IllegalStateException expected) {
-            assertThat(expected.getMessage(), containsString(FAIL_STRING));
-        }
-    }
-
-    @Test
-    public void test_StringPersonMap() throws Exception {
-        Map<String, Person> args = new HashMap<String, Person>();
-        args.put("1", new Person());
-        try {
-            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-            objectOutput.writeObject(args);
-            fail();
-        } catch (NotSerializableException expected) {
-        } catch (IllegalStateException expected) {
-            assertThat(expected.getMessage(), containsString(FAIL_STRING));
-        }
-    }
-
-    @Test
-    public void test_StringPersonListMap() throws Exception {
-        Map<String, List<Person>> args = new HashMap<String, List<Person>>();
-
-        List<Person> sublist = new ArrayList<Person>();
-        sublist.add(new Person());
-        args.put("1", sublist);
-        try {
-            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-            objectOutput.writeObject(args);
-            fail();
-        } catch (NotSerializableException expected) {
-        } catch (IllegalStateException expected) {
-            assertThat(expected.getMessage(), containsString(FAIL_STRING));
-        }
-    }
-
-    @Test
-    public void test_PersonListList() throws Exception {
-        List<List<Person>> args = new ArrayList<List<Person>>();
-        List<Person> sublist = new ArrayList<Person>();
-        sublist.add(new Person());
-        args.add(sublist);
-        try {
-            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-            objectOutput.writeObject(args);
-            fail();
-        } catch (NotSerializableException expected) {
-        } catch (IllegalStateException expected) {
-            assertThat(expected.getMessage(), containsString(FAIL_STRING));
-        }
-    }
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonOkTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonOkTest.java
deleted file mode 100644
index a4b6594..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/base/AbstractSerializationPersonOkTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.base;
-
-import org.apache.dubbo.common.serialize.model.person.Phone;
-
-import org.junit.jupiter.api.Test;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public abstract class AbstractSerializationPersonOkTest extends AbstractSerializationTest {
-    @Test
-    public void test_Phone() throws Exception {
-        assertObject(new Phone());
-    }
-
-    @Test
-    public void test_Person_withType() throws Exception {
-        assertObjectWithType(new Phone(), Phone.class);
-    }
-
-    @Test
-    public void test_PersonList() throws Exception {
-        List<Phone> args = new ArrayList<Phone>();
-        args.add(new Phone());
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_PersonSet() throws Exception {
-        Set<Phone> args = new HashSet<Phone>();
-        args.add(new Phone());
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_IntPersonMap() throws Exception {
-        Map<Integer, Phone> args = new HashMap<Integer, Phone>();
-        args.put(1, new Phone());
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_StringPersonMap() throws Exception {
-        Map<String, Phone> args = new HashMap<String, Phone>();
-        args.put("1", new Phone());
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_StringPersonListMap() throws Exception {
-        Map<String, List<Phone>> args = new HashMap<String, List<Phone>>();
-
-        List<Phone> sublist = new ArrayList<Phone>();
-        sublist.add(new Phone());
-        args.put("1", sublist);
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_PersonListList() throws Exception {
-        List<List<Phone>> args = new ArrayList<List<Phone>>();
-        List<Phone> sublist = new ArrayList<Phone>();
-        sublist.add(new Phone());
-        args.add(sublist);
-
-        assertObject(args);
-    }
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/base/AbstractSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/base/AbstractSerializationTest.java
deleted file mode 100644
index 02afaea..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/base/AbstractSerializationTest.java
+++ /dev/null
@@ -1,1212 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.base;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.serialize.ObjectInput;
-import org.apache.dubbo.common.serialize.ObjectOutput;
-import org.apache.dubbo.common.serialize.Serialization;
-import org.apache.dubbo.common.serialize.model.AnimalEnum;
-import org.apache.dubbo.common.serialize.model.BizException;
-import org.apache.dubbo.common.serialize.model.BizExceptionNoDefaultConstructor;
-import org.apache.dubbo.common.serialize.model.SerializablePerson;
-import org.apache.dubbo.common.serialize.model.media.Image;
-import org.apache.dubbo.common.serialize.model.media.Media;
-import org.apache.dubbo.common.serialize.model.media.MediaContent;
-import org.apache.dubbo.common.serialize.model.person.BigPerson;
-import org.apache.dubbo.common.serialize.model.person.FullAddress;
-import org.apache.dubbo.common.serialize.model.person.PersonInfo;
-import org.apache.dubbo.common.serialize.model.person.PersonStatus;
-import org.apache.dubbo.common.serialize.model.person.Phone;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.sql.Time;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-
-import static java.time.Duration.ofMillis;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertTimeout;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-public abstract class AbstractSerializationTest {
-    protected static Random random = new Random();
-    protected Serialization serialization;
-    protected URL url = new URL("protocol", "1.1.1.1", 1234);
-    protected ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-
-    // ================ Primitive Type ================
-    protected BigPerson bigPerson;
-    protected MediaContent mediaContent;
-
-    {
-        bigPerson = new BigPerson();
-        bigPerson.setPersonId("superman111");
-        bigPerson.setLoginName("superman");
-        bigPerson.setStatus(PersonStatus.ENABLED);
-        bigPerson.setEmail("sm@1.com");
-        bigPerson.setPenName("pname");
-
-        ArrayList<Phone> phones = new ArrayList<Phone>();
-        Phone phone1 = new Phone("86", "0571", "87654321", "001");
-        Phone phone2 = new Phone("86", "0571", "87654322", "002");
-        phones.add(phone1);
-        phones.add(phone2);
-
-        PersonInfo pi = new PersonInfo();
-        pi.setPhones(phones);
-        Phone fax = new Phone("86", "0571", "87654321", null);
-        pi.setFax(fax);
-        FullAddress addr = new FullAddress("CN", "zj", "3480", "wensanlu", "315000");
-        pi.setFullAddress(addr);
-        pi.setMobileNo("13584652131");
-        pi.setMale(true);
-        pi.setDepartment("b2b");
-        pi.setHomepageUrl("www.capcom.com");
-        pi.setJobTitle("qa");
-        pi.setName("superman");
-
-        bigPerson.setInfoProfile(pi);
-    }
-
-    {
-        Media media = new Media();
-        media.setUri("uri://中华人民共和国");
-        media.setTitle("title");
-        media.setWidth(1239);
-        media.setHeight(1938);
-        media.setFormat("format-xxxx");
-        media.setDuration(93419235);
-        media.setSize(3477897);
-        media.setBitrate(94523);
-        List<String> persons = new ArrayList<String>();
-        persons.add("jerry");
-        persons.add("tom");
-        persons.add("lucy");
-        media.setPersons(persons);
-        media.setCopyright("1999-2011");
-        media.setPlayer(Media.Player.FLASH);
-
-        List<Image> images = new ArrayList<Image>();
-        for (int i = 0; i < 10; ++i) {
-            Image image = new Image();
-            image.setUri("url" + i);
-            if (i % 2 == 0) image.setTitle("title" + i);
-            image.setWidth(34 + i);
-            image.setHeight(2323 + i);
-            image.setSize((i % 2 == 0) ? Image.Size.SMALL : Image.Size.LARGE);
-
-            images.add(image);
-        }
-
-        mediaContent = new MediaContent(media, images);
-    }
-
-    @Test
-    public void test_Bool() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeBool(false);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-        assertFalse(deserialize.readBool());
-
-        try {
-            deserialize.readBool();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_Bool_Multi() throws Exception {
-        boolean[] array = new boolean[100];
-        for (int i = 0; i < array.length; i++) {
-            array[i] = random.nextBoolean();
-        }
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        for (boolean b : array) {
-            objectOutput.writeBool(b);
-        }
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        for (boolean b : array) {
-            assertEquals(b, deserialize.readBool());
-        }
-
-        try {
-            deserialize.readBool();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_Byte() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeByte((byte) 123);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals((byte) 123, deserialize.readByte());
-
-        try {
-            deserialize.readByte();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_Byte_Multi() throws Exception {
-        byte[] array = new byte[100];
-        random.nextBytes(array);
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        for (byte b : array) {
-            objectOutput.writeByte(b);
-        }
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        for (byte b : array) {
-            assertEquals(b, deserialize.readByte());
-        }
-
-        try {
-            deserialize.readByte();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_Short() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeShort((short) 123);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals((short) 123, deserialize.readShort());
-
-        try {
-            deserialize.readShort();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_Integer() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeInt(1);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        int i = deserialize.readInt();
-        assertEquals(1, i);
-
-        try {
-            deserialize.readInt();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_Long() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeLong(123L);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(123L, deserialize.readLong());
-
-        try {
-            deserialize.readLong();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_Float() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeFloat(1.28F);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(1.28F, deserialize.readFloat());
-
-        try {
-            deserialize.readFloat();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    // ================== Util methods ==================
-
-    @Test
-    public void test_Double() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeDouble(1.28);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(1.28, deserialize.readDouble());
-
-        try {
-            deserialize.readDouble();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_UtfString() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeUTF("123中华人民共和国");
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals("123中华人民共和国", deserialize.readUTF());
-
-        try {
-            deserialize.readUTF();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_Bytes() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeBytes("123中华人民共和国".getBytes());
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals("123中华人民共和国".getBytes(), deserialize.readBytes());
-
-        try {
-            deserialize.readBytes();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_BytesRange() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeBytes("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, 9);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        byte[] expectedArray = new byte[9];
-        System.arraycopy("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, expectedArray, 0, expectedArray.length);
-        assertArrayEquals(expectedArray, deserialize.readBytes());
-
-        try {
-            deserialize.readBytes();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    // ================ Array Type ================
-
-    <T> void assertObjectArray(T[] data, Class<T[]> clazz) throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, clazz.cast(deserialize.readObject()));
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    <T> void assertObjectArrayWithType(T[] data, Class<T[]> clazz) throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, clazz.cast(deserialize.readObject(clazz)));
-
-        try {
-            deserialize.readObject(clazz);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    public <T> void assertObject(T data) throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(data, (T) deserialize.readObject());
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    public <T> void assertObjectWithType(T data, Class<T> clazz) throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(data, (T) deserialize.readObject(clazz));
-
-        try {
-            deserialize.readObject(clazz);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_boolArray() throws Exception {
-        boolean[] data = new boolean[]{true, false, true};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject()));
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_boolArray_withType() throws Exception {
-        boolean[] data = new boolean[]{true, false, true};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertTrue(Arrays.equals(data, (boolean[]) deserialize.readObject(boolean[].class)));
-
-        try {
-            deserialize.readObject(boolean[].class);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_charArray() throws Exception {
-        char[] data = new char[]{'a', '中', '无'};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (char[]) deserialize.readObject());
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_charArray_withType() throws Exception {
-        char[] data = new char[]{'a', '中', '无'};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (char[]) deserialize.readObject(char[].class));
-
-        try {
-            deserialize.readObject(char[].class);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_shortArray() throws Exception {
-        short[] data = new short[]{37, 39, 12};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (short[]) deserialize.readObject());
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_shortArray_withType() throws Exception {
-        short[] data = new short[]{37, 39, 12};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (short[]) deserialize.readObject(short[].class));
-
-        try {
-            deserialize.readObject(short[].class);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_intArray() throws Exception {
-        int[] data = new int[]{234, 0, -1};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (int[]) deserialize.readObject());
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_intArray_withType() throws Exception {
-        int[] data = new int[]{234, 0, -1};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (int[]) deserialize.readObject(int[].class));
-
-        try {
-            deserialize.readObject(int[].class);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_longArray() throws Exception {
-        long[] data = new long[]{234, 0, -1};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (long[]) deserialize.readObject());
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_longArray_withType() throws Exception {
-        long[] data = new long[]{234, 0, -1};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (long[]) deserialize.readObject(long[].class));
-
-        try {
-            deserialize.readObject(long[].class);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_floatArray() throws Exception {
-        float[] data = new float[]{37F, -3.14F, 123456.7F};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (float[]) deserialize.readObject(), 0.0001F);
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_floatArray_withType() throws Exception {
-        float[] data = new float[]{37F, -3.14F, 123456.7F};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (float[]) deserialize.readObject(float[].class), 0.0001F);
-
-        try {
-            deserialize.readObject(float[].class);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_doubleArray() throws Exception {
-        double[] data = new double[]{37D, -3.14D, 123456.7D};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (double[]) deserialize.readObject(), 0.0001);
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_doubleArray_withType() throws Exception {
-        double[] data = new double[]{37D, -3.14D, 123456.7D};
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals(data, (double[]) deserialize.readObject(double[].class), 0.0001);
-
-        try {
-            deserialize.readObject(double[].class);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_StringArray() throws Exception {
-        assertObjectArray(new String[]{"1", "b"}, String[].class);
-    }
-
-    @Test
-    public void test_StringArray_withType() throws Exception {
-        assertObjectArrayWithType(new String[]{"1", "b"}, String[].class);
-    }
-
-    // ================ Simple Type ================
-
-    @Test
-    public void test_IntegerArray() throws Exception {
-        assertObjectArray(new Integer[]{234, 0, -1}, Integer[].class);
-    }
-
-    @Test
-    public void test_IntegerArray_withType() throws Exception {
-        assertObjectArrayWithType(new Integer[]{234, 0, -1}, Integer[].class);
-    }
-
-    @Test
-    public void test_EnumArray() throws Exception {
-        assertObjectArray(new AnimalEnum[]{AnimalEnum.bull, AnimalEnum.cat, AnimalEnum.dog, AnimalEnum.horse}, AnimalEnum[].class);
-    }
-
-    @Test
-    public void test_EnumArray_withType() throws Exception {
-        assertObjectArrayWithType(new AnimalEnum[]{AnimalEnum.bull, AnimalEnum.cat, AnimalEnum.dog, AnimalEnum.horse}, AnimalEnum[].class);
-    }
-
-    @Test
-    public void test_SPerson() throws Exception {
-        assertObject(new SerializablePerson());
-    }
-
-    @Test
-    public void test_SPerson_withType() throws Exception {
-        assertObjectWithType(new SerializablePerson(), SerializablePerson.class);
-    }
-
-    @Test
-    public void test_BizException() throws Exception {
-        BizException e = new BizException("Hello");
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(e);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        Object read = deserialize.readObject();
-        assertEquals("Hello", ((BizException) read).getMessage());
-    }
-
-    @Test
-    public void test_BizException_WithType() throws Exception {
-        BizException e = new BizException("Hello");
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(e);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        Object read = deserialize.readObject(BizException.class);
-        assertEquals("Hello", ((BizException) read).getMessage());
-    }
-
-    @Test
-    public void test_BizExceptionNoDefaultConstructor() throws Exception {
-        BizExceptionNoDefaultConstructor e = new BizExceptionNoDefaultConstructor("Hello");
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(e);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        Object read = deserialize.readObject();
-        assertEquals("Hello", ((BizExceptionNoDefaultConstructor) read).getMessage());
-    }
-
-    @Test
-    public void test_BizExceptionNoDefaultConstructor_WithType() throws Exception {
-        BizExceptionNoDefaultConstructor e = new BizExceptionNoDefaultConstructor("Hello");
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(e);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        Object read = deserialize.readObject(BizExceptionNoDefaultConstructor.class);
-        assertEquals("Hello", ((BizExceptionNoDefaultConstructor) read).getMessage());
-    }
-
-    @Test
-    public void test_enum() throws Exception {
-        assertObject(AnimalEnum.dog);
-    }
-
-    @Test
-    public void test_enum_withType() throws Exception {
-        assertObjectWithType(AnimalEnum.dog, AnimalEnum.class);
-    }
-
-    @Test
-    public void test_Date() throws Exception {
-        assertObject(new Date());
-    }
-
-    @Test
-    public void test_Date_withType() throws Exception {
-        assertObjectWithType(new Date(), Date.class);
-    }
-
-    @Test
-    public void test_Time() throws Exception {
-        assertObject(new Time(System.currentTimeMillis()));
-    }
-
-    @Test
-    public void test_Time_withType() throws Exception {
-        assertObjectWithType(new Time(System.currentTimeMillis()), Time.class);
-    }
-
-    @Test
-    public void test_ByteWrap() throws Exception {
-        assertObject(new Byte((byte) 12));
-    }
-
-    @Test
-    public void test_ByteWrap_withType() throws Exception {
-        assertObjectWithType(new Byte((byte) 12), Byte.class);
-    }
-
-    @Test
-    public void test_LongWrap() throws Exception {
-        assertObject(new Long(12));
-    }
-
-    @Test
-    public void test_LongWrap_withType() throws Exception {
-        assertObjectWithType(new Long(12), Long.class);
-    }
-
-    @Test
-    public void test_BigInteger() throws Exception {
-        assertObject(new BigInteger("23423434234234234"));
-    }
-
-    @Test
-    public void test_BigInteger_withType() throws Exception {
-        assertObjectWithType(new BigInteger("23423434234234234"), BigInteger.class);
-    }
-
-    @Test
-    public void test_BigDecimal() throws Exception {
-        assertObject(new BigDecimal("23423434234234234.341274832341234235"));
-    }
-
-    @Test
-    public void test_BigDecimal_withType() throws Exception {
-        assertObjectWithType(new BigDecimal("23423434234234234.341274832341234235"), BigDecimal.class);
-    }
-
-    @Test
-    public void test_StringList_asListReturn() throws Exception {
-        List<String> args = Arrays.asList(new String[]{"1", "b"});
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_StringArrayList() throws Exception {
-        List<String> args = new ArrayList<String>(Arrays.asList(new String[]{"1", "b"}));
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_StringSet() throws Exception {
-        Set<String> args = new HashSet<String>();
-        args.add("1");
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_LinkedHashMap() throws Exception {
-        LinkedHashMap<String, String> data = new LinkedHashMap<String, String>();
-        data.put("1", "a");
-        data.put("2", "b");
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        Object read = deserialize.readObject();
-        assertTrue(read instanceof LinkedHashMap);
-        @SuppressWarnings("unchecked")
-        String key1 = ((LinkedHashMap<String, String>) read).entrySet().iterator().next().getKey();
-        assertEquals("1", key1);
-
-        assertEquals(data, read);
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    // ================ Complex Collection Type ================
-
-    @Test
-    public void test_SPersonList() throws Exception {
-        List<SerializablePerson> args = new ArrayList<SerializablePerson>();
-        args.add(new SerializablePerson());
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_SPersonSet() throws Exception {
-        Set<SerializablePerson> args = new HashSet<SerializablePerson>();
-        args.add(new SerializablePerson());
-
-        assertObject(args);
-    }
-
-    // ================ complex POJO =============
-
-    @Test
-    public void test_IntSPersonMap() throws Exception {
-        Map<Integer, SerializablePerson> args = new HashMap<Integer, SerializablePerson>();
-        args.put(1, new SerializablePerson());
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_StringSPersonMap() throws Exception {
-        Map<String, SerializablePerson> args = new HashMap<String, SerializablePerson>();
-        args.put("1", new SerializablePerson());
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_StringSPersonListMap() throws Exception {
-        Map<String, List<SerializablePerson>> args = new HashMap<String, List<SerializablePerson>>();
-
-        List<SerializablePerson> sublist = new ArrayList<SerializablePerson>();
-        sublist.add(new SerializablePerson());
-        args.put("1", sublist);
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_SPersonListList() throws Exception {
-        List<List<SerializablePerson>> args = new ArrayList<List<SerializablePerson>>();
-        List<SerializablePerson> sublist = new ArrayList<SerializablePerson>();
-        sublist.add(new SerializablePerson());
-        args.add(sublist);
-
-        assertObject(args);
-    }
-
-    @Test
-    public void test_BigPerson() throws Exception {
-        assertObject(bigPerson);
-    }
-
-    @Test
-    public void test_BigPerson_WithType() throws Exception {
-        assertObjectWithType(bigPerson, BigPerson.class);
-    }
-
-    @Test
-    public void test_MediaContent() throws Exception {
-        assertObject(mediaContent);
-    }
-
-    @Test
-    public void test_MediaContent_WithType() throws Exception {
-        assertObjectWithType(mediaContent, MediaContent.class);
-    }
-
-    @Test
-    public void test_MultiObject() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeBool(false);
-        objectOutput.writeObject(bigPerson);
-        objectOutput.writeByte((byte) 23);
-        objectOutput.writeObject(mediaContent);
-        objectOutput.writeInt(-23);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertFalse(deserialize.readBool());
-        assertEquals(bigPerson, deserialize.readObject());
-        assertEquals((byte) 23, deserialize.readByte());
-        assertEquals(mediaContent, deserialize.readObject());
-        assertEquals(-23, deserialize.readInt());
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void test_MultiObject_WithType() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeBool(false);
-        objectOutput.writeObject(bigPerson);
-        objectOutput.writeByte((byte) 23);
-        objectOutput.writeObject(mediaContent);
-        objectOutput.writeInt(-23);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertFalse(deserialize.readBool());
-        assertEquals(bigPerson, deserialize.readObject(BigPerson.class));
-        assertEquals((byte) 23, deserialize.readByte());
-        assertEquals(mediaContent, deserialize.readObject(MediaContent.class));
-        assertEquals(-23, deserialize.readInt());
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-
-    // abnormal case
-
-    @Test
-    public void test_MediaContent_badStream() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(mediaContent);
-        objectOutput.flushBuffer();
-
-        byte[] byteArray = byteArrayOutputStream.toByteArray();
-        for (int i = 0; i < byteArray.length; i++) {
-            if (i % 3 == 0) {
-                byteArray[i] = (byte) ~byteArray[i];
-            }
-        }
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
-
-        try {
-            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-            @SuppressWarnings("unused") // local variable, convenient for debug
-            Object read = deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-            System.out.println(expected);
-        }
-    }
-
-    @Test
-    public void test_MediaContent_WithType_badStream() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(mediaContent);
-        objectOutput.flushBuffer();
-
-        byte[] byteArray = byteArrayOutputStream.toByteArray();
-        for (int i = 0; i < byteArray.length; i++) {
-            if (i % 3 == 0) {
-                byteArray[i] = (byte) ~byteArray[i];
-            }
-        }
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
-
-        try {
-            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-            @SuppressWarnings("unused") // local variable, convenient for debug
-            Object read = deserialize.readObject(MediaContent.class);
-            fail();
-        } catch (IOException expected) {
-            System.out.println(expected);
-        }
-    }
-
-
-    @Test
-    public void test_LoopReference() throws Exception {
-        assertTimeout(ofMillis(3000), () -> {
-            Map<String, Object> map = new HashMap<String, Object>();
-            map.put("k1", "v1");
-            map.put("self", map);
-
-
-            ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-            objectOutput.writeObject(map);
-            objectOutput.flushBuffer();
-
-            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-                byteArrayOutputStream.toByteArray());
-            ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-            @SuppressWarnings("unchecked")
-            Map<String, Object> output = (Map<String, Object>) deserialize.readObject();
-
-            assertEquals("v1", output.get("k1"));
-            assertSame(output, output.get("self"));
-        });
-    }
-
-    // ================ final field test ================
-
-    @Test
-    public void test_URL_mutable_withType() throws Exception {
-        URL data = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path?version=1.0.0&application=morgan&noValue");
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(data);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        URL actual = (URL) deserialize.readObject(URL.class);
-        assertEquals(data, actual);
-        assertEquals(data.getParameters(), actual.getParameters());
-
-        try {
-            deserialize.readObject();
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectInputTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectInputTest.java
deleted file mode 100644
index 2937dba..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectInputTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.fst;
-
-import org.apache.dubbo.common.serialize.model.person.FullAddress;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-public class FstObjectInputTest {
-    private FstObjectInput fstObjectInput;
-
-    @Test
-    public void testWrongClassInput() throws IOException, ClassNotFoundException {
-        Assertions.assertThrows(IOException.class, () -> {
-            this.fstObjectInput = new FstObjectInput(new ByteArrayInputStream("{animal: 'cat'}".getBytes()));
-
-            fstObjectInput.readObject(FullAddress.class);
-        });
-    }
-
-    @Test
-    public void testEmptyByteArrayForEmptyInput() throws IOException {
-        this.fstObjectInput = new FstObjectInput(new ByteArrayInputStream("".getBytes()));
-
-        byte[] bytes = fstObjectInput.readBytes();
-        assertThat(bytes.length, is(0));
-    }
-
-
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectOutputTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectOutputTest.java
deleted file mode 100644
index c8ef1c7..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstObjectOutputTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.fst;
-
-import org.apache.dubbo.common.serialize.model.AnimalEnum;
-import org.apache.dubbo.common.serialize.model.person.FullAddress;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-public class FstObjectOutputTest {
-    private FstObjectOutput fstObjectOutput;
-    private FstObjectInput fstObjectInput;
-    private ByteArrayOutputStream byteArrayOutputStream;
-    private ByteArrayInputStream byteArrayInputStream;
-
-    @BeforeEach
-    public void setUp() {
-        this.byteArrayOutputStream = new ByteArrayOutputStream();
-        this.fstObjectOutput = new FstObjectOutput(byteArrayOutputStream);
-    }
-
-    @AfterEach
-    public void tearDown() throws IOException {
-        new FstObjectInput(new ByteArrayInputStream(new byte[]{0}));
-    }
-
-
-    @Test
-    public void testWriteBool() throws IOException {
-        this.fstObjectOutput.writeBool(false);
-        this.flushToInput();
-
-        boolean result = this.fstObjectInput.readBool();
-        assertThat(result, is(false));
-    }
-
-
-    @Test
-    public void testWriteUTF() throws IOException {
-        this.fstObjectOutput.writeUTF("I don’t know 知りません Не знаю");
-        this.flushToInput();
-
-        String result = this.fstObjectInput.readUTF();
-        assertThat(result, is("I don’t know 知りません Не знаю"));
-    }
-
-    @Test
-    public void testWriteShort() throws IOException {
-        this.fstObjectOutput.writeShort((short) 1);
-        this.flushToInput();
-
-        Short result = this.fstObjectInput.readShort();
-        assertThat(result, is((short) 1));
-    }
-
-    @Test
-    public void testWriteLong() throws IOException {
-        this.fstObjectOutput.writeLong(12345678L);
-        this.flushToInput();
-
-        Long result = this.fstObjectInput.readLong();
-        assertThat(result, is(12345678L));
-    }
-
-    @Test
-    public void testWriteDouble() throws IOException {
-        this.fstObjectOutput.writeDouble(-1.66d);
-        this.flushToInput();
-
-        Double result = this.fstObjectInput.readDouble();
-        assertThat(result, is(-1.66d));
-    }
-
-
-    @Test
-    public void testWriteInt() throws IOException {
-        this.fstObjectOutput.writeInt(1);
-        this.flushToInput();
-
-        Integer result = this.fstObjectInput.readInt();
-        assertThat(result, is(1));
-    }
-
-    @Test
-    public void testWriteByte() throws IOException {
-        this.fstObjectOutput.writeByte((byte) 222);
-        this.flushToInput();
-
-        Byte result = this.fstObjectInput.readByte();
-        assertThat(result, is(((byte) 222)));
-    }
-
-    @Test
-    public void testWriteBytesWithSubLength() throws IOException {
-        this.fstObjectOutput.writeBytes("who are you".getBytes(), 4, 3);
-        this.flushToInput();
-
-        byte[] result = this.fstObjectInput.readBytes();
-        assertThat(result, is("are".getBytes()));
-    }
-
-    @Test
-    public void testWriteBytes() throws IOException {
-        this.fstObjectOutput.writeBytes("who are you".getBytes());
-        this.flushToInput();
-
-        byte[] result = this.fstObjectInput.readBytes();
-        assertThat(result, is("who are you".getBytes()));
-    }
-
-    @Test
-    public void testWriteFloat() throws IOException {
-        this.fstObjectOutput.writeFloat(-666.66f);
-        this.flushToInput();
-
-        Float result = this.fstObjectInput.readFloat();
-        assertThat(result, is(-666.66f));
-    }
-
-    @Test
-    public void testWriteNullBytesWithSubLength() throws IOException {
-        this.fstObjectOutput.writeBytes(null, 4, 3);
-        this.flushToInput();
-
-        byte[] result = this.fstObjectInput.readBytes();
-        assertThat(result, is(nullValue()));
-    }
-
-    @Test
-    public void testWriteNullBytes() throws IOException {
-        this.fstObjectOutput.writeBytes(null);
-        this.flushToInput();
-
-        byte[] result = this.fstObjectInput.readBytes();
-        assertThat(result, is(nullValue()));
-    }
-
-
-    @Test
-    public void testWriteObject() throws IOException, ClassNotFoundException {
-        FullAddress fullAddress = new FullAddress("cId", "pN", "cityId", "Nan Long Street", "51000");
-        this.fstObjectOutput.writeObject(fullAddress);
-        this.flushToInput();
-
-        FullAddress result = this.fstObjectInput.readObject(FullAddress.class);
-        assertThat(result, is(fullAddress));
-    }
-
-    @Test
-    public void testWriteEnum() throws IOException, ClassNotFoundException {
-        this.fstObjectOutput.writeObject(AnimalEnum.cat);
-        this.flushToInput();
-
-        AnimalEnum animalEnum = (AnimalEnum) this.fstObjectInput.readObject();
-        assertThat(animalEnum, is(AnimalEnum.cat));
-    }
-
-    private void flushToInput() throws IOException {
-        this.fstObjectOutput.flushBuffer();
-        this.byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
-        this.fstObjectInput = new FstObjectInput(byteArrayInputStream);
-    }
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstSerializationTest.java
deleted file mode 100644
index 24de5bf..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/fst/FstSerializationTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.fst;
-
-import org.apache.dubbo.common.serialize.ObjectInput;
-import org.apache.dubbo.common.serialize.ObjectOutput;
-
-import org.hamcrest.Matchers;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.mockito.Mockito.mock;
-
-public class FstSerializationTest {
-    private FstSerialization fstSerialization;
-
-    @BeforeEach
-    public void setUp() {
-        this.fstSerialization = new FstSerialization();
-    }
-
-    @Test
-    public void testContentTypeId() {
-        assertThat(fstSerialization.getContentTypeId(), is((byte) 9));
-    }
-
-    @Test
-    public void testContentType() {
-        assertThat(fstSerialization.getContentType(), is("x-application/fst"));
-    }
-
-    @Test
-    public void testSerialize() throws IOException {
-        ObjectOutput objectOutput = fstSerialization.serialize(null, mock(OutputStream.class));
-        assertThat(objectOutput, Matchers.<ObjectOutput>instanceOf(FstObjectOutput.class));
-    }
-
-    @Test
-    public void testDeserialize() throws IOException {
-        ObjectInput objectInput = fstSerialization.deserialize(null, mock(InputStream.class));
-        assertThat(objectInput, Matchers.<ObjectInput>instanceOf(FstObjectInput.class));
-    }
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/jackson/JacksonObjectInputTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/jackson/JacksonObjectInputTest.java
deleted file mode 100644
index bb6912d..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/jackson/JacksonObjectInputTest.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.jackson;
-
-import org.apache.dubbo.common.serialize.model.Organization;
-import org.apache.dubbo.common.serialize.model.Person;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.StringReader;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.List;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.core.Is.is;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * {@link JacksonObjectInput} Unit Test
- */
-public class JacksonObjectInputTest {
-
-    private JacksonObjectInput jacksonObjectInput;
-
-    @Test
-    public void testReadBool() throws IOException {
-        jacksonObjectInput = new JacksonObjectInput(new ByteArrayInputStream("true".getBytes()));
-        boolean result = jacksonObjectInput.readBool();
-
-        assertThat(result, is(true));
-
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("false"));
-        result = jacksonObjectInput.readBool();
-
-        assertThat(result, is(false));
-    }
-
-    @Test
-    public void testReadByte() throws IOException {
-        jacksonObjectInput = new JacksonObjectInput(new ByteArrayInputStream("123".getBytes()));
-        Byte result = jacksonObjectInput.readByte();
-
-        assertThat(result, is(Byte.parseByte("123")));
-    }
-
-    @Test
-    public void testReadBytes() throws IOException {
-        jacksonObjectInput = new JacksonObjectInput(new ByteArrayInputStream("123456".getBytes()));
-        byte[] result = jacksonObjectInput.readBytes();
-
-        assertThat(result, is("123456".getBytes()));
-    }
-
-    @Test
-    public void testReadShort() throws IOException {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("1"));
-        short result = jacksonObjectInput.readShort();
-
-        assertThat(result, is((short) 1));
-    }
-
-    @Test
-    public void testReadInt() throws IOException {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("1"));
-        Integer result = jacksonObjectInput.readInt();
-
-        assertThat(result, is(1));
-    }
-
-    @Test
-    public void testReadDouble() throws IOException {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("1.88"));
-        Double result = jacksonObjectInput.readDouble();
-
-        assertThat(result, is(1.88d));
-    }
-
-    @Test
-    public void testReadLong() throws IOException {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("10"));
-        Long result = jacksonObjectInput.readLong();
-
-        assertThat(result, is(10L));
-    }
-
-    @Test
-    public void testReadFloat() throws IOException {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("1.66"));
-        Float result = jacksonObjectInput.readFloat();
-
-        assertThat(result, is(1.66F));
-    }
-
-    @Test
-    public void testReadUTF() throws IOException {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("\"wording\""));
-        String result = jacksonObjectInput.readUTF();
-
-        assertThat(result, is("wording"));
-    }
-
-    @Test
-    public void testReadObject() throws IOException, ClassNotFoundException {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("{ \"name\":\"John\", \"age\":30 }"));
-        Person result = jacksonObjectInput.readObject(Person.class);
-
-        assertThat(result, not(nullValue()));
-        assertThat(result.getName(), is("John"));
-        assertThat(result.getAge(), is(30));
-    }
-
-    @Test
-    public void testEmptyLine() throws IOException, ClassNotFoundException {
-        Assertions.assertThrows(EOFException.class, () -> {
-            jacksonObjectInput = new JacksonObjectInput(new StringReader(""));
-
-            jacksonObjectInput.readObject();
-        });
-    }
-
-    @Test
-    public void testEmptySpace() throws IOException, ClassNotFoundException {
-        Assertions.assertThrows(EOFException.class, () -> {
-            jacksonObjectInput = new JacksonObjectInput(new StringReader("  "));
-
-            jacksonObjectInput.readObject();
-        });
-    }
-
-    @Test
-    public void testReadObjectWithoutClass() throws IOException, ClassNotFoundException, NoSuchFieldException {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("{ \"name\":\"John\", \"age\":30 }"));
-
-        Map map = jacksonObjectInput.readObject(Map.class);
-
-        assertThat(map, not(nullValue()));
-        assertThat(map.get("name"), is("John"));
-        assertThat(map.get("age"), is(30));
-    }
-
-
-    @Test
-    public void testReadObjectWithTowType() throws Exception {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("[{\"name\":\"John\",\"age\":30},{\"name\":\"Born\",\"age\":24}]"));
-
-        Method methodReturnType = getClass().getMethod("towLayer");
-        Type type = methodReturnType.getGenericReturnType();
-        List<Person> o = jacksonObjectInput.readObject(List.class, type);
-
-        assertTrue(o instanceof List);
-        assertTrue(o.get(0) instanceof Person);
-
-        assertThat(o.size(), is(2));
-        assertThat(o.get(1).getName(), is("Born"));
-    }
-
-    @Test
-    public void testReadObjectWithThreeType() throws Exception {
-        jacksonObjectInput = new JacksonObjectInput(new StringReader("{\"data\":[{\"name\":\"John\",\"age\":30},{\"name\":\"Born\",\"age\":24}]}"));
-
-        Method methodReturnType = getClass().getMethod("threeLayer");
-        Type type = methodReturnType.getGenericReturnType();
-        Organization<List<Person>> o = jacksonObjectInput.readObject(Organization.class, type);
-
-        assertTrue(o instanceof Organization);
-        assertTrue(o.getData() instanceof List);
-        assertTrue(o.getData().get(0) instanceof Person);
-
-        assertThat(o.getData().size(), is(2));
-        assertThat(o.getData().get(1).getName(), is("Born"));
-    }
-
-    public List<Person> towLayer() {
-        return null;
-    }
-
-    public Organization<List<Person>> threeLayer() {
-        return null;
-    }
-
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/jackson/JacksonObjectOutputTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/jackson/JacksonObjectOutputTest.java
deleted file mode 100644
index 52f5f01..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/jackson/JacksonObjectOutputTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.jackson;
-
-import org.apache.dubbo.common.serialize.model.media.Image;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-/**
- * {@link JacksonObjectOutput} Unit Test
- */
-public class JacksonObjectOutputTest {
-
-    private JacksonObjectOutput jacksonObjectOutput;
-    private JacksonObjectInput jacksonObjectInput;
-    private ByteArrayOutputStream byteArrayOutputStream;
-    private ByteArrayInputStream byteArrayInputStream;
-
-    @BeforeEach
-    public void setUp() throws Exception {
-        this.byteArrayOutputStream = new ByteArrayOutputStream();
-        this.jacksonObjectOutput = new JacksonObjectOutput(byteArrayOutputStream);
-    }
-
-    @Test
-    public void testWriteBool() throws IOException {
-        this.jacksonObjectOutput.writeBool(true);
-        this.flushToInput();
-
-        assertThat(jacksonObjectInput.readBool(), is(true));
-    }
-
-    @Test
-    public void testWriteShort() throws IOException {
-        this.jacksonObjectOutput.writeShort((short) 2);
-        this.flushToInput();
-
-        assertThat(jacksonObjectInput.readShort(), is((short) 2));
-    }
-
-    @Test
-    public void testWriteInt() throws IOException {
-        this.jacksonObjectOutput.writeInt(1);
-        this.flushToInput();
-
-        assertThat(jacksonObjectInput.readInt(), is(1));
-    }
-
-    @Test
-    public void testWriteLong() throws IOException {
-        this.jacksonObjectOutput.writeLong(1000L);
-        this.flushToInput();
-
-        assertThat(jacksonObjectInput.readLong(), is(1000L));
-    }
-
-    @Test
-    public void testWriteUTF() throws IOException {
-        this.jacksonObjectOutput.writeUTF("Pace Hasîtî 和平 Мир");
-        this.flushToInput();
-
-        assertThat(jacksonObjectInput.readUTF(), is("Pace Hasîtî 和平 Мир"));
-    }
-
-
-    @Test
-    public void testWriteFloat() throws IOException {
-        this.jacksonObjectOutput.writeFloat(1.88f);
-        this.flushToInput();
-
-        assertThat(this.jacksonObjectInput.readFloat(), is(1.88f));
-    }
-
-    @Test
-    public void testWriteDouble() throws IOException {
-        this.jacksonObjectOutput.writeDouble(1.66d);
-        this.flushToInput();
-
-        assertThat(this.jacksonObjectInput.readDouble(), is(1.66d));
-    }
-
-    @Test
-    public void testWriteBytes() throws IOException {
-        this.jacksonObjectOutput.writeBytes("hello".getBytes());
-        this.flushToInput();
-
-        assertThat(this.jacksonObjectInput.readBytes(), is("hello".getBytes()));
-    }
-
-    @Test
-    public void testWriteBytesWithSubLength() throws IOException {
-        this.jacksonObjectOutput.writeBytes("hello".getBytes(), 2, 2);
-        this.flushToInput();
-
-        assertThat(this.jacksonObjectInput.readBytes(), is("ll".getBytes()));
-    }
-
-    @Test
-    public void testWriteByte() throws IOException {
-        this.jacksonObjectOutput.writeByte((byte) 123);
-        this.flushToInput();
-
-        assertThat(this.jacksonObjectInput.readByte(), is((byte) 123));
-    }
-
-    @Test
-    public void testWriteObject() throws IOException, ClassNotFoundException {
-        Image image = new Image("http://dubbo.apache.org/img/dubbo_white.png", "logo", 300, 480, Image.Size.SMALL);
-        this.jacksonObjectOutput.writeObject(image);
-        this.flushToInput();
-
-        Image readObjectForImage = jacksonObjectInput.readObject(Image.class);
-        assertThat(readObjectForImage, not(nullValue()));
-        assertThat(readObjectForImage, is(image));
-    }
-
-    private void flushToInput() throws IOException {
-        this.jacksonObjectOutput.flushBuffer();
-        this.byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
-        this.jacksonObjectInput = new JacksonObjectInput(byteArrayInputStream);
-    }
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/jackson/JacksonSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/jackson/JacksonSerializationTest.java
deleted file mode 100644
index 4d13d19..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/jackson/JacksonSerializationTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.jackson;
-
-import org.apache.dubbo.common.serialize.ObjectInput;
-import org.apache.dubbo.common.serialize.ObjectOutput;
-import org.hamcrest.MatcherAssert;
-import org.hamcrest.Matchers;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.mockito.Mockito.mock;
-
-/**
- * {@link JacksonSerialization} Unit Test
- */
-public class JacksonSerializationTest {
-
-    private JacksonSerialization jacksonSerialization;
-
-    @BeforeEach
-    public void setUp() {
-        this.jacksonSerialization = new JacksonSerialization();
-    }
-
-    @Test
-    public void testContentTypeId() {
-        MatcherAssert.assertThat(jacksonSerialization.getContentTypeId(), is((byte) 18));
-    }
-
-    @Test
-    public void testContentType() {
-        MatcherAssert.assertThat(jacksonSerialization.getContentType(), is("application/json"));
-    }
-
-    @Test
-    public void testObjectOutput() throws IOException {
-        ObjectOutput objectOutput = jacksonSerialization.serialize(null, mock(OutputStream.class));
-        assertThat(objectOutput, Matchers.instanceOf(JacksonObjectOutput.class));
-    }
-
-    @Test
-    public void testObjectInput() throws IOException {
-        ObjectInput objectInput = jacksonSerialization.deserialize(null, mock(InputStream.class));
-        assertThat(objectInput, Matchers.instanceOf(JacksonObjectInput.class));
-    }
-
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoPersonOkTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoPersonOkTest.java
deleted file mode 100644
index 6ed8636..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoPersonOkTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.kryo;
-
-import org.apache.dubbo.common.serialize.base.AbstractSerializationPersonOkTest;
-
-/**
- * KryoPersonOkTest
- */
-public class KryoPersonOkTest extends AbstractSerializationPersonOkTest {
-
-    {
-        serialization = new KryoSerialization();
-    }
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoSerialization2Test.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoSerialization2Test.java
deleted file mode 100644
index 760761e..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/kryo/KryoSerialization2Test.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.dubbo.common.serialize.kryo;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.serialize.ObjectInput;
-import org.apache.dubbo.common.serialize.ObjectOutput;
-import org.apache.dubbo.common.serialize.Serialization;
-import org.apache.dubbo.common.serialize.kryo.optimized.KryoSerialization2;
-import org.apache.dubbo.common.serialize.model.person.BigPerson;
-import org.apache.dubbo.common.serialize.model.person.FullAddress;
-import org.apache.dubbo.common.serialize.model.person.PersonInfo;
-import org.apache.dubbo.common.serialize.model.person.PersonStatus;
-import org.apache.dubbo.common.serialize.model.person.Phone;
-
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
-
-public class KryoSerialization2Test {
-
-    protected Serialization serialization = new KryoSerialization2();
-
-    protected ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-
-    protected URL url = new URL("protocol", "1.1.1.1", 1234);
-
-    // ================ Primitive Type ================
-    protected BigPerson bigPerson;
-
-    {
-        bigPerson = new BigPerson();
-        bigPerson.setPersonId("superman111");
-        bigPerson.setLoginName("superman");
-        bigPerson.setStatus(PersonStatus.ENABLED);
-        bigPerson.setEmail("sm@1.com");
-        bigPerson.setPenName("pname");
-
-        ArrayList<Phone> phones = new ArrayList<Phone>();
-        Phone phone1 = new Phone("86", "0571", "87654321", "001");
-        Phone phone2 = new Phone("86", "0571", "87654322", "002");
-        phones.add(phone1);
-        phones.add(phone2);
-
-        PersonInfo pi = new PersonInfo();
-        pi.setPhones(phones);
-        Phone fax = new Phone("86", "0571", "87654321", null);
-        pi.setFax(fax);
-        FullAddress addr = new FullAddress("CN", "zj", "3480", "wensanlu", "315000");
-        pi.setFullAddress(addr);
-        pi.setMobileNo("13584652131");
-        pi.setMale(true);
-        pi.setDepartment("b2b");
-        pi.setHomepageUrl("www.capcom.com");
-        pi.setJobTitle("qa");
-        pi.setName("superman");
-
-        bigPerson.setInfoProfile(pi);
-    }
-
-    @Test
-    public void testObject() throws IOException, ClassNotFoundException {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(bigPerson);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(bigPerson, BigPerson.class.cast(deserialize.readObject(BigPerson.class)));
-
-        try {
-            deserialize.readObject(BigPerson.class);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-    @Test
-    public void testObjectWithAttachments() throws IOException, ClassNotFoundException {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(bigPerson);
-
-        Map<String, Object> attachments = new HashMap<>();
-        attachments.put("attachments", "attachments");
-        objectOutput.writeObject(attachments);
-
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(bigPerson, BigPerson.class.cast(deserialize.readObject(BigPerson.class)));
-        assertEquals(attachments, deserialize.readAttachments());
-
-        try {
-            deserialize.readObject(BigPerson.class);
-            fail();
-        } catch (IOException expected) {
-        }
-    }
-
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/AnimalEnum.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/AnimalEnum.java
deleted file mode 100644
index 482337d..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/AnimalEnum.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model;
-
-public enum AnimalEnum {
-    dog, cat, rat, cow, bull, horse
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/BizException.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/BizException.java
deleted file mode 100644
index 96657e7..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/BizException.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model;
-
-public class BizException extends RuntimeException {
-
-    private static final long serialVersionUID = 1L;
-
-    public BizException(String message) {
-        super(message);
-    }
-
-    public BizException() {
-    }
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/BizExceptionNoDefaultConstructor.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/BizExceptionNoDefaultConstructor.java
deleted file mode 100644
index ab41fd5..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/BizExceptionNoDefaultConstructor.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model;
-
-public class BizExceptionNoDefaultConstructor extends RuntimeException {
-
-    private static final long serialVersionUID = 1L;
-
-    public BizExceptionNoDefaultConstructor(String message) {
-        super(message);
-    }
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/Person.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/Person.java
deleted file mode 100644
index 5498622..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/Person.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model;
-
-import java.util.Arrays;
-
-public class Person {
-    byte oneByte = 123;
-    private String name = "name1";
-    private int age = 11;
-
-    private String[] value = {"value1", "value2"};
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public byte getOneByte() {
-        return oneByte;
-    }
-
-    public void setOneByte(byte b) {
-        this.oneByte = b;
-    }
-
-    public int getAge() {
-        return age;
-    }
-
-    public void setAge(int age) {
-        this.age = age;
-    }
-
-    public String[] getValue() {
-        return value;
-    }
-
-    public void setValue(String[] value) {
-        this.value = value;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value));
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + age;
-        result = prime * result + ((name == null) ? 0 : name.hashCode());
-        result = prime * result + Arrays.hashCode(value);
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        Person other = (Person) obj;
-        if (age != other.age)
-            return false;
-        if (name == null) {
-            if (other.name != null)
-                return false;
-        } else if (!name.equals(other.name))
-            return false;
-        if (!Arrays.equals(value, other.value))
-            return false;
-        return true;
-    }
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/SerializablePerson.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/SerializablePerson.java
deleted file mode 100644
index b5ecad9..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/SerializablePerson.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model;
-
-import java.io.Serializable;
-import java.util.Arrays;
-
-public class SerializablePerson implements Serializable {
-    private static final long serialVersionUID = 1L;
-    byte oneByte = 123;
-    private String name = "name1";
-    private int age = 11;
-
-    private String[] value = {"value1", "value2"};
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public byte getOneByte() {
-        return oneByte;
-    }
-
-    public void setOneByte(byte b) {
-        this.oneByte = b;
-    }
-
-    public int getAge() {
-        return age;
-    }
-
-    public void setAge(int age) {
-        this.age = age;
-    }
-
-    public String[] getValue() {
-        return value;
-    }
-
-    public void setValue(String[] value) {
-        this.value = value;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("Person name(%s) age(%d) byte(%s) [value=%s]", name, age, oneByte, Arrays.toString(value));
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + age;
-        result = prime * result + ((name == null) ? 0 : name.hashCode());
-        result = prime * result + Arrays.hashCode(value);
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        SerializablePerson other = (SerializablePerson) obj;
-        if (age != other.age)
-            return false;
-        if (name == null) {
-            if (other.name != null)
-                return false;
-        } else if (!name.equals(other.name))
-            return false;
-        if (!Arrays.equals(value, other.value))
-            return false;
-        return true;
-    }
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/media/Image.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/media/Image.java
deleted file mode 100644
index da235d2..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/media/Image.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model.media;
-
-
-public class Image implements java.io.Serializable {
-    private static final long serialVersionUID = 1L;
-    public String uri;
-    public String title;  // Can be null
-    public int width;
-    public int height;
-    public Size size;
-
-    public Image() {
-    }
-
-    public Image(String uri, String title, int width, int height, Size size) {
-        this.height = height;
-        this.title = title;
-        this.uri = uri;
-        this.width = width;
-        this.size = size;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        Image image = (Image) o;
-
-        if (height != image.height) return false;
-        if (width != image.width) return false;
-        if (size != image.size) return false;
-        if (title != null ? !title.equals(image.title) : image.title != null) return false;
-        if (uri != null ? !uri.equals(image.uri) : image.uri != null) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = uri != null ? uri.hashCode() : 0;
-        result = 31 * result + (title != null ? title.hashCode() : 0);
-        result = 31 * result + width;
-        result = 31 * result + height;
-        result = 31 * result + (size != null ? size.hashCode() : 0);
-        return result;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("[Image ");
-        sb.append("uri=").append(uri);
-        sb.append(", title=").append(title);
-        sb.append(", width=").append(width);
-        sb.append(", height=").append(height);
-        sb.append(", size=").append(size);
-        sb.append("]");
-        return sb.toString();
-    }
-
-    public String getUri() {
-        return uri;
-    }
-
-    public void setUri(String uri) {
-        this.uri = uri;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    public int getWidth() {
-        return width;
-    }
-
-    public void setWidth(int width) {
-        this.width = width;
-    }
-
-    public int getHeight() {
-        return height;
-    }
-
-    public void setHeight(int height) {
-        this.height = height;
-    }
-
-    public Size getSize() {
-        return size;
-    }
-
-    public void setSize(Size size) {
-        this.size = size;
-    }
-
-    public enum Size {
-        SMALL, LARGE
-    }
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/media/Media.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/media/Media.java
deleted file mode 100644
index 555f847..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/media/Media.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model.media;
-
-import java.util.List;
-
-@SuppressWarnings("serial")
-public class Media implements java.io.Serializable {
-    public String uri;
-    public String title;        // Can be unset.
-    public int width;
-    public int height;
-    public String format;
-    public long duration;
-    public long size;
-    public int bitrate;         // Can be unset.
-    public boolean hasBitrate;
-    public List<String> persons;
-    public Player player;
-    public String copyright;    // Can be unset.
-
-    public Media() {
-    }
-
-    public Media(String uri, String title, int width, int height, String format, long duration, long size, int bitrate, boolean hasBitrate, List<String> persons, Player player, String copyright) {
-        this.uri = uri;
-        this.title = title;
-        this.width = width;
-        this.height = height;
-        this.format = format;
-        this.duration = duration;
-        this.size = size;
-        this.bitrate = bitrate;
-        this.hasBitrate = hasBitrate;
-        this.persons = persons;
-        this.player = player;
-        this.copyright = copyright;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        Media media = (Media) o;
-
-        if (bitrate != media.bitrate) return false;
-        if (duration != media.duration) return false;
-        if (hasBitrate != media.hasBitrate) return false;
-        if (height != media.height) return false;
-        if (size != media.size) return false;
-        if (width != media.width) return false;
-        if (copyright != null ? !copyright.equals(media.copyright) : media.copyright != null) return false;
-        if (format != null ? !format.equals(media.format) : media.format != null) return false;
-        if (persons != null ? !persons.equals(media.persons) : media.persons != null) return false;
-        if (player != media.player) return false;
-        if (title != null ? !title.equals(media.title) : media.title != null) return false;
-        if (uri != null ? !uri.equals(media.uri) : media.uri != null) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = uri != null ? uri.hashCode() : 0;
-        result = 31 * result + (title != null ? title.hashCode() : 0);
-        result = 31 * result + width;
-        result = 31 * result + height;
-        result = 31 * result + (format != null ? format.hashCode() : 0);
-        result = 31 * result + (int) (duration ^ (duration >>> 32));
-        result = 31 * result + (int) (size ^ (size >>> 32));
-        result = 31 * result + bitrate;
-        result = 31 * result + (hasBitrate ? 1 : 0);
-        result = 31 * result + (persons != null ? persons.hashCode() : 0);
-        result = 31 * result + (player != null ? player.hashCode() : 0);
-        result = 31 * result + (copyright != null ? copyright.hashCode() : 0);
-        return result;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("[Media ");
-        sb.append("uri=").append(uri);
-        sb.append(", title=").append(title);
-        sb.append(", width=").append(width);
-        sb.append(", height=").append(height);
-        sb.append(", format=").append(format);
-        sb.append(", duration=").append(duration);
-        sb.append(", size=").append(size);
-        sb.append(", hasBitrate=").append(hasBitrate);
-        sb.append(", bitrate=").append(String.valueOf(bitrate));
-        sb.append(", persons=").append(persons);
-        sb.append(", player=").append(player);
-        sb.append(", copyright=").append(copyright);
-        sb.append("]");
-        return sb.toString();
-    }
-
-    public String getUri() {
-        return uri;
-    }
-
-    public void setUri(String uri) {
-        this.uri = uri;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    public int getWidth() {
-        return width;
-    }
-
-    public void setWidth(int width) {
-        this.width = width;
-    }
-
-    public int getHeight() {
-        return height;
-    }
-
-    public void setHeight(int height) {
-        this.height = height;
-    }
-
-    public String getFormat() {
-        return format;
-    }
-
-    public void setFormat(String format) {
-        this.format = format;
-    }
-
-    public long getDuration() {
-        return duration;
-    }
-
-    public void setDuration(long duration) {
-        this.duration = duration;
-    }
-
-    public long getSize() {
-        return size;
-    }
-
-    public void setSize(long size) {
-        this.size = size;
-    }
-
-    public int getBitrate() {
-        return bitrate;
-    }
-
-    public void setBitrate(int bitrate) {
-        this.bitrate = bitrate;
-        this.hasBitrate = true;
-    }
-
-    public List<String> getPersons() {
-        return persons;
-    }
-
-    public void setPersons(List<String> persons) {
-        this.persons = persons;
-    }
-
-    public Player getPlayer() {
-        return player;
-    }
-
-    public void setPlayer(Player player) {
-        this.player = player;
-    }
-
-    public String getCopyright() {
-        return copyright;
-    }
-
-    public void setCopyright(String copyright) {
-        this.copyright = copyright;
-    }
-
-    public enum Player {
-        JAVA, FLASH
-    }
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/media/MediaContent.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/media/MediaContent.java
deleted file mode 100644
index 3b65e99..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/media/MediaContent.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model.media;
-
-import java.util.List;
-
-@SuppressWarnings("serial")
-public class MediaContent implements java.io.Serializable {
-    public Media media;
-    public List<Image> images;
-
-    public MediaContent() {
-    }
-
-    public MediaContent(Media media, List<Image> images) {
-        this.media = media;
-        this.images = images;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        MediaContent that = (MediaContent) o;
-
-        if (images != null ? !images.equals(that.images) : that.images != null) return false;
-        if (media != null ? !media.equals(that.media) : that.media != null) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = media != null ? media.hashCode() : 0;
-        result = 31 * result + (images != null ? images.hashCode() : 0);
-        return result;
-    }
-
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("[MediaContent: ");
-        sb.append("media=").append(media);
-        sb.append(", images=").append(images);
-        sb.append("]");
-        return sb.toString();
-    }
-
-    public Media getMedia() {
-        return media;
-    }
-
-    public void setMedia(Media media) {
-        this.media = media;
-    }
-
-    public List<Image> getImages() {
-        return images;
-    }
-
-    public void setImages(List<Image> images) {
-        this.images = images;
-    }
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/FullAddress.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/FullAddress.java
deleted file mode 100644
index 4f762cf..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/FullAddress.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model.person;
-
-import java.io.Serializable;
-
-public class FullAddress implements Serializable {
-
-    private static final long serialVersionUID = 5163979984269419831L;
-
-    private String countryId;
-
-    private String countryName;
-
-    private String provinceName;
-
-    private String cityId;
-
-    private String cityName;
-
-    private String streetAddress;
-
-    private String zipCode;
-
-    public FullAddress() {
-    }
-
-    public FullAddress(String countryId, String provinceName, String cityId, String streetAddress,
-                       String zipCode) {
-        this.countryId = countryId;
-        this.countryName = countryId;
-        this.provinceName = provinceName;
-        this.cityId = cityId;
-        this.cityName = cityId;
-        this.streetAddress = streetAddress;
-        this.zipCode = zipCode;
-    }
-
-    public FullAddress(String countryId, String countryName, String provinceName, String cityId,
-                       String cityName, String streetAddress, String zipCode) {
-        this.countryId = countryId;
-        this.countryName = countryName;
-        this.provinceName = provinceName;
-        this.cityId = cityId;
-        this.cityName = cityName;
-        this.streetAddress = streetAddress;
-        this.zipCode = zipCode;
-    }
-
-    public String getCountryId() {
-        return countryId;
-    }
-
-    public void setCountryId(String countryId) {
-        this.countryId = countryId;
-    }
-
-    public String getCountryName() {
-        return countryName;
-    }
-
-    public void setCountryName(String countryName) {
-        this.countryName = countryName;
-    }
-
-    public String getProvinceName() {
-        return provinceName;
-    }
-
-    public void setProvinceName(String provinceName) {
-        this.provinceName = provinceName;
-    }
-
-    public String getCityId() {
-        return cityId;
-    }
-
-    public void setCityId(String cityId) {
-        this.cityId = cityId;
-    }
-
-    public String getCityName() {
-        return cityName;
-    }
-
-    public void setCityName(String cityName) {
-        this.cityName = cityName;
-    }
-
-    public String getStreetAddress() {
-        return streetAddress;
-    }
-
-    public void setStreetAddress(String streetAddress) {
-        this.streetAddress = streetAddress;
-    }
-
-    public String getZipCode() {
-        return zipCode;
-    }
-
-    public void setZipCode(String zipCode) {
-        this.zipCode = zipCode;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((cityId == null) ? 0 : cityId.hashCode());
-        result = prime * result + ((cityName == null) ? 0 : cityName.hashCode());
-        result = prime * result + ((countryId == null) ? 0 : countryId.hashCode());
-        result = prime * result + ((countryName == null) ? 0 : countryName.hashCode());
-        result = prime * result + ((provinceName == null) ? 0 : provinceName.hashCode());
-        result = prime * result + ((streetAddress == null) ? 0 : streetAddress.hashCode());
-        result = prime * result + ((zipCode == null) ? 0 : zipCode.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        FullAddress other = (FullAddress) obj;
-        if (cityId == null) {
-            if (other.cityId != null)
-                return false;
-        } else if (!cityId.equals(other.cityId))
-            return false;
-        if (cityName == null) {
-            if (other.cityName != null)
-                return false;
-        } else if (!cityName.equals(other.cityName))
-            return false;
-        if (countryId == null) {
-            if (other.countryId != null)
-                return false;
-        } else if (!countryId.equals(other.countryId))
-            return false;
-        if (countryName == null) {
-            if (other.countryName != null)
-                return false;
-        } else if (!countryName.equals(other.countryName))
-            return false;
-        if (provinceName == null) {
-            if (other.provinceName != null)
-                return false;
-        } else if (!provinceName.equals(other.provinceName))
-            return false;
-        if (streetAddress == null) {
-            if (other.streetAddress != null)
-                return false;
-        } else if (!streetAddress.equals(other.streetAddress))
-            return false;
-        if (zipCode == null) {
-            if (other.zipCode != null)
-                return false;
-        } else if (!zipCode.equals(other.zipCode))
-            return false;
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        if (countryName != null && countryName.length() > 0) {
-            sb.append(countryName);
-        }
-        if (provinceName != null && provinceName.length() > 0) {
-            sb.append(" ");
-            sb.append(provinceName);
-        }
-        if (cityName != null && cityName.length() > 0) {
-            sb.append(" ");
-            sb.append(cityName);
-        }
-        if (streetAddress != null && streetAddress.length() > 0) {
-            sb.append(" ");
-            sb.append(streetAddress);
-        }
-        return sb.toString();
-    }
-
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/PersonStatus.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/PersonStatus.java
deleted file mode 100644
index f5ae72c..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/PersonStatus.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model.person;
-
-public enum PersonStatus {
-    ENABLED,
-    DISABLED
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/Phone.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/Phone.java
deleted file mode 100644
index 1822eae..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/model/person/Phone.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.model.person;
-
-import java.io.Serializable;
-
-public class Phone implements Serializable {
-
-    private static final long serialVersionUID = 4399060521859707703L;
-
-    private String country;
-
-    private String area;
-
-    private String number;
-
-    private String extensionNumber;
-
-    public Phone() {
-    }
-
-    public Phone(String country, String area, String number, String extensionNumber) {
-        this.country = country;
-        this.area = area;
-        this.number = number;
-        this.extensionNumber = extensionNumber;
-    }
-
-    public String getCountry() {
-        return country;
-    }
-
-    public void setCountry(String country) {
-        this.country = country;
-    }
-
-    public String getArea() {
-        return area;
-    }
-
-    public void setArea(String area) {
-        this.area = area;
-    }
-
-    public String getNumber() {
-        return number;
-    }
-
-    public void setNumber(String number) {
-        this.number = number;
-    }
-
-    public String getExtensionNumber() {
-        return extensionNumber;
-    }
-
-    public void setExtensionNumber(String extensionNumber) {
-        this.extensionNumber = extensionNumber;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((area == null) ? 0 : area.hashCode());
-        result = prime * result + ((country == null) ? 0 : country.hashCode());
-        result = prime * result + ((extensionNumber == null) ? 0 : extensionNumber.hashCode());
-        result = prime * result + ((number == null) ? 0 : number.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
-            return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        Phone other = (Phone) obj;
-        if (area == null) {
-            if (other.area != null)
-                return false;
-        } else if (!area.equals(other.area))
-            return false;
-        if (country == null) {
-            if (other.country != null)
-                return false;
-        } else if (!country.equals(other.country))
-            return false;
-        if (extensionNumber == null) {
-            if (other.extensionNumber != null)
-                return false;
-        } else if (!extensionNumber.equals(other.extensionNumber))
-            return false;
-        if (number == null) {
-            if (other.number != null)
-                return false;
-        } else if (!number.equals(other.number))
-            return false;
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        if (country != null && country.length() > 0) {
-            sb.append(country);
-            sb.append("-");
-        }
-        if (area != null && area.length() > 0) {
-            sb.append(area);
-            sb.append("-");
-        }
-        if (number != null && number.length() > 0) {
-            sb.append(number);
-        }
-        if (extensionNumber != null && extensionNumber.length() > 0) {
-            sb.append("-");
-            sb.append(extensionNumber);
-        }
-        return sb.toString();
-    }
-
-}
\ No newline at end of file
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/AbstractProtobufSerializationTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/AbstractProtobufSerializationTest.java
deleted file mode 100644
index 88ad01a..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/AbstractProtobufSerializationTest.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.protobuf.support;
-
-import org.apache.dubbo.common.URL;
-import org.apache.dubbo.common.serialize.ObjectInput;
-import org.apache.dubbo.common.serialize.ObjectOutput;
-import org.apache.dubbo.common.serialize.Serialization;
-import org.apache.dubbo.common.serialize.protobuf.support.model.GooglePB;
-
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.fail;
-
-public class AbstractProtobufSerializationTest {
-    protected static Random random = new Random();
-    protected URL url = new URL("protocol", "1.1.1.1", 1234);
-    protected ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-    protected Serialization serialization = new GenericProtobufSerialization();
-
-    @Test
-    public void test_Bool() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeBool(false);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertFalse(deserialize.readBool());
-
-        try {
-            deserialize.readBool();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_Bool_Multi() throws Exception {
-        boolean[] array = new boolean[100];
-        for (int i = 0; i < array.length; i++) {
-            array[i] = random.nextBoolean();
-        }
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        for (boolean b : array) {
-            objectOutput.writeBool(b);
-        }
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        for (boolean b : array) {
-            assertEquals(b, deserialize.readBool());
-        }
-
-        try {
-            deserialize.readBool();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_Byte() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeByte((byte) 123);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals((byte) 123, deserialize.readByte());
-
-        try {
-            deserialize.readByte();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_Byte_Multi() throws Exception {
-        byte[] array = new byte[100];
-        random.nextBytes(array);
-
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        for (byte b : array) {
-            objectOutput.writeByte(b);
-        }
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        for (byte b : array) {
-            assertEquals(b, deserialize.readByte());
-        }
-
-        try {
-            deserialize.readByte();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_Short() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeShort((short) 123);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals((short) 123, deserialize.readShort());
-
-        try {
-            deserialize.readShort();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_Integer() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeInt(1);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        int i = deserialize.readInt();
-        assertEquals(1, i);
-
-        try {
-            deserialize.readInt();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_Long() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeLong(123L);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(123L, deserialize.readLong());
-
-        try {
-            deserialize.readLong();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_Float() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeFloat(1.28F);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(1.28F, deserialize.readFloat());
-
-        try {
-            deserialize.readFloat();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    // ================== Util methods ==================
-
-    @Test
-    public void test_Double() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeDouble(1.28);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals(1.28, deserialize.readDouble());
-
-        try {
-            deserialize.readDouble();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_UtfString() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeUTF("123中华人民共和国");
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertEquals("123中华人民共和国", deserialize.readUTF());
-
-        try {
-            deserialize.readUTF();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_Bytes() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeBytes("123中华人民共和国".getBytes());
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        assertArrayEquals("123中华人民共和国".getBytes(), deserialize.readBytes());
-
-        try {
-            deserialize.readBytes();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    @Test
-    public void test_BytesRange() throws Exception {
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeBytes("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, 9);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput deserialize = serialization.deserialize(url, byteArrayInputStream);
-
-        byte[] expectedArray = new byte[9];
-        System.arraycopy("123中华人民共和国-新疆维吾尔自治区".getBytes(), 1, expectedArray, 0, expectedArray.length);
-        assertArrayEquals(expectedArray, deserialize.readBytes());
-
-        try {
-            deserialize.readBytes();
-            fail();
-        } catch (Exception expected) {
-            expected.printStackTrace();
-        }
-    }
-
-    private GooglePB.PBRequestType buildPbMessage() {
-        Random random = new Random();
-        final int bound = 100000;
-        List<GooglePB.PhoneNumber> phoneNumberList = new ArrayList<>();
-        for (int i = 0; i < 5; i++) {
-            phoneNumberList.add(GooglePB.PhoneNumber.newBuilder().setNumber(random.nextInt(bound) + "").setType(GooglePB.PhoneType.forNumber(random.nextInt(GooglePB.PhoneType.values().length - 1))).build());
-        }
-
-        Map<String, GooglePB.PhoneNumber> phoneNumberMap = new HashMap<>();
-        for (int i = 0; i < 5; i++) {
-            phoneNumberMap.put("phoneNumber" + i, GooglePB.PhoneNumber.newBuilder().setNumber(random.nextInt(bound) + "").setType(GooglePB.PhoneType.forNumber(random.nextInt(GooglePB.PhoneType.values().length - 1))).build());
-        }
-        GooglePB.PBRequestType request = GooglePB.PBRequestType.newBuilder()
-            .setAge(15).setCash(10).setMoney(16.0).setNum(100L)
-            .addAllPhone(phoneNumberList).putAllDoubleMap(phoneNumberMap).build();
-        return request;
-    }
-
-    @Test
-    public void testPbNormal() throws Exception {
-        ProtobufUtils.marshaller(GooglePB.PBRequestType.getDefaultInstance());
-        GooglePB.PBRequestType request = buildPbMessage();
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeObject(request);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput objectInput = serialization.deserialize(url, byteArrayInputStream);
-
-        GooglePB.PBRequestType derializedRequest = objectInput.readObject(GooglePB.PBRequestType.class);
-        assertEquals(request, derializedRequest);
-    }
-
-    /**
-     * Special test case
-     * Dubbo protocol will directly writes native map (Invocation.attachments) using protobuf.
-     * this should definitely be fixed but not done yet.
-     */
-    @Test
-    public void testPbMap() throws Exception {
-        Map<String, Object> attachments = new HashMap<>();
-        attachments.put("key", "value");
-        attachments.put("int", Integer.MAX_VALUE);
-        attachments.put("long", Long.MAX_VALUE);
-        attachments.put("bool", true);
-        attachments.put("float", 0.0001);
-        attachments.put("double", 0.0001d);
-        attachments.put("null", null);
-        ObjectOutput objectOutput = serialization.serialize(url, byteArrayOutputStream);
-        objectOutput.writeAttachments(attachments);
-        objectOutput.flushBuffer();
-
-        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
-            byteArrayOutputStream.toByteArray());
-        ObjectInput objectInput = serialization.deserialize(url, byteArrayInputStream);
-
-        Map<String, Object> derializedAttachments = objectInput.readAttachments();
-        assertEquals(attachments, derializedAttachments);
-    }
-
-    @Test
-    public void testPbThrowable() {
-
-    }
-
-    @Test
-    public void testNotPb() {
-
-    }
-
-}
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutputTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutputTest.java
deleted file mode 100644
index 38f5150..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protobuf/support/GenericProtobufJsonObjectOutputTest.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.protobuf.support;
-
-import org.apache.dubbo.common.serialize.protobuf.support.model.GooglePB;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-
-public class GenericProtobufJsonObjectOutputTest {
-    private ByteArrayOutputStream byteArrayOutputStream;
-    private GenericProtobufJsonObjectOutput genericProtobufObjectOutput;
-    private GenericProtobufJsonObjectInput genericProtobufObjectInput;
-    private ByteArrayInputStream byteArrayInputStream;
-
-    @BeforeEach
-    public void setUp() {
-        this.byteArrayOutputStream = new ByteArrayOutputStream();
-        this.genericProtobufObjectOutput = new GenericProtobufJsonObjectOutput(byteArrayOutputStream);
-    }
-
-    @Test
-    public void testWriteObjectNull() throws IOException {
-        assertThrows(IllegalArgumentException.class, () -> {
-            this.genericProtobufObjectOutput.writeObject(null);
-        });
-    }
-
-    @Test
-    public void testWriteGooglePbObject() throws IOException {
-        Random random = new Random();
-        final int bound = 100000;
-        List<GooglePB.PhoneNumber> phoneNumberList = new ArrayList<>();
-        for (int i = 0; i < 5; i++) {
-            phoneNumberList.add(GooglePB.PhoneNumber.newBuilder().setNumber(random.nextInt(bound) + "").setType(GooglePB.PhoneType.forNumber(random.nextInt(GooglePB.PhoneType.values().length - 1))).build());
-        }
-
-        Map<String, GooglePB.PhoneNumber> phoneNumberMap = new HashMap<>();
-        for (int i = 0; i < 5; i++) {
-            phoneNumberMap.put("phoneNumber" + i, GooglePB.PhoneNumber.newBuilder().setNumber(random.nextInt(bound) + "").setType(GooglePB.PhoneType.forNumber(random.nextInt(GooglePB.PhoneType.values().length - 1))).build());
-        }
-        GooglePB.PBRequestType request = GooglePB.PBRequestType.newBuilder()
-            .setAge(15).setCash(10).setMoney(16.0).setNum(100L)
-            .addAllPhone(phoneNumberList).putAllDoubleMap(phoneNumberMap).build();
-
-        this.genericProtobufObjectOutput.writeObject(request);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readObject(GooglePB.PBRequestType.class), is(request));
-    }
-
-    @Test
-    public void testWriteBoolean() throws IOException {
-        boolean random = new Random().nextBoolean();
-        this.genericProtobufObjectOutput.writeBool(random);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readBool(), is(random));
-    }
-
-    @Test
-    public void testWriteByte() throws IOException {
-        int random = new Random().nextInt();
-        this.genericProtobufObjectOutput.writeByte((byte) random);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readByte(), is((byte) random));
-    }
-
-    @Test
-    public void testWriteShort() throws IOException {
-        int random = new Random().nextInt();
-        this.genericProtobufObjectOutput.writeShort((short) random);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readShort(), is((short) random));
-    }
-
-    @Test
-    public void testWriteInt() throws IOException {
-        int random = new Random().nextInt();
-        this.genericProtobufObjectOutput.writeInt(random);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readInt(), is(random));
-    }
-
-    @Test
-    public void testWriteFloat() throws IOException {
-        float random = new Random().nextFloat();
-        this.genericProtobufObjectOutput.writeFloat(random);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readFloat(), is(random));
-    }
-
-
-    @Test
-    public void testWriteDouble() throws IOException {
-        double random = new Random().nextDouble();
-        this.genericProtobufObjectOutput.writeDouble(random);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readDouble(), is(random));
-    }
-
-
-    @Test
-    public void testWriteString() throws IOException {
-        byte[] bytes = new byte[new Random().nextInt(100)];
-        new Random().nextBytes(bytes);
-
-        this.genericProtobufObjectOutput.writeUTF(new String(bytes));
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readUTF(), is(new String(bytes)));
-    }
-
-    @Test
-    public void testWriteBytes() throws IOException {
-        byte[] bytes = new byte[new Random().nextInt(100)];
-        new Random().nextBytes(bytes);
-        this.genericProtobufObjectOutput.writeBytes(bytes);
-        this.flushToInput();
-        final byte[] bytes1 = genericProtobufObjectInput.readBytes();
-        assertThat(bytes1, is(bytes));
-    }
-
-    @Test
-    public void testWriteBytesSpecLength() throws IOException {
-        final int length = new Random().nextInt(100);
-        byte[] bytes = new byte[length];
-        new Random().nextBytes(bytes);
-        this.genericProtobufObjectOutput.writeBytes(bytes, 0, length);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readBytes(), is(bytes));
-    }
-
-    @Test
-    public void testWriteLong() throws IOException {
-        long random = new Random().nextLong();
-        this.genericProtobufObjectOutput.writeLong(random);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readLong(), is(random));
-    }
-
-
-    @Test
-    public void testWriteMap() throws IOException, ClassNotFoundException {
-        Map<String, Object> map = new HashMap<>();
-        map.put("key", "hello");
-        map.put("value", "dubbo");
-        this.genericProtobufObjectOutput.writeAttachments(map);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readAttachments(), is(map));
-    }
-
-
-    @Test
-    void testWriteMultiType() throws IOException, ClassNotFoundException {
-        long random = new Random().nextLong();
-        this.genericProtobufObjectOutput.writeLong(random);
-        Map<String, Object> map = new HashMap<>();
-        map.put("key", "hello");
-        map.put("value", "world");
-        this.genericProtobufObjectOutput.writeAttachments(map);
-        final int length = new Random().nextInt(100);
-        byte[] bytes = new byte[length];
-        new Random().nextBytes(bytes);
-        this.genericProtobufObjectOutput.writeBytes(bytes, 0, length);
-        int randomShort = new Random().nextInt();
-        this.genericProtobufObjectOutput.writeShort((short) randomShort);
-        this.flushToInput();
-        assertThat(genericProtobufObjectInput.readLong(), is(random));
-        assertThat(genericProtobufObjectInput.readAttachments(), is(map));
-        assertThat(genericProtobufObjectInput.readBytes(), is(bytes));
-        assertThat(genericProtobufObjectInput.readShort(), is((short) randomShort));
-    }
-
-    private void flushToInput() {
-        this.genericProtobufObjectOutput.flushBuffer();
-        this.byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
-        this.genericProtobufObjectInput = new GenericProtobufJsonObjectInput(byteArrayInputStream);
-    }
-}
-
diff --git a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffObjectOutputTest.java b/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffObjectOutputTest.java
deleted file mode 100644
index 693a8e3..0000000
--- a/dubbo-serialization-extensions/dubbo-serialization-test/src/test/java/org/apache/dubbo/common/serialize/protostuff/ProtostuffObjectOutputTest.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.dubbo.common.serialize.protostuff;
-
-import org.apache.dubbo.common.serialize.model.SerializablePerson;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.Serializable;
-import java.sql.Timestamp;
-import java.time.LocalTime;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.nullValue;
-
-public class ProtostuffObjectOutputTest {
-
-    private ByteArrayOutputStream byteArrayOutputStream;
-    private ProtostuffObjectOutput protostuffObjectOutput;
-    private ProtostuffObjectInput protostuffObjectInput;
-    private ByteArrayInputStream byteArrayInputStream;
-
-    @BeforeEach
-    public void setUp() throws Exception {
-        this.byteArrayOutputStream = new ByteArrayOutputStream();
-        this.protostuffObjectOutput = new ProtostuffObjectOutput(byteArrayOutputStream);
-    }
-
-    @Test
-    public void testWriteObjectNull() throws IOException, ClassNotFoundException {
-        this.protostuffObjectOutput.writeObject(null);
-        this.flushToInput();
-
-        assertThat(protostuffObjectInput.readObject(), nullValue());
-    }
-
-    @Test
-    public void testSerializeTimestamp() throws IOException, ClassNotFoundException {
-        Timestamp originTime = new Timestamp(System.currentTimeMillis());
-        this.protostuffObjectOutput.writeObject(originTime);
-        this.flushToInput();
-
-        Timestamp serializedTime = protostuffObjectInput.readObject(Timestamp.class);
-        assertThat(serializedTime, is(originTime));
-    }
-
-    @Test
-    public void testSerializeSqlDate() throws IOException, ClassNotFoundException {
-        java.sql.Date originTime = new java.sql.Date(System.currentTimeMillis());
-        this.protostuffObjectOutput.writeObject(originTime);
-        this.flushToInput();
-
-        java.sql.Date serializedTime = protostuffObjectInput.readObject(java.sql.Date.class);
-        assertThat(serializedTime, is(originTime));
-    }
-
-    @Test
-    public void testObjectList() throws IOException, ClassNotFoundException {
-        List<SerializablePerson> args = new ArrayList<SerializablePerson>();
-        args.add(new SerializablePerson());
-
-        this.protostuffObjectOutput.writeObject(args);
-        this.flushToInput();
-
-        List<SerializablePerson> serializedTime = (List<SerializablePerson>) protostuffObjectInput.readObject();
-        assertThat(serializedTime, is(args));
-    }
-
-    @Test
-    public void testCustomizeDateList() throws IOException, ClassNotFoundException {
-        java.sql.Date originTime = new java.sql.Date(System.currentTimeMillis());
-        java.sql.Date yesterdayTime = new java.sql.Date(System.currentTimeMillis() + 30 * 60 * 1000);
-        java.sql.Date beforeTime = new java.sql.Date(System.currentTimeMillis() + 30 * 60 * 1000 * 4);
-        List<java.sql.Date> list = new ArrayList<>();
-
-        list.add(originTime);
-        list.add(yesterdayTime);
-        list.add(beforeTime);
-
-        this.protostuffObjectOutput.writeObject(list);
-        this.flushToInput();
-
-        List<java.sql.Date> serializedTimeList = (List<java.sql.Date>) protostuffObjectInput.readObject();
-        assertThat(serializedTimeList, is(list));
-    }
-
-    @Test
-    public void testCustomizeTimeList() throws IOException, ClassNotFoundException {
-
-        List<LocalTime> list = new ArrayList<LocalTime>();
-
-        LocalTime localTime = LocalTime.parse("12:00:00");
-        LocalTime localSecondTime = LocalTime.parse("13:00:00");
-        LocalTime localThirdTime = LocalTime.parse("14:00:00");
-        list.add(localTime);
-        list.add(localSecondTime);
-        list.add(localThirdTime);
-
-        LocalTimeList timeList = new LocalTimeList(list);
-        this.protostuffObjectOutput.writeObject(timeList);
-        this.flushToInput();
-
-        LocalTimeList serializedTime = protostuffObjectInput.readObject(LocalTimeList.class);
-        assertThat(serializedTime, is(timeList));
-    }
-
-    @Test
-    public void testListObject() throws IOException, ClassNotFoundException {
-
-        List<SerializablePerson> list = new ArrayList<SerializablePerson>();
-
-        list.add(new SerializablePerson());
-        list.add(new SerializablePerson());
-        list.add(new SerializablePerson());
-
-        SerializablePersonList personList = new SerializablePersonList(list);
-
-        this.protostuffObjectOutput.writeObject(personList);
-        this.flushToInput();
-
-        SerializablePersonList serializedTime = protostuffObjectInput.readObject(SerializablePersonList.class);
-        assertThat(serializedTime, is(personList));
-    }
-
-
-    @Test
-    public void testSerializeSqlTime() throws IOException, ClassNotFoundException {
-        java.sql.Time originTime = new java.sql.Time(System.currentTimeMillis());
-        this.protostuffObjectOutput.writeObject(originTime);
-        this.flushToInput();
-
-        java.sql.Time serializedTime = protostuffObjectInput.readObject(java.sql.Time.class);
-        assertThat(serializedTime, is(originTime));
-    }
-
-    @Test
-    public void testSerializeDate() throws IOException, ClassNotFoundException {
-        Date originTime = new Date();
-        this.protostuffObjectOutput.writeObject(originTime);
-        this.flushToInput();
-
-        Date serializedTime = protostuffObjectInput.readObject(Date.class);
-        assertThat(serializedTime, is(originTime));
-    }
-
-    private void flushToInput() throws IOException {
-        this.protostuffObjectOutput.flushBuffer();
-        this.byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
-        this.protostuffObjectInput = new ProtostuffObjectInput(byteArrayInputStream);
-    }
-
-    private static class SerializablePersonList implements Serializable {
-        private static final long serialVersionUID = 1L;
-
-        public List<SerializablePerson> personList;
-
-        public SerializablePersonList() {
-        }
-
-        public SerializablePersonList(List<SerializablePerson> list) {
-            this.personList = list;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (obj == null)
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-
-            SerializablePersonList list = (SerializablePersonList) obj;
-            if (list.personList == null && this.personList == null)
-                return true;
-            if (list.personList == null || this.personList == null)
-                return false;
-            if (list.personList.size() != this.personList.size())
-                return false;
-            for (int i = 0; i < this.personList.size(); i++) {
-                if (!this.personList.get(i).equals(list.personList.get(i)))
-                    return false;
-            }
-            return true;
-        }
-    }
-
-    private static class LocalTimeList implements Serializable {
-        private static final long serialVersionUID = 1L;
-
-        List<LocalTime> timeList;
-
-        public LocalTimeList() {
-        }
-
-        public LocalTimeList(List<LocalTime> timeList) {
-            this.timeList = timeList;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (obj == null)
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-
-            LocalTimeList timeList = (LocalTimeList) obj;
-            if (timeList.timeList == null && this.timeList == null)
-                return true;
-            if (timeList.timeList == null || this.timeList == null)
-                return false;
-            if (timeList.timeList.size() != this.timeList.size())
-                return false;
-            for (int i = 0; i < this.timeList.size(); i++) {
-                if (!this.timeList.get(i).equals(timeList.timeList.get(i)))
-                    return false;
-            }
-            return true;
-        }
-    }
-}
diff --git a/dubbo-serialization-extensions/pom.xml b/dubbo-serialization-extensions/pom.xml
index 442cd91..8a607d3 100644
--- a/dubbo-serialization-extensions/pom.xml
+++ b/dubbo-serialization-extensions/pom.xml
@@ -29,7 +29,7 @@
     <packaging>pom</packaging>
     <artifactId>dubbo-serialization-extensions</artifactId>
     <properties>
-        <dubbo.version>3.2.7</dubbo.version>
+
     </properties>
 
     <modules>
diff --git a/dubbo-tag-extensions/README.md b/dubbo-tag-extensions/README.md
index 8051960..5d40544 100644
--- a/dubbo-tag-extensions/README.md
+++ b/dubbo-tag-extensions/README.md
@@ -1,6 +1,6 @@
 # dubbo tag subnets
 
-[中文](./README.md)
+[中文](./README_zh.md)
 
 dubbo-tag-subnets will generate a tag by subnets, then consumer will prefer rpc provider in the same subnets. 
 
diff --git a/dubbo-tag-extensions/README_zh.md b/dubbo-tag-extensions/README_zh.md
index e3c31db..2b05790 100644
--- a/dubbo-tag-extensions/README_zh.md
+++ b/dubbo-tag-extensions/README_zh.md
@@ -1,6 +1,6 @@
 # dubbo tag subnets
 
-[中文](./README.md)
+[English](./README.md)
 
 dubbo-tag-subnets 会根据子网生成tag, 然后同子网内的tag相同, 服务调用会优先发生在同子网中.
 
diff --git a/dubbo-tag-extensions/dubbo-tag-subnets/pom.xml b/dubbo-tag-extensions/dubbo-tag-subnets/pom.xml
index 99b6b31..21fe496 100644
--- a/dubbo-tag-extensions/dubbo-tag-subnets/pom.xml
+++ b/dubbo-tag-extensions/dubbo-tag-subnets/pom.xml
@@ -24,7 +24,7 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <version>1.0.2-SNAPSHOT</version>
+    <version>${revision}</version>
     <artifactId>dubbo-tag-subnets</artifactId>
     <name>${project.artifactId}</name>
     <description>The tag subnets module of dubbo project</description>
diff --git a/dubbo-wasm/README.md b/dubbo-wasm/README.md
new file mode 100644
index 0000000..7ea3858
--- /dev/null
+++ b/dubbo-wasm/README.md
@@ -0,0 +1,245 @@
+# dubbo WASM api
+
+[中文](./README_zh.md)
+
+At present, Dubbo's SPI extensions can only be written in Java language, the dubbo-wasm module aims to overcome this limitation.
+
+WASM(WebAssembly) bytecode is designed to be encoded in a size- and load-time-efficient binary format. WASM aims to leverage the common hardware features available on various platforms to execute in browsers at machine code speed.
+
+WASI(WebAssembly System Interface) provide a portable interface for applications that run within a constrained sandbox environment, which allows WASM to run in non browser environments such as Linux. It's portable and secure.
+
+We use [wasmtime-java](https://github.com/kawamuray/wasmtime-java) to run WASI in Java, as long as the language can be compiled into WASM bytecode, it can be used to write SPI extensions for dubbo.
+
+## Module structure
+
+* dubbo-wasm-api: Responsible for defining basic exceptions and providing runtime for executing WASM bytecode (Dubbo API)
+* dubbo-wasm-cluster-api: Provide the WASM version of the dubbo-cluster module SPIs
+* dubbo-wasm-common-api: Provide the WASM version of the dubbo-common module SPIs
+* dubbo-wasm-metrics-api(todo): Provide the WASM version of the dubbo-metrics module SPIs
+* dubbo-wasm-registry-api: Provide the WASM version of the dubbo-registry-api module SPIs
+* dubbo-wasm-remoting-api(todo): Provide the WASM version of the dubbo-remoting-api module SPIs
+* dubbo-wasm-rpc-api: Provide the WASM version of the dubbo-rpc-api module SPIs
+
+## Note
+
+Due to the strict requirements of WASM on parameter types and for simplicity reasons, types other than `java.lang.Long`/`java.lang.Integer` are not used as parameters or return value.
+
+## How to use
+
+Below is an example of implementing `org.apache.dubbo.rpc.cluster.LoadBalance` SPI in rust.
+
+### 1.Create subproject in another language
+
+```shell
+cargo new --lib your_subproject_name
+```
+
+### 2.Add methods to lib.rs
+
+```rust
+// Adding `#[no_mangle]` to prevent the Rust compiler from modifying method names is mandatory
+#[no_mangle]
+pub unsafe extern "C" fn doSelect(_arg_id: i64) -> i32 {
+    1
+}
+```
+
+### 3.Add `[lib]` to `Cargo.toml` and change `crate-type` to `["cdylib"]`. Ultimately, your `Cargo.toml` should look like
+
+```toml
+[package]
+name = "your_subproject_name"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+# ......
+
+[lib]
+crate-type = ["cdylib"]
+```
+
+### 4.Generate the wasm file
+
+```shell
+cargo build --target wasm32-wasi --release
+```
+
+You will see `{your_subproject_name}/target/wasm32-wasi/release/{your_subproject_name}.wasm`.
+
+### 5.Add dependency to `pom.xml`
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-wasm-cluster-api</artifactId>
+    <version>${revision}</version>
+</dependency>
+```
+
+### 6.Add `x.y.z.RustLoadBalance.java`
+
+```java
+package x.y.z;
+
+public class RustLoadBalance extends AbstractWasmLoadBalance {
+    public static final String NAME = "rust";
+}
+```
+
+### 7.Add `resources/META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance`
+
+```text
+rust=x.y.z.RustLoadBalance
+```
+
+### 8.Rename the WASM file
+
+Due to the class `x.y.z.RustLoadBalance.java`,the final wasm file name should be `x.y.z.RustLoadBalance.wasm`, finally, put the wasm file in the `resources` folder of your module.
+
+## Communicate with Java
+
+Below is an example of using rust.
+
+### Pass args to another language
+
+#### 1.Export method to another language
+
+```java
+public class RustLoadBalance extends AbstractWasmLoadBalance {
+    //......
+    @Override
+    protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+        Map<String, Func> funcMap = new HashMap<>();
+        //......
+        funcMap.put("get_args", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32, 
+            (argId, addr, len) -> {
+                String config = "hello from java " + argId;
+                System.out.println("java side->" + config);
+                assertEquals("hello from java 0", config);
+                ByteBuffer buf = supplier.get();
+                for (int i = 0; i < len && i < config.length(); i++) {
+                    buf.put(addr.intValue() + i, (byte) config.charAt(i));
+                }
+                return Math.min(config.length(), len);
+            }));
+        //......
+        return funcMap;
+    }
+}
+```
+
+#### 2.Import method in another language
+
+```rust
+#[link(wasm_import_module = "dubbo")]
+extern "C" {
+    fn get_args(arg_id: i64, addr: i64, len: i32) -> i32;
+}
+```
+
+#### 3.Get args in another language
+
+```rust
+#[no_mangle]
+pub unsafe extern "C" fn doSelect(arg_id: i64) -> i32 {
+    //......
+    let mut buf = [0u8; 32];
+    let buf_ptr = buf.as_mut_ptr() as i64;
+    eprintln!("rust side-> buffer base address: {}", buf_ptr);
+    // get arg from java
+    let len = get_args(arg_id, buf_ptr, buf.len() as i32);
+    let java_arg = std::str::from_utf8(&buf[..len as usize]).unwrap();
+    eprintln!("rust side-> recv:{}", java_arg);
+    //......
+}
+```
+
+### Pass result to Java
+
+Below is an example of using rust.
+
+#### 1.Export method to another language
+
+```java
+public class RustLoadBalance extends AbstractWasmLoadBalance {
+    //......
+    
+    private static final Map<Long, String> RESULTS = new ConcurrentHashMap<>();
+    
+    @Override
+    protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+        Map<String, Func> funcMap = new HashMap<>();
+        //......
+        funcMap.put("put_result", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+            (argId, addr, len) -> {
+                ByteBuffer buf = supplier.get();
+                byte[] bytes = new byte[len];
+                for (int i = 0; i < len; i++) {
+                    bytes[i] = buf.get(addr.intValue() + i);
+                }
+                String result = new String(bytes, StandardCharsets.UTF_8);
+                assertEquals("rust result", result);
+                RESULTS.put(argId, result);
+                System.out.println("java side->" + result);
+                return 0;
+            }));
+        //......
+        return funcMap;
+    }
+}
+```
+
+#### 2.Import method in another language
+
+```rust
+#[link(wasm_import_module = "dubbo")]
+extern "C" {
+    fn put_result(arg_id: i64, addr: i64, len: i32) -> i32;
+}
+```
+
+#### 3.Pass result in another language
+
+```rust
+#[no_mangle]
+pub unsafe extern "C" fn doSelect(arg_id: i64) -> i32 {
+    //......
+    let rust_result = "rust result".as_bytes();
+    let result_ptr = rust_result.as_ptr() as i64;
+    _ = put_result(arg_id, result_ptr, rust_result.len() as i32);
+    //......
+}
+```
+
+#### 4.Get result in Java
+
+```java
+public class RustLoadBalance extends AbstractWasmLoadBalance {
+    //......
+    
+    private static final Map<Long, String> RESULTS = new ConcurrentHashMap<>();
+
+    @Override
+    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
+        return wasmLoader.getWasmExtern(DO_SELECT_METHOD_NAME)
+                .map(extern -> {
+                    // call WASI function
+                    final Long argumentId = getArgumentId(invokers, url, invocation);
+                    ARGUMENTS.put(argumentId, new Argument<>(invokers, url, invocation));
+                    // WASI cannot easily pass Java objects like JNI, here we pass Long
+                    // then we can get the argument by Long
+                    final Integer index = WasmFunctions.func(wasmLoader.getStore(), extern.func(), WasmValType.I64, WasmValType.I32)
+                            .call(argumentId);
+                    ARGUMENTS.remove(argumentId);
+                    
+                    // For demonstration purposes, the doSelect method has been overwritten
+                    final String result = RESULTS.get(argumentId);
+                    assertEquals("rust result", result);
+                    return invokers.get(index);
+                })
+                .orElseThrow(() -> new DubboWasmException(
+                        DO_SELECT_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+}
+```
diff --git a/dubbo-wasm/README_zh.md b/dubbo-wasm/README_zh.md
new file mode 100644
index 0000000..e6b22de
--- /dev/null
+++ b/dubbo-wasm/README_zh.md
@@ -0,0 +1,247 @@
+# dubbo WASM api
+
+[English](./README.md)
+
+目前,Dubbo的SPI扩展只能使用Java语言编写,dubbo-wasm模块旨在克服这一限制。
+
+WASM(WebAssembly)字节码设计为以大小和高效加载时间的二进制格式编码。WASM旨在利用各种平台上可用的通用硬件特性,在浏览器中以机器码速度执行。
+
+WASI(WebAssembly系统接口)为在受限沙箱环境中运行的应用程序提供了一个可移植接口,这使得WASM可以在非浏览器环境(如Linux)中运行。它具有可移植性和安全性。
+
+我们使用 [wasmtime-java](https://github.com/kawamuray/wasmtime-java) 在Java中运行WASI,只要语言可以编译成WASM字节码,就可以用于编写dubbo的SPI扩展。
+
+## 模块结构
+
+* dubbo-wasm-api:负责定义基本异常并提供执行WASM字节码的运行时(Dubbo API)
+* dubbo-wasm-cluster-api:提供dubbo-cluster模块SPI的WASM版本
+* dubbo-wasm-common-api:提供dubbo-common模块SPI的WASM版本
+* dubbo-wasm-metrics-api(待办):提供dubbo-metrics模块SPI的WASM版本
+* dubbo-wasm-registry-api:提供dubbo-registry-api模块SPI的WASM版本
+* dubbo-wasm-remoting-api(待办):提供dubbo-remoting-api模块SPI的WASM版本
+* dubbo-wasm-rpc-api:提供dubbo-rpc-api模块SPI的WASM版本
+
+## 注意事项
+
+由于WASM对参数类型有严格要求,并且为了简单起见,除了 `java.lang.Long/java.lang.Integer` 之外的类型不用作参数或返回值。
+
+## 如何使用
+
+以下是在rust中实现 `org.apache.dubbo.rpc.cluster.LoadBalance` SPI的示例。
+
+
+### 1.用其他语言创建子项目
+
+```shell
+cargo new --lib your_subproject_name
+```
+
+### 2.在lib.rs中添加方法
+
+```rust
+// Adding `#[no_mangle]` to prevent the Rust compiler from modifying method names is mandatory
+#[no_mangle]
+pub unsafe extern "C" fn doSelect(_arg_id: i64) -> i32 {
+    1
+}
+```
+
+###  3.在 `Cargo.toml` 中添加 `[lib]` 并将 `crate-type` 更改为 `["cdylib"]`。最终,你的 `Cargo.toml` 应如下所示:
+
+
+```toml
+[package]
+name = "your_subproject_name"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+# ......
+
+[lib]
+crate-type = ["cdylib"]
+```
+
+### 4.生成 wasm 文件
+
+```shell
+cargo build --target wasm32-wasi --release
+```
+
+你可以看到 `{your_subproject_name}/target/wasm32-wasi/release/{your_subproject_name}.wasm`.
+
+### 5.添加依赖到 `pom.xml`
+
+```xml
+<dependency>
+    <groupId>org.apache.dubbo.extensions</groupId>
+    <artifactId>dubbo-wasm-cluster-api</artifactId>
+    <version>${revision}</version>
+</dependency>
+```
+
+### 6.添加 `x.y.z.RustLoadBalance.java`
+
+```java
+package x.y.z;
+
+public class RustLoadBalance extends AbstractWasmLoadBalance {
+    public static final String NAME = "rust";
+}
+```
+
+### 7.添加 `resources/META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance`
+
+```text
+rust=x.y.z.RustLoadBalance
+```
+
+### 8.重命名 WASM 文件
+
+由于类 `x.y.z.RustLoadBalance.java`,最终的wasm文件名应该是 `x.y.z.RustLoadBalance.wasm`,最后将 wasm 文件放在你模块的 `resources` 文件夹中。
+
+## 与Java通信
+
+以下是使用rust的示例。
+
+### 将参数传递给另一种语言
+
+#### 1.将方法导出到另一种语言
+
+```java
+public class RustLoadBalance extends AbstractWasmLoadBalance {
+    //......
+    @Override
+    protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+        Map<String, Func> funcMap = new HashMap<>();
+        //......
+        funcMap.put("get_args", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32, 
+            (argId, addr, len) -> {
+                String config = "hello from java " + argId;
+                System.out.println("java side->" + config);
+                assertEquals("hello from java 0", config);
+                ByteBuffer buf = supplier.get();
+                for (int i = 0; i < len && i < config.length(); i++) {
+                    buf.put(addr.intValue() + i, (byte) config.charAt(i));
+                }
+                return Math.min(config.length(), len);
+            }));
+        //......
+        return funcMap;
+    }
+}
+```
+
+#### 2.在另一种语言中导入方法
+
+```rust
+#[link(wasm_import_module = "dubbo")]
+extern "C" {
+    fn get_args(arg_id: i64, addr: i64, len: i32) -> i32;
+}
+```
+
+#### 3.在另一种语言中获取参数
+
+```rust
+#[no_mangle]
+pub unsafe extern "C" fn doSelect(arg_id: i64) -> i32 {
+    //......
+    let mut buf = [0u8; 32];
+    let buf_ptr = buf.as_mut_ptr() as i64;
+    eprintln!("rust side-> buffer base address: {}", buf_ptr);
+    // get arg from java
+    let len = get_args(arg_id, buf_ptr, buf.len() as i32);
+    let java_arg = std::str::from_utf8(&buf[..len as usize]).unwrap();
+    eprintln!("rust side-> recv:{}", java_arg);
+    //......
+}
+```
+
+### 将结果传递给Java
+
+以下是使用rust的示例。
+
+#### 1.将方法导出到另一种语言
+
+```java
+public class RustLoadBalance extends AbstractWasmLoadBalance {
+    //......
+    
+    private static final Map<Long, String> RESULTS = new ConcurrentHashMap<>();
+    
+    @Override
+    protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+        Map<String, Func> funcMap = new HashMap<>();
+        //......
+        funcMap.put("put_result", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+            (argId, addr, len) -> {
+                ByteBuffer buf = supplier.get();
+                byte[] bytes = new byte[len];
+                for (int i = 0; i < len; i++) {
+                    bytes[i] = buf.get(addr.intValue() + i);
+                }
+                String result = new String(bytes, StandardCharsets.UTF_8);
+                assertEquals("rust result", result);
+                RESULTS.put(argId, result);
+                System.out.println("java side->" + result);
+                return 0;
+            }));
+        //......
+        return funcMap;
+    }
+}
+```
+
+#### 2.在另一种语言中导入方法
+
+```rust
+#[link(wasm_import_module = "dubbo")]
+extern "C" {
+    fn put_result(arg_id: i64, addr: i64, len: i32) -> i32;
+}
+```
+
+#### 3.在另一种语言中传递结果
+
+```rust
+#[no_mangle]
+pub unsafe extern "C" fn doSelect(arg_id: i64) -> i32 {
+    //......
+    let rust_result = "rust result".as_bytes();
+    let result_ptr = rust_result.as_ptr() as i64;
+    _ = put_result(arg_id, result_ptr, rust_result.len() as i32);
+    //......
+}
+```
+
+#### 4.在Java中获取结果
+
+```java
+public class RustLoadBalance extends AbstractWasmLoadBalance {
+    //......
+    
+    private static final Map<Long, String> RESULTS = new ConcurrentHashMap<>();
+
+    @Override
+    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
+        return wasmLoader.getWasmExtern(DO_SELECT_METHOD_NAME)
+                .map(extern -> {
+                    // call WASI function
+                    final Long argumentId = getArgumentId(invokers, url, invocation);
+                    ARGUMENTS.put(argumentId, new Argument<>(invokers, url, invocation));
+                    // WASI cannot easily pass Java objects like JNI, here we pass Long
+                    // then we can get the argument by Long
+                    final Integer index = WasmFunctions.func(wasmLoader.getStore(), extern.func(), WasmValType.I64, WasmValType.I32)
+                            .call(argumentId);
+                    ARGUMENTS.remove(argumentId);
+                    
+                    // For demonstration purposes, the doSelect method has been overwritten
+                    final String result = RESULTS.get(argumentId);
+                    assertEquals("rust result", result);
+                    return invokers.get(index);
+                })
+                .orElseThrow(() -> new DubboWasmException(
+                        DO_SELECT_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+}
+```
diff --git a/dubbo-wasm/dubbo-wasm-api/pom.xml b/dubbo-wasm/dubbo-wasm-api/pom.xml
new file mode 100644
index 0000000..e871812
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-api/pom.xml
@@ -0,0 +1,39 @@
+<?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">
+    <parent>
+        <artifactId>dubbo-wasm</artifactId>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-wasm-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.github.kawamuray.wasmtime</groupId>
+            <artifactId>wasmtime-java</artifactId>
+            <version>0.18.0</version>
+        </dependency>
+    </dependencies>
+
+
+</project>
diff --git a/dubbo-wasm/dubbo-wasm-api/src/main/java/org/apache/dubbo/wasm/WasmLoader.java b/dubbo-wasm/dubbo-wasm-api/src/main/java/org/apache/dubbo/wasm/WasmLoader.java
new file mode 100644
index 0000000..d163de0
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-api/src/main/java/org/apache/dubbo/wasm/WasmLoader.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm;
+
+import org.apache.dubbo.wasm.exception.DubboWasmInitException;
+
+import io.github.kawamuray.wasmtime.Extern;
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Linker;
+import io.github.kawamuray.wasmtime.Memory;
+import io.github.kawamuray.wasmtime.Module;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.wasi.WasiCtx;
+import io.github.kawamuray.wasmtime.wasi.WasiCtxBuilder;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * The WasmLoader aims to load wasm file and provide the wasm function to java,
+ * also provide the java function to wasm if we need.
+ *
+ * @see <a href="https://github.com/apache/shenyu/pull/5412">WasmLoader</a>
+ */
+public class WasmLoader implements AutoCloseable {
+
+    private static final String IMPORT_WASM_MODULE_NAME = "dubbo";
+
+    private static final String MEMORY_METHOD_NAME = "memory";
+
+    private final AtomicBoolean closed = new AtomicBoolean(false);
+
+    private final String wasmName;
+
+    private final WasiCtx wasiCtx = new WasiCtxBuilder().inheritStdout().inheritStderr().build();
+
+    /**
+     * the WASM store.
+     *
+     * @see io.github.kawamuray.wasmtime.WasmFunctions#consumer
+     * @see io.github.kawamuray.wasmtime.WasmFunctions#func
+     */
+    private final Store<Void> store = Store.withoutData(wasiCtx);
+
+    private final Linker linker = new Linker(store.engine());
+
+    /**
+     * wasmCallJavaFuncName -> wasmCallJavaFunc.
+     */
+    private final Map<String, Func> wasmCallJavaFuncMap = new HashMap<>();
+
+    private final Module module;
+
+    private final AtomicReference<Memory> memRef = new AtomicReference<>();
+
+    public WasmLoader() {
+        this(null, null, null);
+    }
+
+    /**
+     * This constructor is designed for classes that cannot extend WasmLoader.
+     *
+     * @see io.github.kawamuray.wasmtime.WasmFunctions#wrap
+     */
+    public WasmLoader(final Class<?> wasmClass,
+                      final Function<Class<?>, String> nameInitializer,
+                      final BiFunction<Store<Void>, Supplier<ByteBuffer>, Map<String, Func>> initializer) {
+        final Class<?> clazz = Objects.nonNull(wasmClass) ? wasmClass : this.getClass();
+        String wasmName = null;
+        try {
+            if (Objects.nonNull(nameInitializer)) {
+                wasmName = nameInitializer.apply(clazz);
+            }
+            if (Objects.isNull(wasmName) || wasmName.isEmpty()) {
+                wasmName = buildWasmName(clazz);
+            }
+            if (Objects.isNull(wasmName) || wasmName.isEmpty()) {
+                throw new DubboWasmInitException("Illegal WASM file name");
+            }
+            this.wasmName = wasmName.endsWith(".wasm") ? wasmName : wasmName + ".wasm";
+            // locate `.wasm` lib.
+            URL resource = clazz.getClassLoader().getResource(wasmName);
+            if (Objects.isNull(resource)) {
+                throw new DubboWasmInitException("Can't find wasm file: " + wasmName);
+            }
+            // Reads the WebAssembly module as bytes.
+            byte[] wasmBytes = Files.readAllBytes(Paths.get(resource.toURI()));
+            // Instantiates the WebAssembly module.
+            if (Objects.nonNull(initializer)) {
+                Map<String, Func> wasmFunctionMap = initializer.apply(store, this::getBuffer);
+                if (Objects.nonNull(wasmFunctionMap) && !wasmFunctionMap.isEmpty()) {
+                    wasmCallJavaFuncMap.putAll(wasmFunctionMap);
+                }
+            }
+            Map<String, Func> wasmFunctionMap = initWasmCallJavaFunc(store, this::getBuffer);
+            if (Objects.nonNull(wasmFunctionMap) && !wasmFunctionMap.isEmpty()) {
+                wasmCallJavaFuncMap.putAll(wasmFunctionMap);
+            }
+            this.module = Module.fromBinary(store.engine(), wasmBytes);
+            WasiCtx.addToLinker(linker);
+            // maybe need define many functions
+            if (!wasmCallJavaFuncMap.isEmpty()) {
+                wasmCallJavaFuncMap.forEach((funcName, wasmCallJavaFunc) ->
+                    linker.define(store, IMPORT_WASM_MODULE_NAME, funcName, Extern.fromFunc(wasmCallJavaFunc)));
+            }
+            linker.module(store, "", module);
+            // Let the `wasmCallJavaFunc` function to refer this as a placeholder of Memory because
+            // we have to add the function as import before loading the module exporting Memory.
+            Optional<Extern> extern = this.getWasmExtern(MEMORY_METHOD_NAME);
+            if (!extern.isPresent()) {
+                throw new DubboWasmInitException(MEMORY_METHOD_NAME + " function not find in wasm file: " + wasmName);
+            }
+            this.memRef.set(extern.get().memory());
+            Runtime.getRuntime().addShutdownHook(new Thread(this::close));
+        } catch (URISyntaxException | IOException e) {
+            throw new DubboWasmInitException(e);
+        }
+    }
+
+    private ByteBuffer getBuffer() {
+        return memRef.get().buffer(store);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> supplier) {
+        return null;
+    }
+
+    /**
+     * get the WASI function.
+     *
+     * @param wasiFuncName the WASI function name
+     * @return the WASI function
+     */
+    public Optional<Extern> getWasmExtern(final String wasiFuncName) {
+        return linker.get(store, "", wasiFuncName);
+    }
+
+    /**
+     * get the wasm file name.
+     *
+     * @return wasm file name
+     */
+    public String getWasmName() {
+        return wasmName;
+    }
+
+    /**
+     * use this when call WASI.
+     *
+     * @return the Store
+     */
+    public Store<Void> getStore() {
+        return store;
+    }
+
+    @Override
+    public void close() {
+        if (this.closed.compareAndSet(false, true)) {
+            this.wasiCtx.close();
+            this.store.close();
+            this.linker.close();
+            if (!wasmCallJavaFuncMap.isEmpty()) {
+                this.wasmCallJavaFuncMap.forEach((funcName, wasmCallJavaFunc) -> wasmCallJavaFunc.close());
+            }
+            this.module.close();
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-api/src/main/java/org/apache/dubbo/wasm/exception/DubboWasmException.java b/dubbo-wasm/dubbo-wasm-api/src/main/java/org/apache/dubbo/wasm/exception/DubboWasmException.java
new file mode 100644
index 0000000..d4b9fe0
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-api/src/main/java/org/apache/dubbo/wasm/exception/DubboWasmException.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.exception;
+
+/**
+ * dubbo WASM Exception.
+ */
+public class DubboWasmException extends RuntimeException {
+
+    private static final long serialVersionUID = 5925652370728356835L;
+
+    /**
+     * Instantiates a new dubbo WASM exception.
+     *
+     * @param e the e
+     */
+    public DubboWasmException(final Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Instantiates a new dubbo WASM exception.
+     *
+     * @param message the message
+     */
+    public DubboWasmException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new dubbo WASM exception.
+     *
+     * @param message   the message
+     * @param throwable the throwable
+     */
+    public DubboWasmException(final String message, final Throwable throwable) {
+        super(message, throwable);
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-api/src/main/java/org/apache/dubbo/wasm/exception/DubboWasmInitException.java b/dubbo-wasm/dubbo-wasm-api/src/main/java/org/apache/dubbo/wasm/exception/DubboWasmInitException.java
new file mode 100644
index 0000000..008bf41
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-api/src/main/java/org/apache/dubbo/wasm/exception/DubboWasmInitException.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.exception;
+
+/**
+ * dubbo WASM init Exception.
+ */
+public class DubboWasmInitException extends DubboWasmException {
+
+    private static final long serialVersionUID = -325000764263035159L;
+
+    /**
+     * Instantiates a new dubbo WASM exception.
+     *
+     * @param e the e
+     */
+    public DubboWasmInitException(final Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Instantiates a new dubbo WASM exception.
+     *
+     * @param message the message
+     */
+    public DubboWasmInitException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new dubbo WASM exception.
+     *
+     * @param message   the message
+     * @param throwable the throwable
+     */
+    public DubboWasmInitException(final String message, final Throwable throwable) {
+        super(message, throwable);
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-cluster-api/pom.xml b/dubbo-wasm/dubbo-wasm-cluster-api/pom.xml
new file mode 100644
index 0000000..908e32d
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-cluster-api/pom.xml
@@ -0,0 +1,62 @@
+<?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">
+    <parent>
+        <artifactId>dubbo-wasm</artifactId>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-wasm-cluster-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-cluster</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>../dubbo-wasm-test/src/main/resources</directory>
+                <includes>
+                    <include>rust_extensions.wasm</include>
+                </includes>
+            </resource>
+        </resources>
+    </build>
+</project>
diff --git a/dubbo-wasm/dubbo-wasm-cluster-api/src/main/java/org/apache/dubbo/wasm/cluster/loadbalance/AbstractWasmLoadBalance.java b/dubbo-wasm/dubbo-wasm-cluster-api/src/main/java/org/apache/dubbo/wasm/cluster/loadbalance/AbstractWasmLoadBalance.java
new file mode 100644
index 0000000..1517bfb
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-cluster-api/src/main/java/org/apache/dubbo/wasm/cluster/loadbalance/AbstractWasmLoadBalance.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.cluster.loadbalance;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance;
+import org.apache.dubbo.wasm.WasmLoader;
+import org.apache.dubbo.wasm.exception.DubboWasmException;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+/**
+ * LoadBalancers implemented in other languages should extend this class, we still need to write Java subclasses,
+ * so we can reuse the convenient/powerful control of dubbo, such as {@link org.apache.dubbo.common.extension.Activate}.
+ *
+ * @see WasmLoader
+ */
+public abstract class AbstractWasmLoadBalance extends AbstractLoadBalance {
+
+    private static final String DO_SELECT_METHOD_NAME = "doSelect";
+
+    protected static final Map<Long, Argument<?>> ARGUMENTS = new ConcurrentHashMap<>();
+
+    private final WasmLoader wasmLoader;
+
+    public AbstractWasmLoadBalance() {
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> supplier) {
+        return null;
+    }
+
+    @Override
+    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
+        return wasmLoader.getWasmExtern(DO_SELECT_METHOD_NAME)
+            .map(extern -> {
+                // call WASI function
+                final Long argumentId = getArgumentId(invokers, url, invocation);
+                ARGUMENTS.put(argumentId, new Argument<>(invokers, url, invocation));
+                // WASI cannot easily pass Java objects like JNI, here we pass Long
+                // then we can get the argument by Long
+                final Integer index = WasmFunctions.func(wasmLoader.getStore(), extern.func(), WasmValType.I64, WasmValType.I32)
+                    .call(argumentId);
+                ARGUMENTS.remove(argumentId);
+                return invokers.get(index);
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_SELECT_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected <T> Long getArgumentId(List<Invoker<T>> invokers, URL url, Invocation invocation) {
+        return 0L;
+    }
+
+    protected static class Argument<T> {
+        protected List<Invoker<T>> invokers;
+        protected URL url;
+        protected Invocation invocation;
+
+        private Argument(List<Invoker<T>> invokers, URL url, Invocation invocation) {
+            this.invokers = invokers;
+            this.url = url;
+            this.invocation = invocation;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-cluster-api/src/main/java/org/apache/dubbo/wasm/cluster/router/AbstractWasmRouter.java b/dubbo-wasm/dubbo-wasm-cluster-api/src/main/java/org/apache/dubbo/wasm/cluster/router/AbstractWasmRouter.java
new file mode 100644
index 0000000..f4f2af1
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-cluster-api/src/main/java/org/apache/dubbo/wasm/cluster/router/AbstractWasmRouter.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.cluster.router;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.cluster.router.AbstractRouter;
+import org.apache.dubbo.rpc.cluster.router.RouterResult;
+import org.apache.dubbo.wasm.WasmLoader;
+import org.apache.dubbo.wasm.exception.DubboWasmException;
+
+import io.github.kawamuray.wasmtime.Extern;
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+/**
+ * Routers implemented in other languages should extend this class, we still need to write Java subclasses,
+ * so we can reuse the convenient/powerful control of dubbo, such as {@link org.apache.dubbo.common.extension.Activate}.
+ *
+ * @see WasmLoader
+ */
+public abstract class AbstractWasmRouter extends AbstractRouter {
+
+    private static final String NOTIFY_METHOD_NAME = "notify";
+
+    private static final String ROUTE_METHOD_NAME = "route";
+
+    private static final String STOP_METHOD_NAME = "stop";
+
+    protected static final Map<Long, Argument<?>> ARGUMENTS = new ConcurrentHashMap<>();
+
+    private final WasmLoader wasmLoader;
+
+    public AbstractWasmRouter() {
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> supplier) {
+        return null;
+    }
+
+    @Override
+    public <T> void notify(List<Invoker<T>> invokers) {
+        wasmLoader.getWasmExtern(NOTIFY_METHOD_NAME).ifPresent(notify ->
+            callWASI(invokers, null, null, false, notify));
+    }
+
+    @Override
+    public <T> RouterResult<Invoker<T>> route(List<Invoker<T>> invokers,
+                                              URL url,
+                                              Invocation invocation,
+                                              boolean needToPrintMessage) throws RpcException {
+        return wasmLoader.getWasmExtern(ROUTE_METHOD_NAME)
+            .map(route -> {
+                final Long argumentId = callWASI(invokers, url, invocation, needToPrintMessage, route);
+                return doRoute(invokers, url, invocation, needToPrintMessage, argumentId);
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                ROUTE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract <T> RouterResult<Invoker<T>> doRoute(List<Invoker<T>> invokers,
+                                                            URL url,
+                                                            Invocation invocation,
+                                                            boolean needToPrintMessage,
+                                                            Long argumentId);
+
+    private <T> Long callWASI(final List<Invoker<T>> invokers,
+                              final URL url,
+                              final Invocation invocation,
+                              boolean needToPrintMessage,
+                              final Extern doExecute) {
+        // WASI cannot easily pass Java objects like JNI, here we pass Long as arg
+        // then we can get the argument by Long
+        final Long argumentId = getArgumentId(invokers, url, invocation, needToPrintMessage);
+        ARGUMENTS.put(argumentId, new Argument<>(invokers, url, invocation, needToPrintMessage));
+        // call WASI function
+        WasmFunctions.consumer(wasmLoader.getStore(), doExecute.func(), WasmValType.I64)
+            .accept(argumentId);
+        ARGUMENTS.remove(argumentId);
+        return argumentId;
+    }
+
+    protected abstract <T> Long getArgumentId(List<Invoker<T>> invokers, URL url, Invocation invocation, boolean needToPrintMessage);
+
+    @Override
+    public void stop() {
+        wasmLoader.getWasmExtern(STOP_METHOD_NAME).ifPresent(stop ->
+            WasmFunctions.consumer(wasmLoader.getStore(), stop.func()).accept());
+    }
+
+    protected static class Argument<T> {
+        protected List<Invoker<T>> invokers;
+        protected URL url;
+        protected Invocation invocation;
+        protected boolean needToPrintMessage;
+
+        private Argument(List<Invoker<T>> invokers, URL url, Invocation invocation, boolean needToPrintMessage) {
+            this.invokers = invokers;
+            this.url = url;
+            this.invocation = invocation;
+            this.needToPrintMessage = needToPrintMessage;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-cluster-api/src/test/java/org/apache/dubbo/wasm/cluster/loadbalance/AbstractWasmLoadBalanceTest.java b/dubbo-wasm/dubbo-wasm-cluster-api/src/test/java/org/apache/dubbo/wasm/cluster/loadbalance/AbstractWasmLoadBalanceTest.java
new file mode 100644
index 0000000..5928926
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-cluster-api/src/test/java/org/apache/dubbo/wasm/cluster/loadbalance/AbstractWasmLoadBalanceTest.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.cluster.loadbalance;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.wasm.cluster.loadbalance.AbstractWasmLoadBalance;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmLoadBalanceTest {
+
+    @Test
+    void test() {
+        MyInvoker myInvoker = new MyInvoker();
+        final List<Invoker<Object>> invokers = new ArrayList<>();
+        invokers.add(new MyInvoker());
+        invokers.add(myInvoker);
+
+        final RustLoadBalance balance = new RustLoadBalance();
+        final Invoker<Object> selected = balance.doSelect(invokers, null, null);
+        assertEquals(myInvoker, selected);
+    }
+
+    static class RustLoadBalance extends AbstractWasmLoadBalance {
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+
+        @Override
+        protected <T> Long getArgumentId(List<Invoker<T>> invokers, URL url, Invocation invocation) {
+            return 1L;
+        }
+    }
+
+    static class MyInvoker implements Invoker<Object> {
+        @Override
+        public URL getUrl() {
+            return null;
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return false;
+        }
+
+        @Override
+        public void destroy() {
+        }
+
+        @Override
+        public Class<Object> getInterface() {
+            return null;
+        }
+
+        @Override
+        public Result invoke(Invocation invocation) throws RpcException {
+            return null;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-cluster-api/src/test/java/org/apache/dubbo/wasm/cluster/router/AbstractWasmRouterTest.java b/dubbo-wasm/dubbo-wasm-cluster-api/src/test/java/org/apache/dubbo/wasm/cluster/router/AbstractWasmRouterTest.java
new file mode 100644
index 0000000..69e5152
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-cluster-api/src/test/java/org/apache/dubbo/wasm/cluster/router/AbstractWasmRouterTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.cluster.router;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.cluster.router.RouterResult;
+import org.apache.dubbo.wasm.cluster.router.AbstractWasmRouter;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmRouterTest {
+
+    @Test
+    void test() {
+        RustRouter router = new RustRouter();
+        RouterResult<Invoker<Object>> result = router.route(null, null, null, false);
+        Assertions.assertNull(result.getResult());
+    }
+
+    static class RustRouter extends AbstractWasmRouter {
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+
+        @Override
+        protected <T> RouterResult<Invoker<T>> doRoute(List<Invoker<T>> invokers, URL url, Invocation invocation, boolean needToPrintMessage, Long argumentId) {
+            final String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return new RouterResult<>(invokers);
+        }
+
+        @Override
+        protected <T> Long getArgumentId(List<Invoker<T>> invokers, URL url, Invocation invocation, boolean needToPrintMessage) {
+            return 2L;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-common-api/pom.xml b/dubbo-wasm/dubbo-wasm-common-api/pom.xml
new file mode 100644
index 0000000..18f1e6b
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-common-api/pom.xml
@@ -0,0 +1,62 @@
+<?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">
+    <parent>
+        <artifactId>dubbo-wasm</artifactId>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-wasm-common-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-common</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>../dubbo-wasm-test/src/main/resources</directory>
+                <includes>
+                    <include>rust_extensions.wasm</include>
+                </includes>
+            </resource>
+        </resources>
+    </build>
+</project>
diff --git a/dubbo-wasm/dubbo-wasm-common-api/src/main/java/org/apache/dubbo/wasm/common/config/configcenter/AbstractWasmDynamicConfiguration.java b/dubbo-wasm/dubbo-wasm-common-api/src/main/java/org/apache/dubbo/wasm/common/config/configcenter/AbstractWasmDynamicConfiguration.java
new file mode 100644
index 0000000..46d4868
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-common-api/src/main/java/org/apache/dubbo/wasm/common/config/configcenter/AbstractWasmDynamicConfiguration.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.common.config.configcenter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.common.config.configcenter.TreePathDynamicConfiguration;
+import org.apache.dubbo.wasm.WasmLoader;
+import org.apache.dubbo.wasm.exception.DubboWasmException;
+
+import io.github.kawamuray.wasmtime.Extern;
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+/**
+ * DynamicConfigurations implemented in other languages should extend this class, we still need to write Java subclasses,
+ * so we can reuse the convenient/powerful control of dubbo, such as {@link org.apache.dubbo.common.extension.Activate}.
+ *
+ * @see WasmLoader
+ */
+public abstract class AbstractWasmDynamicConfiguration extends TreePathDynamicConfiguration {
+
+    private static final String GET_INTERNAL_PROPERTY_SELECT_METHOD_NAME = "getInternalProperty";
+
+    private static final String DO_PUBLISH_CONFIG_METHOD_NAME = "doPublishConfig";
+
+    private static final String DO_GET_CONFIG_METHOD_NAME = "doGetConfig";
+
+    private static final String DO_REMOVE_CONFIG_METHOD_NAME = "doRemoveConfig";
+
+    private static final String DO_GET_CONFIG_KEYS_METHOD_NAME = "doGetConfigKeys";
+
+    private static final String DO_GET_CONFIG_KEY_ITEM_METHOD_NAME = "doGetConfigKeyItem";
+
+    private static final String DO_ADD_LISTENER_METHOD_NAME = "doAddListener";
+
+    private static final String DO_REMOVE_LISTENER_METHOD_NAME = "doRemoveListener";
+
+    private static final String DO_CLOSE_METHOD_NAME = "doClose";
+
+    private static final String PUBLISH_CONFIG_CAS_METHOD_NAME = "publishConfigCas";
+
+    protected static final Map<Long, String> GET_INTERNAL_PROPERTY_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, DoPublishConfigArgument> DO_PUBLISH_CONFIG_ARGUMENT_MAP = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, String> DO_GET_CONFIG_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, String> DO_REMOVE_CONFIG_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, String> DO_GET_CONFIG_KEYS_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, DoAddListenerArgument> DO_ADD_LISTENER_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, DoRemoveListenerArgument> DO_REMOVE_LISTENER_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, PublishConfigCasArgument> PUBLISH_CONFIG_CAS_ARGUMENTS = new ConcurrentHashMap<>();
+
+    private final WasmLoader wasmLoader;
+
+    public AbstractWasmDynamicConfiguration(URL url) {
+        super(url);
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> supplier) {
+        return null;
+    }
+
+    @Override
+    public Object getInternalProperty(String key) {
+        return wasmLoader.getWasmExtern(GET_INTERNAL_PROPERTY_SELECT_METHOD_NAME)
+            .map(extern -> {
+                final Long argumentId = getInternalPropertyArgumentId(key);
+                GET_INTERNAL_PROPERTY_ARGUMENTS.put(argumentId, key);
+                WasmFunctions.consumer(wasmLoader.getStore(), extern.func(), WasmValType.I64).accept(argumentId);
+                GET_INTERNAL_PROPERTY_ARGUMENTS.remove(argumentId);
+                return getInternalProperty(key, argumentId);
+            })
+            .orElse(null);
+    }
+
+    protected Long getInternalPropertyArgumentId(String key) {
+        return 0L;
+    }
+
+    protected Object getInternalProperty(String key, Long argumentId) {
+        return null;
+    }
+
+    @Override
+    protected boolean doPublishConfig(String pathKey, String content) throws Exception {
+        return wasmLoader.getWasmExtern(DO_PUBLISH_CONFIG_METHOD_NAME)
+            .map(extern -> {
+                final Long argumentId = doPublishConfigArgumentId(pathKey, content);
+                DO_PUBLISH_CONFIG_ARGUMENT_MAP.put(argumentId, new DoPublishConfigArgument(pathKey, content));
+                Integer result = WasmFunctions.func(wasmLoader.getStore(), extern.func(), WasmValType.I64, WasmValType.I32)
+                    .call(argumentId);
+                DO_PUBLISH_CONFIG_ARGUMENT_MAP.remove(argumentId);
+                return result != 0;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_PUBLISH_CONFIG_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract Long doPublishConfigArgumentId(String pathKey, String content);
+
+    @Override
+    protected String doGetConfig(String pathKey) throws Exception {
+        Optional<Extern> func = wasmLoader.getWasmExtern(DO_GET_CONFIG_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(DO_GET_CONFIG_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = doGetConfigArgumentId(pathKey);
+        DO_GET_CONFIG_ARGUMENTS.put(argumentId, pathKey);
+        WasmFunctions.consumer(wasmLoader.getStore(), func.get().func(), WasmValType.I64).accept(argumentId);
+        DO_GET_CONFIG_ARGUMENTS.remove(argumentId);
+        return doGetConfig(pathKey, argumentId);
+    }
+
+    protected abstract Long doGetConfigArgumentId(String key);
+
+    protected abstract String doGetConfig(String pathKey, Long argumentId) throws Exception;
+
+    @Override
+    protected boolean doRemoveConfig(String pathKey) throws Exception {
+        Optional<Extern> func = wasmLoader.getWasmExtern(DO_REMOVE_CONFIG_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(DO_REMOVE_CONFIG_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = doRemoveConfigArgumentId(pathKey);
+        DO_REMOVE_CONFIG_ARGUMENTS.put(argumentId, pathKey);
+        Integer result = WasmFunctions.func(wasmLoader.getStore(), func.get().func(), WasmValType.I64, WasmValType.I32)
+            .call(argumentId);
+        DO_REMOVE_CONFIG_ARGUMENTS.remove(argumentId);
+        return result != 0;
+    }
+
+    protected abstract Long doRemoveConfigArgumentId(String key);
+
+    @Override
+    protected Collection<String> doGetConfigKeys(String groupPath) {
+        Optional<Extern> func = wasmLoader.getWasmExtern(DO_GET_CONFIG_KEYS_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(DO_GET_CONFIG_KEYS_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = doGetConfigKeysArgumentId(groupPath);
+        DO_GET_CONFIG_KEYS_ARGUMENTS.put(argumentId, groupPath);
+        Integer count = WasmFunctions.func(wasmLoader.getStore(), func.get().func(), WasmValType.I64, WasmValType.I32)
+            .call(argumentId);
+        Optional<Extern> itemFunc = wasmLoader.getWasmExtern(DO_GET_CONFIG_KEY_ITEM_METHOD_NAME);
+        if (!itemFunc.isPresent()) {
+            throw new DubboWasmException(DO_GET_CONFIG_KEY_ITEM_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        Collection<String> keys = new ArrayList<>(count);
+        for (int i = 0; i < count; i++) {
+            WasmFunctions.consumer(wasmLoader.getStore(), itemFunc.get().func(), WasmValType.I64, WasmValType.I32)
+                .accept(argumentId, i);
+            keys.add(doGetConfigKeyItem(groupPath, argumentId));
+        }
+        DO_GET_CONFIG_KEYS_ARGUMENTS.remove(argumentId);
+        return keys;
+    }
+
+    protected abstract Long doGetConfigKeysArgumentId(String groupPath);
+
+    protected abstract String doGetConfigKeyItem(String groupPath, Long argumentId);
+
+    @Override
+    protected void doAddListener(String pathKey, ConfigurationListener listener, String key, String group) {
+        Optional<Extern> func = wasmLoader.getWasmExtern(DO_ADD_LISTENER_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(DO_ADD_LISTENER_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = doAddListenerArgumentId(pathKey, listener, key, group);
+        DO_ADD_LISTENER_ARGUMENTS.put(argumentId, new DoAddListenerArgument(pathKey, listener, key, group));
+        WasmFunctions.consumer(wasmLoader.getStore(), func.get().func(), WasmValType.I64).accept(argumentId);
+        DO_ADD_LISTENER_ARGUMENTS.remove(argumentId);
+        doAddListener(pathKey, listener, key, group, argumentId);
+    }
+
+    protected abstract Long doAddListenerArgumentId(String pathKey, ConfigurationListener listener, String key, String group);
+
+    protected abstract void doAddListener(String pathKey, ConfigurationListener listener, String key, String group, Long argumentId);
+
+    @Override
+    protected void doRemoveListener(String pathKey, ConfigurationListener listener) {
+        Optional<Extern> func = wasmLoader.getWasmExtern(DO_REMOVE_LISTENER_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(DO_REMOVE_LISTENER_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = doRemoveListenerArgumentId(pathKey, listener);
+        DO_REMOVE_LISTENER_ARGUMENTS.put(argumentId, new DoRemoveListenerArgument(pathKey, listener));
+        WasmFunctions.consumer(wasmLoader.getStore(), func.get().func(), WasmValType.I64).accept(argumentId);
+        DO_REMOVE_LISTENER_ARGUMENTS.remove(argumentId);
+        doRemoveListener(pathKey, listener, argumentId);
+    }
+
+    protected abstract Long doRemoveListenerArgumentId(String pathKey, ConfigurationListener listener);
+
+    protected abstract void doRemoveListener(String pathKey, ConfigurationListener listener, Long argumentId);
+
+    @Override
+    protected void doClose() throws Exception {
+        Optional<Extern> func = wasmLoader.getWasmExtern(DO_CLOSE_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(DO_CLOSE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        WasmFunctions.consumer(wasmLoader.getStore(), func.get().func()).accept();
+    }
+
+    @Override
+    public boolean publishConfigCas(String key, String group, String content, Object ticket) throws UnsupportedOperationException {
+        return wasmLoader.getWasmExtern(PUBLISH_CONFIG_CAS_METHOD_NAME)
+            .map(func -> {
+                final Long argumentId = publishConfigCasArgumentId(key, group, content, ticket);
+                PUBLISH_CONFIG_CAS_ARGUMENTS.put(argumentId, new PublishConfigCasArgument(key, group, content, ticket));
+                Integer result = WasmFunctions.func(wasmLoader.getStore(), func.func(), WasmValType.I64, WasmValType.I32)
+                    .call(argumentId);
+                PUBLISH_CONFIG_CAS_ARGUMENTS.remove(argumentId);
+                return result != 0;
+            })
+            .orElse(false);
+    }
+
+    protected Long publishConfigCasArgumentId(String key, String group, String content, Object ticket) {
+        return 0L;
+    }
+
+    protected static class DoPublishConfigArgument {
+        protected String pathKey;
+        protected String content;
+
+        private DoPublishConfigArgument(String pathKey, String content) {
+            this.pathKey = pathKey;
+            this.content = content;
+        }
+    }
+
+    protected static class DoAddListenerArgument {
+        protected String pathKey;
+        protected ConfigurationListener listener;
+        protected String key;
+        protected String group;
+
+        private DoAddListenerArgument(String pathKey, ConfigurationListener listener, String key, String group) {
+            this.pathKey = pathKey;
+            this.listener = listener;
+            this.key = key;
+            this.group = group;
+        }
+    }
+
+    protected static class DoRemoveListenerArgument {
+        protected String pathKey;
+        protected ConfigurationListener listener;
+
+        private DoRemoveListenerArgument(String pathKey, ConfigurationListener listener) {
+            this.pathKey = pathKey;
+            this.listener = listener;
+        }
+    }
+
+    protected static class PublishConfigCasArgument {
+        protected String key;
+        protected String group;
+        protected String content;
+        protected Object ticket;
+
+        private PublishConfigCasArgument(String key, String group, String content, Object ticket) {
+            this.key = key;
+            this.group = group;
+            this.content = content;
+            this.ticket = ticket;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-common-api/src/test/java/org/apache/dubbo/wasm/common/config/configcenter/AbstractWasmDynamicConfigurationTest.java b/dubbo-wasm/dubbo-wasm-common-api/src/test/java/org/apache/dubbo/wasm/common/config/configcenter/AbstractWasmDynamicConfigurationTest.java
new file mode 100644
index 0000000..99d865f
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-common-api/src/test/java/org/apache/dubbo/wasm/common/config/configcenter/AbstractWasmDynamicConfigurationTest.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.common.config.configcenter;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.config.configcenter.ConfigurationListener;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmDynamicConfigurationTest {
+
+    @Test
+    void test() {
+        try (RustDynamicConfiguration configuration = new RustDynamicConfiguration()) {
+            configuration.getInternalProperty("key");
+            configuration.publishConfig("pathKey", "content");
+            configuration.getConfig("key", "group");
+            configuration.removeConfig("key", "group");
+            configuration.doGetConfigKeys("groupPath");
+            configuration.addListener("key", null);
+            configuration.removeListener("key", null);
+            configuration.publishConfigCas("key", "group", "content", new Object());
+        } catch (Exception ignored) {
+        }
+    }
+
+    static class RustDynamicConfiguration extends AbstractWasmDynamicConfiguration {
+
+        public RustDynamicConfiguration() {
+            super(URL.valueOf("dubbo://127.0.0.1:12345?timeout=1234&default.timeout=5678"));
+        }
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+
+        @Override
+        protected Long getInternalPropertyArgumentId(String key) {
+            return 8L;
+        }
+
+        @Override
+        protected Object getInternalProperty(String key, Long argumentId) {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return result;
+        }
+
+        @Override
+        protected Long doPublishConfigArgumentId(String pathKey, String content) {
+            return 8L;
+        }
+
+        @Override
+        protected Long doGetConfigArgumentId(String key) {
+            return null;
+        }
+
+        @Override
+        protected String doGetConfig(String pathKey, Long argumentId) throws Exception {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return result;
+        }
+
+        @Override
+        protected Long doRemoveConfigArgumentId(String key) {
+            return 8L;
+        }
+
+        @Override
+        protected Long doGetConfigKeysArgumentId(String groupPath) {
+            return 8L;
+        }
+
+        @Override
+        protected String doGetConfigKeyItem(String groupPath, Long argumentId) {
+            final String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return result;
+        }
+
+        @Override
+        protected Long doAddListenerArgumentId(String pathKey, ConfigurationListener listener, String key, String group) {
+            return 8L;
+        }
+
+        @Override
+        protected void doAddListener(String pathKey, ConfigurationListener listener, String key, String group, Long argumentId) {
+            final String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected Long doRemoveListenerArgumentId(String pathKey, ConfigurationListener listener) {
+            return 8L;
+        }
+
+        @Override
+        protected void doRemoveListener(String pathKey, ConfigurationListener listener, Long argumentId) {
+            final String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected Long publishConfigCasArgumentId(String key, String group, String content, Object ticket) {
+            return 8L;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-registry-api/pom.xml b/dubbo-wasm/dubbo-wasm-registry-api/pom.xml
new file mode 100644
index 0000000..effddee
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-registry-api/pom.xml
@@ -0,0 +1,62 @@
+<?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">
+    <parent>
+        <artifactId>dubbo-wasm</artifactId>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-wasm-registry-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-registry-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>../dubbo-wasm-test/src/main/resources</directory>
+                <includes>
+                    <include>rust_extensions.wasm</include>
+                </includes>
+            </resource>
+        </resources>
+    </build>
+</project>
diff --git a/dubbo-wasm/dubbo-wasm-registry-api/src/main/java/org/apache/dubbo/wasm/registry/client/AbstractWasmServiceDiscovery.java b/dubbo-wasm/dubbo-wasm-registry-api/src/main/java/org/apache/dubbo/wasm/registry/client/AbstractWasmServiceDiscovery.java
new file mode 100644
index 0000000..98bacf4
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-registry-api/src/main/java/org/apache/dubbo/wasm/registry/client/AbstractWasmServiceDiscovery.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.registry.client;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.AbstractServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.wasm.WasmLoader;
+import org.apache.dubbo.wasm.exception.DubboWasmException;
+
+import io.github.kawamuray.wasmtime.Extern;
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+/**
+ * ServiceDiscoveries implemented in other languages should extend this class, we still need to write Java subclasses,
+ * so we can reuse the convenient/powerful control of dubbo, such as {@link org.apache.dubbo.common.extension.Activate}.
+ *
+ * @see WasmLoader
+ */
+public abstract class AbstractWasmServiceDiscovery extends AbstractServiceDiscovery {
+
+    private static final String DO_REGISTER_METHOD_NAME = "doRegister";
+
+    private static final String DO_UNREGISTER_METHOD_NAME = "doUnregister";
+
+    private static final String DO_DESTROY_METHOD_NAME = "doDestroy";
+
+    private static final String GET_SERVICES_METHOD_NAME = "getServices";
+
+    private static final String GET_INSTANCES_METHOD_NAME = "getInstances";
+
+    private static final String ADD_SERVICE_INSTANCES_CHANGED_LISTENER_METHOD_NAME = "addServiceInstancesChangedListener";
+
+    private static final String REMOVE_SERVICE_INSTANCES_CHANGED_LISTENER_METHOD_NAME = "removeServiceInstancesChangedListener";
+
+    protected static final Map<Long, ServiceInstance> REGISTER_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, String> GET_INSTANCES_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, ServiceInstancesChangedListener> SERVICE_INSTANCES_CHANGED_LISTENER_ARGUMENTS
+        = new ConcurrentHashMap<>();
+
+    private final WasmLoader wasmLoader;
+
+    public AbstractWasmServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
+        super(applicationModel, registryURL);
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    public AbstractWasmServiceDiscovery(String serviceName, URL registryURL) {
+        super(serviceName, registryURL);
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> supplier) {
+        return null;
+    }
+
+    @Override
+    protected void doRegister(ServiceInstance serviceInstance) throws RuntimeException {
+        wasmLoader.getWasmExtern(DO_REGISTER_METHOD_NAME)
+            .map(doRegister -> {
+                Long argumentId = callWASI(serviceInstance, doRegister);
+                doRegister(serviceInstance, argumentId);
+                return DO_REGISTER_METHOD_NAME;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_REGISTER_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract void doRegister(ServiceInstance serviceInstance, Long argumentId) throws RuntimeException;
+
+    @Override
+    protected void doUnregister(ServiceInstance serviceInstance) {
+        wasmLoader.getWasmExtern(DO_UNREGISTER_METHOD_NAME)
+            .map(doUnregister -> {
+                Long argumentId = callWASI(serviceInstance, doUnregister);
+                doUnregister(serviceInstance, argumentId);
+                return DO_UNREGISTER_METHOD_NAME;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_UNREGISTER_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract void doUnregister(ServiceInstance serviceInstance, Long argumentId);
+
+    private Long callWASI(final ServiceInstance serviceInstance,
+                          final Extern doExecute) {
+        // WASI cannot easily pass Java objects like JNI, here we pass Long as arg
+        // then we can get the argument by Long
+        final Long argumentId = getArgumentId(serviceInstance);
+        REGISTER_ARGUMENTS.put(argumentId, serviceInstance);
+        // call WASI function
+        WasmFunctions.consumer(wasmLoader.getStore(), doExecute.func(), WasmValType.I64)
+            .accept(argumentId);
+        REGISTER_ARGUMENTS.remove(argumentId);
+        return argumentId;
+    }
+
+    protected abstract Long getArgumentId(ServiceInstance serviceInstance);
+
+    @Override
+    protected void doDestroy() {
+        wasmLoader.getWasmExtern(DO_DESTROY_METHOD_NAME)
+            .map(doDestroy -> {
+                WasmFunctions.consumer(wasmLoader.getStore(), doDestroy.func()).accept();
+                return DO_DESTROY_METHOD_NAME;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_DESTROY_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    @Override
+    public Set<String> getServices() {
+        return wasmLoader.getWasmExtern(GET_SERVICES_METHOD_NAME)
+            .map(extern -> {
+                Integer count = WasmFunctions.func(wasmLoader.getStore(), extern.func(), WasmValType.I32).call();
+                return doGetServices(count);
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                GET_SERVICES_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract Set<String> doGetServices(Integer count);
+
+    @Override
+    public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
+        return wasmLoader.getWasmExtern(GET_INSTANCES_METHOD_NAME)
+            .map(extern -> {
+                // WASI cannot easily pass Java objects like JNI, here we pass Long as arg
+                // then we can get the argument by Long
+                final Long argumentId = getArgumentId(serviceName);
+                GET_INSTANCES_ARGUMENTS.put(argumentId, serviceName);
+                // call WASI function
+                Integer count = WasmFunctions.func(wasmLoader.getStore(), extern.func(),
+                    WasmValType.I64, WasmValType.I32).call(argumentId);
+                GET_INSTANCES_ARGUMENTS.remove(argumentId);
+                return doGetInstances(serviceName, argumentId, count);
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                GET_INSTANCES_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract List<ServiceInstance> doGetInstances(String serviceName, Long argumentId, Integer count);
+
+    protected abstract Long getArgumentId(String serviceName);
+
+    @Override
+    public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener)
+        throws NullPointerException, IllegalArgumentException {
+        wasmLoader.getWasmExtern(ADD_SERVICE_INSTANCES_CHANGED_LISTENER_METHOD_NAME)
+            .map(addServiceInstancesChangedListener -> {
+                Long argumentId = callWASI(listener, addServiceInstancesChangedListener);
+                addServiceInstancesChangedListener(listener, argumentId);
+                return ADD_SERVICE_INSTANCES_CHANGED_LISTENER_METHOD_NAME;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                ADD_SERVICE_INSTANCES_CHANGED_LISTENER_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener, Long argumentId)
+        throws NullPointerException, IllegalArgumentException;
+
+    @Override
+    public void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws IllegalArgumentException {
+        wasmLoader.getWasmExtern(REMOVE_SERVICE_INSTANCES_CHANGED_LISTENER_METHOD_NAME)
+            .map(addServiceInstancesChangedListener -> {
+                Long argumentId = callWASI(listener, addServiceInstancesChangedListener);
+                removeServiceInstancesChangedListener(listener, argumentId);
+                return REMOVE_SERVICE_INSTANCES_CHANGED_LISTENER_METHOD_NAME;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                REMOVE_SERVICE_INSTANCES_CHANGED_LISTENER_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener, Long argumentId)
+        throws IllegalArgumentException;
+
+    private Long callWASI(final ServiceInstancesChangedListener listener,
+                          final Extern doExecute) {
+        // WASI cannot easily pass Java objects like JNI, here we pass Long as arg
+        // then we can get the argument by Long
+        final Long argumentId = getArgumentId(listener);
+        SERVICE_INSTANCES_CHANGED_LISTENER_ARGUMENTS.put(argumentId, listener);
+        // call WASI function
+        WasmFunctions.consumer(wasmLoader.getStore(), doExecute.func(), WasmValType.I64)
+            .accept(argumentId);
+        SERVICE_INSTANCES_CHANGED_LISTENER_ARGUMENTS.remove(argumentId);
+        return argumentId;
+    }
+
+    protected abstract Long getArgumentId(ServiceInstancesChangedListener listener);
+}
diff --git a/dubbo-wasm/dubbo-wasm-registry-api/src/main/java/org/apache/dubbo/wasm/registry/support/AbstractWasmRegistry.java b/dubbo-wasm/dubbo-wasm-registry-api/src/main/java/org/apache/dubbo/wasm/registry/support/AbstractWasmRegistry.java
new file mode 100644
index 0000000..9c3052f
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-registry-api/src/main/java/org/apache/dubbo/wasm/registry/support/AbstractWasmRegistry.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.registry.support;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.support.FailbackRegistry;
+import org.apache.dubbo.wasm.WasmLoader;
+import org.apache.dubbo.wasm.exception.DubboWasmException;
+
+import io.github.kawamuray.wasmtime.Extern;
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+/**
+ * Registries implemented in other languages should extend this class, we still need to write Java subclasses,
+ * so we can reuse the convenient/powerful control of dubbo, such as {@link org.apache.dubbo.common.extension.Activate}.
+ *
+ * @see WasmLoader
+ */
+public abstract class AbstractWasmRegistry extends FailbackRegistry {
+
+    private static final String DO_REGISTER_METHOD_NAME = "doRegister";
+
+    private static final String DO_UNREGISTER_METHOD_NAME = "doUnregister";
+
+    private static final String DO_SUBSCRIBE_METHOD_NAME = "doSubscribe";
+
+    private static final String DO_UNSUBSCRIBE_METHOD_NAME = "doUnsubscribe";
+
+    private static final String IS_AVAILABLE_METHOD_NAME = "isAvailable";
+
+    protected static final Map<Long, Argument> ARGUMENTS = new ConcurrentHashMap<>();
+
+    private final WasmLoader wasmLoader;
+
+    public AbstractWasmRegistry(URL url) {
+        super(url);
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> supplier) {
+        return null;
+    }
+
+    @Override
+    public void doRegister(URL url) {
+        wasmLoader.getWasmExtern(DO_REGISTER_METHOD_NAME)
+            .map(doRegister -> {
+                Long argumentId = callWASI(url, null, doRegister);
+                doRegister(url, argumentId);
+                return DO_REGISTER_METHOD_NAME;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_REGISTER_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract void doRegister(URL url, Long argumentId);
+
+    @Override
+    public void doUnregister(URL url) {
+        wasmLoader.getWasmExtern(DO_UNREGISTER_METHOD_NAME)
+            .map(doUnregister -> {
+                Long argumentId = callWASI(url, null, doUnregister);
+                doUnregister(url, argumentId);
+                return DO_UNREGISTER_METHOD_NAME;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_UNREGISTER_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract void doUnregister(URL url, Long argumentId);
+
+    @Override
+    public void doSubscribe(URL url, NotifyListener listener) {
+        wasmLoader.getWasmExtern(DO_SUBSCRIBE_METHOD_NAME)
+            .map(doSubscribe -> {
+                Long argumentId = callWASI(url, listener, doSubscribe);
+                doSubscribe(url, listener, argumentId);
+                return DO_SUBSCRIBE_METHOD_NAME;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_SUBSCRIBE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract void doSubscribe(URL url, NotifyListener listener, Long argumentId);
+
+    @Override
+    public void doUnsubscribe(URL url, NotifyListener listener) {
+        wasmLoader.getWasmExtern(DO_UNSUBSCRIBE_METHOD_NAME)
+            .map(doUnsubscribe -> {
+                Long argumentId = callWASI(url, listener, doUnsubscribe);
+                doUnsubscribe(url, listener, argumentId);
+                return DO_UNSUBSCRIBE_METHOD_NAME;
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_UNSUBSCRIBE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract void doUnsubscribe(URL url, NotifyListener listener, Long argumentId);
+
+    private Long callWASI(final URL url,
+                          final NotifyListener listener,
+                          final Extern doExecute) {
+        // WASI cannot easily pass Java objects like JNI, here we pass Long as arg
+        // then we can get the argument by Long
+        final Long argumentId = getArgumentId(url, listener);
+        ARGUMENTS.put(argumentId, new Argument(url, listener));
+        // call WASI function
+        WasmFunctions.consumer(wasmLoader.getStore(), doExecute.func(), WasmValType.I64)
+            .accept(argumentId);
+        ARGUMENTS.remove(argumentId);
+        return argumentId;
+    }
+
+    protected abstract Long getArgumentId(URL url, NotifyListener listener);
+
+    @Override
+    public boolean isAvailable() {
+        Integer result = wasmLoader.getWasmExtern(IS_AVAILABLE_METHOD_NAME)
+            .map(isAvailable -> WasmFunctions.func(wasmLoader.getStore(), isAvailable.func(), WasmValType.I32).call())
+            .orElseThrow(() -> new DubboWasmException(
+                IS_AVAILABLE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+        return result > 0;
+    }
+
+    protected static class Argument {
+        protected URL url;
+        protected NotifyListener listener;
+
+        private Argument(URL url, NotifyListener listener) {
+            this.url = url;
+            this.listener = listener;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-registry-api/src/test/java/org/apache/dubbo/wasm/registry/client/AbstractWasmServiceDiscoveryTest.java b/dubbo-wasm/dubbo-wasm-registry-api/src/test/java/org/apache/dubbo/wasm/registry/client/AbstractWasmServiceDiscoveryTest.java
new file mode 100644
index 0000000..68d2721
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-registry-api/src/test/java/org/apache/dubbo/wasm/registry/client/AbstractWasmServiceDiscoveryTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.registry.client;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmServiceDiscoveryTest {
+
+    @Test
+    void test() {
+        RustServiceDiscovery discovery = new RustServiceDiscovery();
+        discovery.register();
+        discovery.unregister();
+
+        Set<String> services = discovery.getServices();
+        assertEquals(Collections.singleton("rust result"), services);
+
+        List<ServiceInstance> instances = discovery.getInstances("test");
+        assertEquals(1, instances.size());
+        assertEquals("rust result", instances.get(0).getRegistryCluster());
+
+        ServiceInstancesChangedListener listener = new ServiceInstancesChangedListener(new HashSet<>(), discovery);
+        discovery.addServiceInstancesChangedListener(listener);
+        discovery.removeServiceInstancesChangedListener(listener);
+    }
+
+    static class RustServiceDiscovery extends AbstractWasmServiceDiscovery {
+
+        public RustServiceDiscovery() {
+            super("RustServiceDiscovery", URL.valueOf("dubbo://127.0.0.1:12345?timeout=1234&default.timeout=5678"));
+        }
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+
+        @Override
+        protected void doRegister(ServiceInstance serviceInstance, Long argumentId) throws RuntimeException {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected void doUnregister(ServiceInstance serviceInstance, Long argumentId) {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected Long getArgumentId(ServiceInstance serviceInstance) {
+            return 7L;
+        }
+
+        @Override
+        protected Set<String> doGetServices(Integer count) {
+            Set<String> set = new HashSet<>(count);
+            for (int i = 0; i < count; i++) {
+                set.add(TestHelper.getResult(7));
+            }
+            return set;
+        }
+
+        @Override
+        protected List<ServiceInstance> doGetInstances(String serviceName, Long argumentId, Integer count) {
+            List<ServiceInstance> instances = new ArrayList<>(count);
+            for (int i = 0; i < count; i++) {
+                DefaultServiceInstance instance = new DefaultServiceInstance();
+                instance.setServiceName(serviceName);
+                instance.setRegistryCluster(TestHelper.getResult(argumentId));
+                instances.add(instance);
+            }
+            return instances;
+        }
+
+        @Override
+        protected Long getArgumentId(String serviceName) {
+            return 7L;
+        }
+
+        @Override
+        protected void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener, Long argumentId) throws NullPointerException, IllegalArgumentException {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected void removeServiceInstancesChangedListener(ServiceInstancesChangedListener listener, Long argumentId) throws IllegalArgumentException {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected Long getArgumentId(ServiceInstancesChangedListener listener) {
+            return 7L;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-registry-api/src/test/java/org/apache/dubbo/wasm/registry/support/AbstractWasmRegistryTest.java b/dubbo-wasm/dubbo-wasm-registry-api/src/test/java/org/apache/dubbo/wasm/registry/support/AbstractWasmRegistryTest.java
new file mode 100644
index 0000000..77f7e8f
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-registry-api/src/test/java/org/apache/dubbo/wasm/registry/support/AbstractWasmRegistryTest.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.registry.support;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmRegistryTest {
+
+    @Test
+    void test() {
+        URL url = URL.valueOf("dubbo://127.0.0.1:12345?timeout=1234&default.timeout=5678");
+        RustRegistry registry = new RustRegistry(url);
+        registry.register(url);
+        registry.unregister(url);
+
+        MyNotifyListener notifyListener = new MyNotifyListener();
+        registry.subscribe(url, notifyListener);
+        registry.unsubscribe(url, notifyListener);
+
+        assertTrue(registry.isAvailable());
+    }
+
+    static class RustRegistry extends AbstractWasmRegistry {
+
+        public RustRegistry(URL url) {
+            super(url);
+        }
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+
+        @Override
+        protected void doRegister(URL url, Long argumentId) {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected void doUnregister(URL url, Long argumentId) {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected void doSubscribe(URL url, NotifyListener listener, Long argumentId) {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected void doUnsubscribe(URL url, NotifyListener listener, Long argumentId) {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+        }
+
+        @Override
+        protected Long getArgumentId(URL url, NotifyListener listener) {
+            return 6L;
+        }
+    }
+
+    static class MyNotifyListener implements NotifyListener {
+        @Override
+        public void notify(List<URL> urls) {
+
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-remoting-api/pom.xml b/dubbo-wasm/dubbo-wasm-remoting-api/pom.xml
new file mode 100644
index 0000000..4335dde
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-remoting-api/pom.xml
@@ -0,0 +1,62 @@
+<?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">
+    <parent>
+        <artifactId>dubbo-wasm</artifactId>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-wasm-remoting-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-remoting-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>../dubbo-wasm-test/src/main/resources</directory>
+                <includes>
+                    <include>rust_extensions.wasm</include>
+                </includes>
+            </resource>
+        </resources>
+    </build>
+</project>
diff --git a/dubbo-wasm/dubbo-wasm-remoting-api/src/main/java/org/apache/dubbo/wasm/remoting/transport/AbstractWasmChannel.java b/dubbo-wasm/dubbo-wasm-remoting-api/src/main/java/org/apache/dubbo/wasm/remoting/transport/AbstractWasmChannel.java
new file mode 100644
index 0000000..3d21818
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-remoting-api/src/main/java/org/apache/dubbo/wasm/remoting/transport/AbstractWasmChannel.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.remoting.transport;
+
+import io.github.kawamuray.wasmtime.Extern;
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.remoting.ChannelHandler;
+import org.apache.dubbo.remoting.RemotingException;
+import org.apache.dubbo.remoting.transport.AbstractChannel;
+import org.apache.dubbo.wasm.WasmLoader;
+import org.apache.dubbo.wasm.exception.DubboWasmException;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+/**
+ * Channels implemented in other languages should extend this class, we still need to write Java subclasses,
+ * so we can reuse the convenient/powerful control of dubbo, such as {@link org.apache.dubbo.common.extension.Activate}.
+ *
+ * @see WasmLoader
+ */
+public abstract class AbstractWasmChannel extends AbstractChannel {
+
+    private static final Logger logger = LoggerFactory.getLogger(AbstractWasmChannel.class);
+
+    private static final String SEND_METHOD_NAME = "send";
+
+    private static final String CLOSE_METHOD_NAME = "closeChannel";
+
+    private static final String GET_REMOTE_ADDRESS_HOST_METHOD_NAME = "getRemoteAddressHost";
+
+    private static final String GET_REMOTE_ADDRESS_PORT_METHOD_NAME = "getRemoteAddressPort";
+
+    private static final String IS_CONNECTED_METHOD_NAME = "isConnected";
+
+    private static final String HAS_ATTRIBUTE_METHOD_NAME = "hasAttribute";
+
+    private static final String GET_ATTRIBUTE_METHOD_NAME = "getAttribute";
+
+    private static final String SET_ATTRIBUTE_METHOD_NAME = "setAttribute";
+
+    private static final String REMOVE_ATTRIBUTE_METHOD_NAME = "removeAttribute";
+
+    private static final String GET_LOCAL_ADDRESS_HOST_METHOD_NAME = "getLocalAddressHost";
+
+    private static final String GET_LOCAL_ADDRESS_PORT_METHOD_NAME = "getLocalAddressPort";
+
+    protected static final Map<Long, SendArgument> SEND_ARGUMENT_MAP = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, String> GET_REMOTE_ADDRESS_RESULTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, String> HAS_ATTRIBUTE_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, String> GET_ATTRIBUTE_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, SetAttributeArgument> SET_ATTRIBUTE_ARGUMENT_MAP = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, String> REMOVE_ATTRIBUTE_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, String> GET_LOCAL_ADDRESS_RESULTS = new ConcurrentHashMap<>();
+
+    private final WasmLoader wasmLoader;
+
+    public AbstractWasmChannel(URL url, ChannelHandler handler) {
+        super(url, handler);
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> buf) {
+        Map<String, Func> funcMap = new HashMap<>();
+        funcMap.put("setRemoteAddressHost", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+            (respId, addr, len) -> {
+                ByteBuffer buffer = buf.get();
+                byte[] bytes = new byte[len];
+                for (int i = 0; i < len; i++) {
+                    bytes[i] = buffer.get(addr.intValue() + i);
+                }
+                GET_REMOTE_ADDRESS_RESULTS.put(respId, new String(bytes, StandardCharsets.UTF_8));
+                return 0;
+            }));
+        funcMap.put("setLocalAddressHost", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+            (respId, addr, len) -> {
+                ByteBuffer buffer = buf.get();
+                byte[] bytes = new byte[len];
+                for (int i = 0; i < len; i++) {
+                    bytes[i] = buffer.get(addr.intValue() + i);
+                }
+                GET_LOCAL_ADDRESS_RESULTS.put(respId, new String(bytes, StandardCharsets.UTF_8));
+                return 0;
+            }));
+        return funcMap;
+    }
+
+    @Override
+    public void send(Object message, boolean sent) throws RemotingException {
+        super.send(message, sent);
+        Optional<Extern> func = wasmLoader.getWasmExtern(SEND_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(SEND_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = sendArgumentId(message, sent);
+        SEND_ARGUMENT_MAP.put(argumentId, new SendArgument(message, sent));
+        WasmFunctions.consumer(wasmLoader.getStore(), func.get().func(), WasmValType.I64).accept(argumentId);
+        SEND_ARGUMENT_MAP.remove(argumentId);
+    }
+
+    protected abstract Long sendArgumentId(Object message, boolean sent);
+
+    @Override
+    public void close() {
+        try {
+            super.close();
+        } catch (Exception e) {
+            logger.warn(e.getMessage(), e);
+        }
+        Optional<Extern> func = wasmLoader.getWasmExtern(CLOSE_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(CLOSE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        WasmFunctions.consumer(wasmLoader.getStore(), func.get().func()).accept();
+    }
+
+    @Override
+    public InetSocketAddress getRemoteAddress() {
+        Optional<Extern> hostFunc = wasmLoader.getWasmExtern(GET_REMOTE_ADDRESS_HOST_METHOD_NAME);
+        if (!hostFunc.isPresent()) {
+            throw new DubboWasmException(GET_REMOTE_ADDRESS_HOST_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        Optional<Extern> portFunc = wasmLoader.getWasmExtern(GET_REMOTE_ADDRESS_PORT_METHOD_NAME);
+        if (!portFunc.isPresent()) {
+            throw new DubboWasmException(GET_REMOTE_ADDRESS_PORT_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long respId = WasmFunctions.func(wasmLoader.getStore(), hostFunc.get().func(), WasmValType.I64).call();
+        final Integer port = WasmFunctions.func(wasmLoader.getStore(), portFunc.get().func(), WasmValType.I32).call();
+        return InetSocketAddress.createUnresolved(GET_REMOTE_ADDRESS_RESULTS.get(respId), port);
+    }
+
+    @Override
+    public boolean isConnected() {
+        Optional<Extern> func = wasmLoader.getWasmExtern(IS_CONNECTED_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(IS_CONNECTED_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        Integer result = WasmFunctions.func(wasmLoader.getStore(), func.get().func(), WasmValType.I32).call();
+        return result != 0;
+    }
+
+    @Override
+    public boolean hasAttribute(String key) {
+        Optional<Extern> func = wasmLoader.getWasmExtern(HAS_ATTRIBUTE_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(HAS_ATTRIBUTE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = hasAttributeArgumentId(key);
+        HAS_ATTRIBUTE_ARGUMENTS.put(argumentId, key);
+        Integer result = WasmFunctions.func(wasmLoader.getStore(), func.get().func(), WasmValType.I64, WasmValType.I32)
+            .call(argumentId);
+        HAS_ATTRIBUTE_ARGUMENTS.remove(argumentId);
+        return result != 0;
+    }
+
+    protected abstract Long hasAttributeArgumentId(String key);
+
+    @Override
+    public Object getAttribute(String key) {
+        Optional<Extern> func = wasmLoader.getWasmExtern(GET_ATTRIBUTE_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(GET_ATTRIBUTE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = getAttributeArgumentId(key);
+        GET_ATTRIBUTE_ARGUMENTS.put(argumentId, key);
+        WasmFunctions.consumer(wasmLoader.getStore(), func.get().func(), WasmValType.I64).accept(argumentId);
+        GET_ATTRIBUTE_ARGUMENTS.remove(argumentId);
+        return getAttribute(key, argumentId);
+    }
+
+    protected abstract Long getAttributeArgumentId(String key);
+
+    protected abstract Object getAttribute(String key, Long argumentId);
+
+    @Override
+    public void setAttribute(String key, Object value) {
+        Optional<Extern> func = wasmLoader.getWasmExtern(SET_ATTRIBUTE_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(SET_ATTRIBUTE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = setAttributeArgumentId(key, value);
+        SET_ATTRIBUTE_ARGUMENT_MAP.put(argumentId, new SetAttributeArgument(key, value));
+        WasmFunctions.consumer(wasmLoader.getStore(), func.get().func(), WasmValType.I64).accept(argumentId);
+        SET_ATTRIBUTE_ARGUMENT_MAP.remove(argumentId);
+    }
+
+    protected abstract Long setAttributeArgumentId(String key, Object value);
+
+    @Override
+    public void removeAttribute(String key) {
+        Optional<Extern> func = wasmLoader.getWasmExtern(REMOVE_ATTRIBUTE_METHOD_NAME);
+        if (!func.isPresent()) {
+            throw new DubboWasmException(REMOVE_ATTRIBUTE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long argumentId = removeAttributeArgumentId(key);
+        REMOVE_ATTRIBUTE_ARGUMENTS.put(argumentId, key);
+        WasmFunctions.consumer(wasmLoader.getStore(), func.get().func(), WasmValType.I64).accept(argumentId);
+        REMOVE_ATTRIBUTE_ARGUMENTS.remove(argumentId);
+    }
+
+    protected abstract Long removeAttributeArgumentId(String key);
+
+    @Override
+    public InetSocketAddress getLocalAddress() {
+        Optional<Extern> hostFunc = wasmLoader.getWasmExtern(GET_LOCAL_ADDRESS_HOST_METHOD_NAME);
+        if (!hostFunc.isPresent()) {
+            throw new DubboWasmException(GET_LOCAL_ADDRESS_HOST_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        Optional<Extern> portFunc = wasmLoader.getWasmExtern(GET_LOCAL_ADDRESS_PORT_METHOD_NAME);
+        if (!portFunc.isPresent()) {
+            throw new DubboWasmException(GET_LOCAL_ADDRESS_PORT_METHOD_NAME + " function not found in " + wasmLoader.getWasmName());
+        }
+        final Long respId = WasmFunctions.func(wasmLoader.getStore(), hostFunc.get().func(), WasmValType.I64).call();
+        final Integer port = WasmFunctions.func(wasmLoader.getStore(), portFunc.get().func(), WasmValType.I32).call();
+        return InetSocketAddress.createUnresolved(GET_LOCAL_ADDRESS_RESULTS.get(respId), port);
+    }
+
+    protected static class SendArgument {
+        protected Object message;
+        protected boolean sent;
+
+        private SendArgument(Object message, boolean sent) {
+            this.message = message;
+            this.sent = sent;
+        }
+    }
+
+    protected static class SetAttributeArgument {
+        protected String key;
+        protected Object value;
+
+        private SetAttributeArgument(String key, Object value) {
+            this.key = key;
+            this.value = value;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-remoting-api/src/test/java/org/apache/dubbo/wasm/remoting/transport/AbstractWasmChannelTest.java b/dubbo-wasm/dubbo-wasm-remoting-api/src/test/java/org/apache/dubbo/wasm/remoting/transport/AbstractWasmChannelTest.java
new file mode 100644
index 0000000..72852d6
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-remoting-api/src/test/java/org/apache/dubbo/wasm/remoting/transport/AbstractWasmChannelTest.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.remoting.transport;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.remoting.transport.ChannelHandlerAdapter;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmChannelTest {
+
+    @Test
+    void test() {
+        RustChannel channel = new RustChannel();
+        try {
+            channel.send("key");
+            channel.getRemoteAddress();
+            channel.isConnected();
+            channel.hasAttribute("key");
+            channel.getAttribute("key");
+            channel.setAttribute("key", "value");
+            channel.removeAttribute("key");
+            channel.getLocalAddress();
+        } catch (Exception ignored) {
+        } finally {
+            channel.close();
+        }
+    }
+
+    static class RustChannel extends AbstractWasmChannel {
+        public RustChannel() {
+            super(URL.valueOf("dubbo://127.0.0.1:12345?timeout=1234&default.timeout=5678"), new ChannelHandlerAdapter());
+        }
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            Map<String, Func> funcs = TestHelper.initWasmCallJavaFunc(store, supplier);
+            funcs.putAll(super.initWasmCallJavaFunc(store, supplier));
+            return funcs;
+        }
+
+        @Override
+        protected Long sendArgumentId(Object message, boolean sent) {
+            return 9L;
+        }
+
+        @Override
+        protected Long hasAttributeArgumentId(String key) {
+            return 9L;
+        }
+
+        @Override
+        protected Long getAttributeArgumentId(String key) {
+            return 9L;
+        }
+
+        @Override
+        protected Object getAttribute(String key, Long argumentId) {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return result;
+        }
+
+        @Override
+        protected Long setAttributeArgumentId(String key, Object value) {
+            return 9L;
+        }
+
+        @Override
+        protected Long removeAttributeArgumentId(String key) {
+            return 9L;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-rpc-api/pom.xml b/dubbo-wasm/dubbo-wasm-rpc-api/pom.xml
new file mode 100644
index 0000000..c3f59ae
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-rpc-api/pom.xml
@@ -0,0 +1,62 @@
+<?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">
+    <parent>
+        <artifactId>dubbo-wasm</artifactId>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-wasm-rpc-api</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-rpc-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>../dubbo-wasm-test/src/main/resources</directory>
+                <includes>
+                    <include>rust_extensions.wasm</include>
+                </includes>
+            </resource>
+        </resources>
+    </build>
+</project>
diff --git a/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/AbstractWasmFilter.java b/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/AbstractWasmFilter.java
new file mode 100644
index 0000000..a9ba813
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/AbstractWasmFilter.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.rpc;
+
+import org.apache.dubbo.rpc.Filter;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.wasm.WasmLoader;
+import org.apache.dubbo.wasm.exception.DubboWasmException;
+
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Filters implemented in other languages should extend this class, we still need to write Java subclasses,
+ * so we can reuse the convenient/powerful control of dubbo, such as {@link org.apache.dubbo.common.extension.Activate}.
+ *
+ * @see WasmLoader
+ */
+public abstract class AbstractWasmFilter extends WasmLoader implements Filter {
+
+    private static final String INVOKE_METHOD_NAME = "invoke";
+
+    protected static final Map<Long, Argument> ARGUMENTS = new ConcurrentHashMap<>();
+
+    @Override
+    public final Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
+        return super.getWasmExtern(INVOKE_METHOD_NAME)
+            .map(extern -> {
+                // call WASI function
+                final Long argumentId = getArgumentId(invoker, invocation);
+                ARGUMENTS.put(argumentId, new Argument(invoker, invocation));
+                // WASI cannot easily pass Java objects like JNI, here we pass Long
+                // then we can get the argument by Long
+                WasmFunctions.consumer(super.getStore(), extern.func(), WasmValType.I64)
+                    .accept(argumentId);
+                ARGUMENTS.remove(argumentId);
+                return doInvoke(invoker, invocation, argumentId);
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                INVOKE_METHOD_NAME + " function not found in " + super.getWasmName()));
+    }
+
+    protected abstract Result doInvoke(Invoker<?> invoker, Invocation invocation, Long argumentId);
+
+    protected abstract Long getArgumentId(Invoker<?> invoker, Invocation invocation);
+
+    protected static class Argument {
+        protected Invoker<?> invoker;
+        protected Invocation invocation;
+
+        private Argument(Invoker<?> invoker, Invocation invocation) {
+            this.invoker = invoker;
+            this.invocation = invocation;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmExporter.java b/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmExporter.java
new file mode 100644
index 0000000..443bed3
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmExporter.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.dubbo.wasm.rpc.protocol;
+
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.protocol.AbstractExporter;
+import org.apache.dubbo.wasm.WasmLoader;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public abstract class AbstractWasmExporter<T> extends AbstractExporter<T> {
+
+    private static final String AFTER_UN_EXPORT_METHOD_NAME = "afterUnExport";
+
+    private final WasmLoader wasmLoader;
+
+    public AbstractWasmExporter(Invoker<T> invoker) {
+        super(invoker);
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> supplier) {
+        return null;
+    }
+
+    @Override
+    public void afterUnExport() {
+        wasmLoader.getWasmExtern(AFTER_UN_EXPORT_METHOD_NAME).ifPresent(afterUnExport ->
+            WasmFunctions.consumer(wasmLoader.getStore(), afterUnExport.func()).accept());
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmInvoker.java b/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmInvoker.java
new file mode 100644
index 0000000..2463a3a
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmInvoker.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.rpc.protocol;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.protocol.AbstractInvoker;
+import org.apache.dubbo.wasm.WasmLoader;
+import org.apache.dubbo.wasm.exception.DubboWasmException;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+public abstract class AbstractWasmInvoker<T> extends AbstractInvoker<T> {
+
+    private static final String DO_INVOKE_METHOD_NAME = "doInvoke";
+
+    private static final String DESTROY_METHOD_NAME = "destroy";
+
+    private static final String DESTROY_ALL_METHOD_NAME = "destroyAll";
+
+    protected static final Map<Long, Invocation> ARGUMENTS = new ConcurrentHashMap<>();
+
+    private final WasmLoader wasmLoader;
+
+    public AbstractWasmInvoker(Class<T> type, URL url) {
+        super(type, url);
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    public AbstractWasmInvoker(Class<T> type, URL url, String[] keys) {
+        super(type, url, keys);
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    public AbstractWasmInvoker(Class<T> type, URL url, Map<String, Object> attachment) {
+        super(type, url, attachment);
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> supplier) {
+        return null;
+    }
+
+    @Override
+    protected Result doInvoke(Invocation invocation) {
+        return wasmLoader.getWasmExtern(DO_INVOKE_METHOD_NAME)
+            .map(extern -> {
+                // call WASI function
+                final Long argumentId = getArgumentId(invocation);
+                ARGUMENTS.put(argumentId, invocation);
+                // WASI cannot easily pass Java objects like JNI, here we pass Long
+                // then we can get the argument by Long
+                WasmFunctions.consumer(wasmLoader.getStore(), extern.func(), WasmValType.I64)
+                    .accept(argumentId);
+                ARGUMENTS.remove(argumentId);
+                return doInvoke(invocation, argumentId);
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                DO_INVOKE_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract Result doInvoke(Invocation invocation, Long argumentId);
+
+    protected abstract Long getArgumentId(Invocation invocation);
+
+    @Override
+    public void destroy() {
+        wasmLoader.getWasmExtern(DESTROY_METHOD_NAME).ifPresent(destroyAll ->
+            WasmFunctions.consumer(wasmLoader.getStore(), destroyAll.func()).accept());
+        super.destroy();
+    }
+
+
+    public void destroyAll() {
+        wasmLoader.getWasmExtern(DESTROY_ALL_METHOD_NAME).ifPresent(destroyAll ->
+            WasmFunctions.consumer(wasmLoader.getStore(), destroyAll.func()).accept());
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmProtocol.java b/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmProtocol.java
new file mode 100644
index 0000000..1c0753a
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-rpc-api/src/main/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmProtocol.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.rpc.protocol;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.protocol.AbstractProtocol;
+import org.apache.dubbo.wasm.WasmLoader;
+import org.apache.dubbo.wasm.exception.DubboWasmException;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+/**
+ * Protocols implemented in other languages should extend this class, we still need to write Java subclasses,
+ * so we can reuse the convenient/powerful control of dubbo, such as {@link org.apache.dubbo.common.extension.Activate}.
+ *
+ * @see WasmLoader
+ */
+public abstract class AbstractWasmProtocol extends AbstractProtocol {
+
+    private static final String REFER_METHOD_NAME = "refer";
+
+    private static final String EXPORT_METHOD_NAME = "export";
+
+    protected static final Map<Long, Argument<?>> REFER_ARGUMENTS = new ConcurrentHashMap<>();
+
+    protected static final Map<Long, Invoker<?>> EXPORT_ARGUMENTS = new ConcurrentHashMap<>();
+
+    private final WasmLoader wasmLoader;
+
+    public AbstractWasmProtocol() {
+        this.wasmLoader = new WasmLoader(this.getClass(), this::buildWasmName, this::initWasmCallJavaFunc);
+    }
+
+    protected String buildWasmName(final Class<?> clazz) {
+        return clazz.getName() + ".wasm";
+    }
+
+    protected Map<String, Func> initWasmCallJavaFunc(final Store<Void> store, Supplier<ByteBuffer> supplier) {
+        return null;
+    }
+
+    @Override
+    protected <T> Invoker<T> protocolBindingRefer(Class<T> type, URL url) throws RpcException {
+        return wasmLoader.getWasmExtern(REFER_METHOD_NAME)
+            .map(extern -> {
+                // call WASI function
+                final Long argumentId = getArgumentId(type, url);
+                REFER_ARGUMENTS.put(argumentId, new Argument<>(type, url));
+                // WASI cannot easily pass Java objects like JNI, here we pass Long
+                // then we can get the argument by Long
+                WasmFunctions.consumer(wasmLoader.getStore(), extern.func(), WasmValType.I64)
+                    .accept(argumentId);
+                REFER_ARGUMENTS.remove(argumentId);
+                return doRefer(type, url, argumentId);
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                REFER_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract <T> Invoker<T> doRefer(Class<T> type, URL url, Long argumentId);
+
+    protected abstract <T> Long getArgumentId(Class<T> type, URL url);
+
+    @Override
+    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
+        return wasmLoader.getWasmExtern(EXPORT_METHOD_NAME)
+            .map(extern -> {
+                // call WASI function
+                final Long argumentId = getArgumentId(invoker);
+                EXPORT_ARGUMENTS.put(argumentId, invoker);
+                // WASI cannot easily pass Java objects like JNI, here we pass Long
+                // then we can get the argument by Long
+                WasmFunctions.consumer(wasmLoader.getStore(), extern.func(), WasmValType.I64)
+                    .accept(argumentId);
+                EXPORT_ARGUMENTS.remove(argumentId);
+                return doExport(invoker, argumentId);
+            })
+            .orElseThrow(() -> new DubboWasmException(
+                EXPORT_METHOD_NAME + " function not found in " + wasmLoader.getWasmName()));
+    }
+
+    protected abstract <T> Exporter<T> doExport(Invoker<T> invoker, Long argumentId);
+
+    protected abstract <T> Long getArgumentId(Invoker<T> invoker);
+
+    protected static class Argument<T> {
+        protected Class<T> type;
+        protected URL url;
+
+        private Argument(Class<T> type, URL url) {
+            this.type = type;
+            this.url = url;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/AbstractWasmFilterTest.java b/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/AbstractWasmFilterTest.java
new file mode 100644
index 0000000..67b6c4b
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/AbstractWasmFilterTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.rpc;
+
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmFilterTest {
+
+    @Test
+    void test() {
+        try (RustFilter filter = new RustFilter()) {
+            filter.invoke(null, null);
+        }
+    }
+
+    static class RustFilter extends AbstractWasmFilter {
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+
+        @Override
+        protected Result doInvoke(Invoker<?> invoker, Invocation invocation, Long argumentId) {
+            final String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return new AppResponse();
+        }
+
+        @Override
+        protected Long getArgumentId(Invoker<?> invoker, Invocation invocation) {
+            return 0L;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmExporterTest.java b/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmExporterTest.java
new file mode 100644
index 0000000..2ed36d5
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmExporterTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.rpc.protocol;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.wasm.rpc.protocol.AbstractWasmExporter;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.function.Supplier;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmExporterTest {
+
+    @Test
+    void test() {
+        RustExporter exporter = new RustExporter();
+        exporter.afterUnExport();
+    }
+
+    static class RustExporter extends AbstractWasmExporter<Object> {
+
+        public RustExporter() {
+            super(new MyInvoker());
+        }
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+    }
+
+    static class MyInvoker implements Invoker<Object> {
+
+        @Override
+        public URL getUrl() {
+            return URL.valueOf("dubbo://127.0.0.1:12345?timeout=1234&default.timeout=5678");
+        }
+
+        @Override
+        public boolean isAvailable() {
+            return false;
+        }
+
+        @Override
+        public void destroy() {
+        }
+
+        @Override
+        public Class<Object> getInterface() {
+            return Object.class;
+        }
+
+        @Override
+        public Result invoke(Invocation invocation) throws RpcException {
+            return null;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmInvokerTest.java b/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmInvokerTest.java
new file mode 100644
index 0000000..6bd1033
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmInvokerTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.rpc.protocol;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.apache.dubbo.wasm.rpc.protocol.AbstractWasmInvoker;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmInvokerTest {
+
+    @Test
+    void test() {
+        RustInvoker invoker = new RustInvoker();
+        invoker.invoke(new RpcInvocation());
+        invoker.destroy();
+        invoker.destroyAll();
+    }
+
+    static class RustInvoker extends AbstractWasmInvoker<Object> {
+
+        public RustInvoker() {
+            super(Object.class, URL.valueOf("dubbo://127.0.0.1:12345?timeout=1234&default.timeout=5678"));
+        }
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+
+        @Override
+        protected Result doInvoke(Invocation invocation, Long argumentId) {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return new AppResponse();
+        }
+
+        @Override
+        protected Long getArgumentId(Invocation invocation) {
+            return 4L;
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmProtocolTest.java b/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmProtocolTest.java
new file mode 100644
index 0000000..c026bbc
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-rpc-api/src/test/java/org/apache/dubbo/wasm/rpc/protocol/AbstractWasmProtocolTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.rpc.protocol;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.rpc.AppResponse;
+import org.apache.dubbo.rpc.Exporter;
+import org.apache.dubbo.rpc.Invocation;
+import org.apache.dubbo.rpc.Invoker;
+import org.apache.dubbo.rpc.Result;
+import org.apache.dubbo.wasm.rpc.protocol.AbstractWasmExporter;
+import org.apache.dubbo.wasm.rpc.protocol.AbstractWasmInvoker;
+import org.apache.dubbo.wasm.rpc.protocol.AbstractWasmProtocol;
+import org.apache.dubbo.wasm.test.TestHelper;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import org.junit.jupiter.api.Test;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * see dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
+ */
+public class AbstractWasmProtocolTest {
+
+    @Test
+    void test() {
+        RustProtocol protocol = new RustProtocol();
+        protocol.refer(Object.class, null);
+        protocol.export(new RustInvoker<>(Object.class));
+    }
+
+    static class RustProtocol extends AbstractWasmProtocol {
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+
+        @Override
+        protected <T> Invoker<T> doRefer(Class<T> type, URL url, Long argumentId) {
+            final String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return new RustInvoker<>(type);
+        }
+
+        @Override
+        protected <T> Long getArgumentId(Class<T> type, URL url) {
+            return 5L;
+        }
+
+        @Override
+        protected <T> Exporter<T> doExport(Invoker<T> invoker, Long argumentId) {
+            final String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return new RustExporter<>(invoker);
+        }
+
+        @Override
+        protected <T> Long getArgumentId(Invoker<T> invoker) {
+            return 6L;
+        }
+
+        @Override
+        public int getDefaultPort() {
+            return 0;
+        }
+    }
+
+    static class RustInvoker<T> extends AbstractWasmInvoker<T> {
+
+        public RustInvoker(Class<T> type) {
+            super(type, URL.valueOf("dubbo://127.0.0.1:12345?timeout=1234&default.timeout=5678"));
+        }
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+
+        @Override
+        protected Result doInvoke(Invocation invocation, Long argumentId) {
+            String result = TestHelper.getResult(argumentId);
+            assertEquals("rust result", result);
+            return new AppResponse();
+        }
+
+        @Override
+        protected Long getArgumentId(Invocation invocation) {
+            return 4L;
+        }
+    }
+
+    static class RustExporter<T> extends AbstractWasmExporter<T> {
+
+        public RustExporter(Invoker<T> invoker) {
+            super(invoker);
+        }
+
+        @Override
+        protected String buildWasmName(Class<?> clazz) {
+            return TestHelper.WASM_NAME;
+        }
+
+        @Override
+        protected Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> supplier) {
+            return TestHelper.initWasmCallJavaFunc(store, supplier);
+        }
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-test/pom.xml b/dubbo-wasm/dubbo-wasm-test/pom.xml
new file mode 100644
index 0000000..ee66b16
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-test/pom.xml
@@ -0,0 +1,43 @@
+<?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">
+    <parent>
+        <artifactId>dubbo-wasm</artifactId>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>dubbo-wasm-test</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.dubbo.extensions</groupId>
+            <artifactId>dubbo-wasm-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+
+</project>
diff --git a/dubbo-wasm/dubbo-wasm-test/src/main/java/org/apache/dubbo/wasm/test/TestHelper.java b/dubbo-wasm/dubbo-wasm-test/src/main/java/org/apache/dubbo/wasm/test/TestHelper.java
new file mode 100644
index 0000000..a019831
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-test/src/main/java/org/apache/dubbo/wasm/test/TestHelper.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.dubbo.wasm.test;
+
+import io.github.kawamuray.wasmtime.Func;
+import io.github.kawamuray.wasmtime.Store;
+import io.github.kawamuray.wasmtime.WasmFunctions;
+import io.github.kawamuray.wasmtime.WasmValType;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+public class TestHelper {
+
+    public static final String WASM_NAME = "rust_extensions.wasm";
+
+    private static final Map<Long, String> RESULTS = new ConcurrentHashMap<>();
+
+    public static Map<String, Func> initWasmCallJavaFunc(Store<Void> store, Supplier<ByteBuffer> buf) {
+        Map<String, Func> funcMap = new HashMap<>();
+        funcMap.put("get_args", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+            (argId, addr, len) -> {
+                ByteBuffer buffer = buf.get();
+                String config = "hello from java " + argId;
+                System.out.println("java side->" + config);
+                for (int i = 0; i < len && i < config.length(); i++) {
+                    buffer.put(addr.intValue() + i, (byte) config.charAt(i));
+                }
+                return Math.min(config.length(), len);
+            }));
+        funcMap.put("put_result", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+            (argId, addr, len) -> {
+                ByteBuffer buffer = buf.get();
+                byte[] bytes = new byte[len];
+                for (int i = 0; i < len; i++) {
+                    bytes[i] = buffer.get(addr.intValue() + i);
+                }
+                String result = new String(bytes, StandardCharsets.UTF_8);
+                RESULTS.put(argId, result);
+                System.out.println("java side->" + result);
+                return 0;
+            }));
+        funcMap.put("setRemoteAddressHost", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+            (respId, addr, len) -> 0));
+        funcMap.put("setLocalAddressHost", WasmFunctions.wrap(store, WasmValType.I64, WasmValType.I64, WasmValType.I32, WasmValType.I32,
+            (respId, addr, len) -> 0));
+        return funcMap;
+    }
+
+    public static String getResult(long id) {
+        return RESULTS.get(id);
+    }
+}
diff --git a/dubbo-wasm/dubbo-wasm-test/src/main/resources/rust_extensions.wasm b/dubbo-wasm/dubbo-wasm-test/src/main/resources/rust_extensions.wasm
new file mode 100755
index 0000000..156cb4a
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-test/src/main/resources/rust_extensions.wasm
Binary files differ
diff --git a/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/Cargo.toml b/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/Cargo.toml
new file mode 100644
index 0000000..8d6a446
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/Cargo.toml
@@ -0,0 +1,26 @@
+#
+# 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]
+name = "rust-extensions"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
diff --git a/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md b/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
new file mode 100644
index 0000000..89e48bf
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/README.md
@@ -0,0 +1,17 @@
+# How to build the wasm file
+
+1. install rustup
+
+2. install rust
+
+3. generate the wasm file
+
+```shell
+cd {dubbo-spi-extensions}/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions
+cargo build --target wasm32-wasi --release
+```
+
+then you will see the wasm file
+in `{dubbo-spi-extensions}/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/target/wasm32-wasi/release/rust_filter.wasm`
+
+4. rename the wasm file if you need
diff --git a/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/src/lib.rs b/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/src/lib.rs
new file mode 100644
index 0000000..90d23e3
--- /dev/null
+++ b/dubbo-wasm/dubbo-wasm-test/src/main/rust-extensions/src/lib.rs
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+
+#[link(wasm_import_module = "dubbo")]
+extern "C" {
+    fn get_args(arg_id: i64, addr: i64, len: i32) -> i32;
+
+    fn put_result(arg_id: i64, addr: i64, len: i32) -> i32;
+
+    fn setRemoteAddressHost(arg_id: i64, addr: i64, len: i32) -> i32;
+
+    fn setLocalAddressHost(arg_id: i64, addr: i64, len: i32) -> i32;
+}
+
+pub unsafe extern "C" fn impls(arg_id: i64) {
+    let mut buf = [0u8; 32];
+    let buf_ptr = buf.as_mut_ptr() as i64;
+    // get arg from java
+    let len = get_args(arg_id, buf_ptr, buf.len() as i32);
+    let java_arg = std::str::from_utf8(&buf[..len as usize]).unwrap();
+    eprintln!("rust side-> recv:{}", java_arg);
+    // pass rust result to java
+    let rust_result = "rust result".as_bytes();
+    let result_ptr = rust_result.as_ptr() as i64;
+    _ = put_result(arg_id, result_ptr, rust_result.len() as i32);
+}
+
+/// 0
+#[no_mangle]
+pub unsafe extern "C" fn invoke(arg_id: i64) {
+    eprintln!("rust side-> invoke");
+    impls(arg_id);
+}
+
+/// 1
+#[no_mangle]
+pub unsafe extern "C" fn doSelect(arg_id: i64) -> i32 {
+    eprintln!("rust side-> doSelect");
+    impls(arg_id);
+    1
+}
+
+/// 2
+#[no_mangle]
+pub unsafe extern "C" fn route(arg_id: i64) {
+    eprintln!("rust side-> route");
+    impls(arg_id);
+}
+
+/// 3
+#[no_mangle]
+pub unsafe extern "C" fn afterUnExport() {
+    eprintln!("rust side-> afterUnExport");
+    impls(3);
+}
+
+/// 4
+#[no_mangle]
+pub unsafe extern "C" fn doInvoke(arg_id: i64) {
+    eprintln!("rust side-> doInvoke");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn destroy() {
+    eprintln!("rust side-> destroy");
+    impls(4);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn destroyAll() {
+    eprintln!("rust side-> destroyAll");
+    impls(4);
+}
+
+/// 5
+#[no_mangle]
+pub unsafe extern "C" fn refer(arg_id: i64) {
+    eprintln!("rust side-> refer");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn export(arg_id: i64) {
+    eprintln!("rust side-> export");
+    impls(arg_id);
+}
+
+/// 6 & 7
+#[no_mangle]
+pub unsafe extern "C" fn doRegister(arg_id: i64) {
+    eprintln!("rust side-> doRegister");
+    impls(arg_id);
+}
+
+/// 6 & 7
+#[no_mangle]
+pub unsafe extern "C" fn doUnregister(arg_id: i64) {
+    eprintln!("rust side-> doUnregister");
+    impls(arg_id);
+}
+
+/// 6
+#[no_mangle]
+pub unsafe extern "C" fn doSubscribe(arg_id: i64) {
+    eprintln!("rust side-> doSubscribe");
+    impls(arg_id);
+}
+
+/// 6
+#[no_mangle]
+pub unsafe extern "C" fn doUnsubscribe(arg_id: i64) {
+    eprintln!("rust side-> doUnsubscribe");
+    impls(arg_id);
+}
+
+/// 6
+#[no_mangle]
+pub unsafe extern "C" fn isAvailable() -> i32 {
+    eprintln!("rust side-> isAvailable");
+    impls(6);
+    1
+}
+
+/// 7
+#[no_mangle]
+pub unsafe extern "C" fn doDestroy() {
+    eprintln!("rust side-> doDestroy");
+    impls(7);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getServices() -> i32 {
+    eprintln!("rust side-> getServices");
+    impls(7);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getInstances(arg_id: i64) -> i32 {
+    eprintln!("rust side-> getInstances");
+    impls(arg_id);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn addServiceInstancesChangedListener(arg_id: i64) {
+    eprintln!("rust side-> addServiceInstancesChangedListener");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn removeServiceInstancesChangedListener(arg_id: i64) {
+    eprintln!("rust side-> removeServiceInstancesChangedListener");
+    impls(arg_id);
+}
+
+/// 8
+
+#[no_mangle]
+pub unsafe extern "C" fn getInternalProperty(arg_id: i64) {
+    eprintln!("rust side-> getInternalProperty");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn doPublishConfig(arg_id: i64) -> i32 {
+    eprintln!("rust side-> doPublishConfig");
+    impls(arg_id);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn doGetConfig(arg_id: i64) {
+    eprintln!("rust side-> doPublishConfig");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn doRemoveConfig(arg_id: i64) -> i32 {
+    eprintln!("rust side-> doRemoveConfig");
+    impls(arg_id);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn doGetConfigKeys(arg_id: i64) -> i32 {
+    eprintln!("rust side-> doGetConfigKeys");
+    impls(arg_id);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn doGetConfigKeyItem(arg_id: i64, index:i32) {
+    eprintln!("rust side-> doGetConfigKeyItem {index}");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn doAddListener(arg_id: i64) {
+    eprintln!("rust side-> doAddListener");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn doRemoveListener(arg_id: i64) {
+    eprintln!("rust side-> doRemoveListener");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn doClose(arg_id: i64) {
+    eprintln!("rust side-> doClose");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn publishConfigCas(arg_id: i64) -> i32 {
+    eprintln!("rust side-> publishConfigCas");
+    impls(arg_id);
+    1
+}
+
+/// 9
+
+#[no_mangle]
+pub unsafe extern "C" fn send(arg_id: i64) {
+    eprintln!("rust side-> send");
+    impls(arg_id);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getRemoteAddressHost()->i64 {
+    eprintln!("rust side-> getRemoteAddressHost");
+    impls(9);
+    let rust_result = "localhost".as_bytes();
+    let result_ptr = rust_result.as_ptr() as i64;
+    _ = setRemoteAddressHost(9, result_ptr, rust_result.len() as i32);
+    9
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getRemoteAddressPort()->i32 {
+    eprintln!("rust side-> getRemoteAddressPort");
+    impls(9);
+    9999
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn isConnected()->i32 {
+    eprintln!("rust side-> isConnected");
+    impls(9);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn hasAttribute(arg_id: i64) -> i32 {
+    eprintln!("rust side-> hasAttribute");
+    impls(arg_id);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getAttribute(arg_id: i64) -> i32 {
+    eprintln!("rust side-> getAttribute");
+    impls(arg_id);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn setAttribute(arg_id: i64) -> i32 {
+    eprintln!("rust side-> setAttribute");
+    impls(arg_id);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn removeAttribute(arg_id: i64) -> i32 {
+    eprintln!("rust side-> removeAttribute");
+    impls(arg_id);
+    1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getLocalAddressHost()->i64 {
+    eprintln!("rust side-> getLocalAddressHost");
+    impls(9);
+    let rust_result = "localhost".as_bytes();
+    let result_ptr = rust_result.as_ptr() as i64;
+    _ = setLocalAddressHost(9, result_ptr, rust_result.len() as i32);
+    9
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getLocalAddressPort()->i32 {
+    eprintln!("rust side-> getLocalAddressPort");
+    impls(9);
+    9999
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn closeChannel() {
+    eprintln!("rust side-> closeChannel");
+    impls(9);
+}
diff --git a/dubbo-wasm/pom.xml b/dubbo-wasm/pom.xml
new file mode 100644
index 0000000..785daa0
--- /dev/null
+++ b/dubbo-wasm/pom.xml
@@ -0,0 +1,58 @@
+<?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">
+    <parent>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <artifactId>extensions-parent</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>dubbo-wasm</artifactId>
+    <packaging>pom</packaging>
+    <version>${revision}</version>
+
+    <modules>
+        <module>dubbo-wasm-api</module>
+        <module>dubbo-wasm-common-api</module>
+        <module>dubbo-wasm-rpc-api</module>
+        <module>dubbo-wasm-cluster-api</module>
+        <module>dubbo-wasm-registry-api</module>
+        <module>dubbo-wasm-remoting-api</module>
+        <module>dubbo-wasm-test</module>
+    </modules>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Internal libs -->
+            <dependency>
+                <groupId>org.apache.dubbo.extensions</groupId>
+                <artifactId>dubbo-wasm-api</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.dubbo.extensions</groupId>
+                <artifactId>dubbo-wasm-test</artifactId>
+                <version>${revision}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+</project>
diff --git a/dubbo-xds/pom.xml b/dubbo-xds/pom.xml
index def5fbb..7bc7c3a 100644
--- a/dubbo-xds/pom.xml
+++ b/dubbo-xds/pom.xml
@@ -36,17 +36,6 @@
         <maven_protobuf_plugin_version>0.6.1</maven_protobuf_plugin_version>
     </properties>
 
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-bom</artifactId>
-                <version>3.2.9</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
 
     <dependencies>
         <dependency>
diff --git a/pom.xml b/pom.xml
index 522f140..dc8651f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -100,20 +100,23 @@
         <module>dobbo-doc-auto-gen</module>
         <module>dubbo-xds</module>
         <module>dubbo-kubernetes</module>
+        <module>dubbo-wasm</module>
+        <module>dubbo-proxy-extensions</module>
     </modules>
 
     <properties>
-        <revision>1.0.5-SNAPSHOT</revision>
+        <revision>3.2.1-SNAPSHOT</revision>
         <!-- Test libs -->
-        <junit_jupiter_version>5.6.0</junit_jupiter_version>
-        <awaitility_version>4.2.0</awaitility_version>
+        <junit_jupiter_version>5.9.3</junit_jupiter_version>
+        <junit_platform_commons>1.9.3</junit_platform_commons>
+        <awaitility_version>4.2.1</awaitility_version>
         <hazelcast_version>3.11.1</hazelcast_version>
         <hamcrest_version>2.2</hamcrest_version>
         <hibernate_validator_version>5.2.4.Final</hibernate_validator_version>
         <el_api_version>2.2.4</el_api_version>
         <jaxb_api_version>2.2.7</jaxb_api_version>
         <cglib_version>2.2</cglib_version>
-        <mockito_version>3.8.0</mockito_version>
+        <mockito_version>4.11.0</mockito_version>
         <!-- for maven compiler plugin -->
         <java_source_version>1.8</java_source_version>
         <java_target_version>1.8</java_target_version>
@@ -140,6 +143,7 @@
         <rat.skip>true</rat.skip>
         <jacoco.skip>false</jacoco.skip>
         <maven_flatten_version>1.2.5</maven_flatten_version>
+
     </properties>
 
     <dependencyManagement>
@@ -176,6 +180,18 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-commons</artifactId>
+            <version>${junit_platform_commons}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit_jupiter_version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-params</artifactId>
             <version>${junit_jupiter_version}</version>
@@ -200,6 +216,12 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+            <version>${mockito_version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>cglib</groupId>
             <artifactId>cglib-nodep</artifactId>
             <version>${cglib_version}</version>
@@ -365,6 +387,7 @@
                         <exclude>Jenkinsfile</exclude>
                         <exclude>**/codestyle/*</exclude>
                         <exclude>**/resources/META-INF/**</exclude>
+                        <exclude>**/resources/security/**</exclude>
                         <exclude>.github/**</exclude>
                         <exclude>compiler/**</exclude>
                         <exclude>**/generated/**</exclude>
diff --git a/test/dubbo-scenario-builder/pom.xml b/test/dubbo-scenario-builder/pom.xml
index 45b7855..11ef061 100644
--- a/test/dubbo-scenario-builder/pom.xml
+++ b/test/dubbo-scenario-builder/pom.xml
@@ -18,10 +18,12 @@
          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">
     <parent>
-        <artifactId>dubbo-extensions-test</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>dubbo-extensions-test</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
+
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>dubbo-scenario-builder</artifactId>
diff --git a/test/dubbo-test-runner/pom.xml b/test/dubbo-test-runner/pom.xml
index 2fc320d..fd4d09f 100644
--- a/test/dubbo-test-runner/pom.xml
+++ b/test/dubbo-test-runner/pom.xml
@@ -20,7 +20,8 @@
     <parent>
         <artifactId>dubbo-extensions-test</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
diff --git a/test/pom.xml b/test/pom.xml
index 6f30434..93eb50f 100644
--- a/test/pom.xml
+++ b/test/pom.xml
@@ -20,20 +20,30 @@
          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.dubbo.extensions</groupId>
+    <parent>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <artifactId>extensions-parent</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
     <artifactId>dubbo-extensions-test</artifactId>
     <packaging>pom</packaging>
-    <version>1.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
     <modules>
         <module>dubbo-test-runner</module>
         <module>dubbo-scenario-builder</module>
+        <module>scenarios</module>
     </modules>
 
-
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
+        <dubbo.version>3.2.11</dubbo.version>
+        <junit.version>4.13.1</junit.version>
+        <spring.version>4.3.30.RELEASE</spring.version>
     </properties>
 
+
 </project>
diff --git a/test/scenarios/pom.xml b/test/scenarios/pom.xml
index be93bf8..e50413d 100644
--- a/test/scenarios/pom.xml
+++ b/test/scenarios/pom.xml
@@ -17,11 +17,16 @@
 <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>
+    <parent>
+        <groupId>org.apache.dubbo.extensions</groupId>
+        <artifactId>dubbo-extensions-test</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
 
-    <groupId>org.apache.dubbo.extensions</groupId>
     <artifactId>dubbo-scenarios</artifactId>
     <packaging>pom</packaging>
-    <version>1.0.0-SNAPSHOT</version>
+    <version>${revision}</version>
 
     <modules>
         <module>scenarios-dubbo-serialization</module>
diff --git a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-avro-test/pom.xml b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-avro-test/pom.xml
index 7ea4941..8a74377 100644
--- a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-avro-test/pom.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-avro-test/pom.xml
@@ -18,9 +18,10 @@
          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">
     <parent>
-        <artifactId>scenarios-dubbo-serialization</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>scenarios-dubbo-serialization</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>dubbo-serialization-avro-test</artifactId>
@@ -28,10 +29,6 @@
     <properties>
         <source.level>1.8</source.level>
         <target.level>1.8</target.level>
-        <dubbo.version>3.0.4</dubbo.version>
-        <junit.version>4.13.1</junit.version>
-        <spring.version>4.3.30.RELEASE</spring.version>
-        <dubbo.serialization.version>1.0.2-SNAPSHOT</dubbo.serialization.version>
         <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
     </properties>
 
@@ -95,13 +92,13 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-avro</artifactId>
-            <version>${dubbo.serialization.version}</version>
+            <version>${revision}</version>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-testcase</artifactId>
-            <version>${project.version}</version>
+            <version>${revision}</version>
         </dependency>
 
     </dependencies>
diff --git a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fastjson-test/pom.xml b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fastjson-test/pom.xml
index cf8abd8..3444299 100644
--- a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fastjson-test/pom.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fastjson-test/pom.xml
@@ -18,9 +18,10 @@
          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">
     <parent>
-        <artifactId>scenarios-dubbo-serialization</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>scenarios-dubbo-serialization</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -29,80 +30,48 @@
     <properties>
         <source.level>1.8</source.level>
         <target.level>1.8</target.level>
-        <dubbo.version>3.2.7</dubbo.version>
-        <junit.version>4.13.1</junit.version>
-        <spring.version>4.3.30.RELEASE</spring.version>
-        <dubbo.serialization.version>1.0.2-SNAPSHOT</dubbo.serialization.version>
         <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
     </properties>
 
-    <dependencyManagement>
-        <dependencies>
-            <dependency>
-                <groupId>org.springframework</groupId>
-                <artifactId>spring-framework-bom</artifactId>
-                <version>${spring.version}</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-bom</artifactId>
-                <version>${dubbo.version}</version>
-                <type>pom</type>
-                <scope>import</scope>
-            </dependency>
-
-            <dependency>
-                <groupId>org.apache.dubbo</groupId>
-                <artifactId>dubbo-dependencies-zookeeper</artifactId>
-                <version>${dubbo.version}</version>
-                <type>pom</type>
-            </dependency>
-
-            <dependency>
-                <groupId>junit</groupId>
-                <artifactId>junit</artifactId>
-                <version>${junit.version}</version>
-            </dependency>
-        </dependencies>
-    </dependencyManagement>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo</artifactId>
+            <version>${dubbo.version}</version>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-dependencies-zookeeper</artifactId>
+            <version>${dubbo.version}</version>
             <type>pom</type>
         </dependency>
 
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
+            <version>${junit.version}</version>
             <scope>test</scope>
         </dependency>
 
         <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring-test</artifactId>
+            <version>${spring.version}</version>
             <scope>test</scope>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-fastjson</artifactId>
-            <version>${dubbo.serialization.version}</version>
+            <version>${revision}</version>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-testcase</artifactId>
-            <version>${project.version}</version>
+            <version>${revision}</version>
         </dependency>
 
     </dependencies>
diff --git a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fst-test/pom.xml b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fst-test/pom.xml
index 77b2957..6e6a6d5 100644
--- a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fst-test/pom.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-fst-test/pom.xml
@@ -18,9 +18,10 @@
          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">
     <parent>
-        <artifactId>scenarios-dubbo-serialization</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>scenarios-dubbo-serialization</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -29,10 +30,6 @@
     <properties>
         <source.level>1.8</source.level>
         <target.level>1.8</target.level>
-        <dubbo.version>3.0.4</dubbo.version>
-        <junit.version>4.13.1</junit.version>
-        <spring.version>4.3.30.RELEASE</spring.version>
-        <dubbo.serialization.version>1.0.2-SNAPSHOT</dubbo.serialization.version>
         <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
     </properties>
 
@@ -96,7 +93,7 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-fst</artifactId>
-            <version>${dubbo.serialization.version}</version>
+            <version>${revision}</version>
             <exclusions>
                 <exclusion>
                     <artifactId>jackson-core</artifactId>
diff --git a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-gson-test/pom.xml b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-gson-test/pom.xml
index 069e982..c5e1615 100644
--- a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-gson-test/pom.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-gson-test/pom.xml
@@ -18,9 +18,10 @@
          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">
     <parent>
-        <artifactId>scenarios-dubbo-serialization</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>scenarios-dubbo-serialization</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -29,10 +30,6 @@
     <properties>
         <source.level>1.8</source.level>
         <target.level>1.8</target.level>
-        <dubbo.version>3.0.4</dubbo.version>
-        <junit.version>4.13.1</junit.version>
-        <spring.version>4.3.30.RELEASE</spring.version>
-        <dubbo.serialization.version>1.0.2-SNAPSHOT</dubbo.serialization.version>
         <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
     </properties>
 
@@ -96,13 +93,13 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-gson</artifactId>
-            <version>${dubbo.serialization.version}</version>
+            <version>${revision}</version>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-testcase</artifactId>
-            <version>${project.version}</version>
+            <version>${revision}</version>
         </dependency>
 
     </dependencies>
diff --git a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-kryo-test/pom.xml b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-kryo-test/pom.xml
index c9a4ea3..8855774 100644
--- a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-kryo-test/pom.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-kryo-test/pom.xml
@@ -18,9 +18,10 @@
          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">
     <parent>
-        <artifactId>scenarios-dubbo-serialization</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>scenarios-dubbo-serialization</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -29,11 +30,6 @@
     <properties>
         <source.level>1.8</source.level>
         <target.level>1.8</target.level>
-        <dubbo.version>3.0.4</dubbo.version>
-        <junit.version>4.13.1</junit.version>
-        <spring.version>4.3.30.RELEASE</spring.version>
-        <dubbo.extensions.version>1.0.5-SNAPSHOT</dubbo.extensions.version>
-        <dubbo.serialization.version>1.0.2-SNAPSHOT</dubbo.serialization.version>
         <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
     </properties>
 
@@ -50,7 +46,7 @@
             <dependency>
                 <groupId>org.apache.dubbo.extensions</groupId>
                 <artifactId>dubbo-extensions-dependencies-bom</artifactId>
-                <version>${dubbo.extensions.version}</version>
+                <version>${revision}</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
@@ -105,13 +101,13 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-kryo</artifactId>
-            <version>${dubbo.serialization.version}</version>
+            <version>${revision}</version>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-testcase</artifactId>
-            <version>${project.version}</version>
+            <version>${revision}</version>
         </dependency>
 
     </dependencies>
diff --git a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protobuf-test/pom.xml b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protobuf-test/pom.xml
index 2c14f1f..768a362 100644
--- a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protobuf-test/pom.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protobuf-test/pom.xml
@@ -18,9 +18,10 @@
          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">
     <parent>
-        <artifactId>scenarios-dubbo-serialization</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>scenarios-dubbo-serialization</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -29,11 +30,6 @@
     <properties>
         <source.level>1.8</source.level>
         <target.level>1.8</target.level>
-        <dubbo.version>3.0.4</dubbo.version>
-        <junit.version>4.13.1</junit.version>
-        <spring.version>4.3.30.RELEASE</spring.version>
-        <dubbo.compiler.version>0.0.2</dubbo.compiler.version>
-        <dubbo.serialization.version>1.0.2-SNAPSHOT</dubbo.serialization.version>
         <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
     </properties>
 
@@ -97,7 +93,7 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-protobuf</artifactId>
-            <version>${dubbo.serialization.version}</version>
+            <version>${revision}</version>
         </dependency>
 
     </dependencies>
@@ -168,11 +164,11 @@
                     <clearOutputDirectory>false</clearOutputDirectory>
                     <protocPlugins>
                         <protocPlugin>
-                            <id>dubbo</id>
+                            <id>dubbo-triple</id>
                             <groupId>org.apache.dubbo</groupId>
                             <artifactId>dubbo-compiler</artifactId>
-                            <version>${dubbo.compiler.version}</version>
-                            <mainClass>org.apache.dubbo.gen.dubbo.Dubbo3Generator</mainClass>
+                            <version>${dubbo.version}</version>
+                            <mainClass>org.apache.dubbo.gen.tri.Dubbo3TripleGenerator</mainClass>
                         </protocPlugin>
                     </protocPlugins>
                 </configuration>
diff --git a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protobuf-test/src/main/resources/spring/dubbo-demo-provider.xml b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protobuf-test/src/main/resources/spring/dubbo-demo-provider.xml
index a0fa283..d11b407 100644
--- a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protobuf-test/src/main/resources/spring/dubbo-demo-provider.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protobuf-test/src/main/resources/spring/dubbo-demo-provider.xml
@@ -30,6 +30,6 @@
     <bean id="demoService" class="org.apache.dubbo.test.serialization.protobuf.DemoServiceImpl"/>
 
     <dubbo:service interface="org.apache.dubbo.test.serialization.protobuf.DemoService" ref="demoService"
-                   serialization="protobuf"/>
+                   serialization="protobuf-json"/>
 
 </beans>
diff --git a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protostuff-test/pom.xml b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protostuff-test/pom.xml
index e5fc135..2bd5324 100644
--- a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protostuff-test/pom.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-protostuff-test/pom.xml
@@ -18,9 +18,10 @@
          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">
     <parent>
-        <artifactId>scenarios-dubbo-serialization</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>scenarios-dubbo-serialization</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -29,10 +30,6 @@
     <properties>
         <source.level>1.8</source.level>
         <target.level>1.8</target.level>
-        <dubbo.version>3.0.4</dubbo.version>
-        <junit.version>4.13.1</junit.version>
-        <spring.version>4.3.30.RELEASE</spring.version>
-        <dubbo.serialization.version>1.0.2-SNAPSHOT</dubbo.serialization.version>
         <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
     </properties>
 
@@ -96,13 +93,13 @@
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-protostuff</artifactId>
-            <version>${dubbo.serialization.version}</version>
+            <version>${revision}</version>
         </dependency>
 
         <dependency>
             <groupId>org.apache.dubbo.extensions</groupId>
             <artifactId>dubbo-serialization-testcase</artifactId>
-            <version>${project.version}</version>
+            <version>${revision}</version>
         </dependency>
 
     </dependencies>
diff --git a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-testcase/pom.xml b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-testcase/pom.xml
index df2ef83..da3859d 100644
--- a/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-testcase/pom.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/dubbo-serialization-testcase/pom.xml
@@ -18,9 +18,10 @@
          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">
     <parent>
-        <artifactId>scenarios-dubbo-serialization</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>scenarios-dubbo-serialization</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
diff --git a/test/scenarios/scenarios-dubbo-serialization/pom.xml b/test/scenarios/scenarios-dubbo-serialization/pom.xml
index 94273db..37ea2af 100644
--- a/test/scenarios/scenarios-dubbo-serialization/pom.xml
+++ b/test/scenarios/scenarios-dubbo-serialization/pom.xml
@@ -18,12 +18,16 @@
          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">
     <parent>
-        <artifactId>dubbo-scenarios</artifactId>
         <groupId>org.apache.dubbo.extensions</groupId>
-        <version>1.0.0-SNAPSHOT</version>
+        <artifactId>dubbo-scenarios</artifactId>
+        <version>${revision}</version>
+        <relativePath>../pom.xml</relativePath>
     </parent>
+
     <modelVersion>4.0.0</modelVersion>
+    <artifactId>scenarios-dubbo-serialization</artifactId>
     <packaging>pom</packaging>
+
     <modules>
         <module>dubbo-serialization-avro-test</module>
         <module>dubbo-serialization-gson-test</module>
@@ -34,7 +38,6 @@
         <module>dubbo-serialization-protostuff-test</module>
         <module>dubbo-serialization-testcase</module>
     </modules>
-    <artifactId>scenarios-dubbo-serialization</artifactId>
 
     <properties>
         <maven.compiler.source>8</maven.compiler.source>