You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by wu...@apache.org on 2018/04/04 07:45:58 UTC

[incubator-servicecomb-java-chassis] branch master updated: SCB-379 support AsyncRestTemplate

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

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a77e34  SCB-379 support AsyncRestTemplate
8a77e34 is described below

commit 8a77e34365e786462b74826c724d10f0e735d5fe
Author: lijasonvip <li...@huawei.com>
AuthorDate: Wed Mar 14 16:16:40 2018 +0800

    SCB-379 support AsyncRestTemplate
    
    Signed-off-by: lijasonvip <li...@huawei.com>
---
 .../tests/SpringMvcIntegrationTestBase.java        | 248 +++++++++++++++++++--
 .../springmvc/reference/CseClientHttpRequest.java  |  47 +++-
 .../reference/async/CseAsyncClientHttpRequest.java |  83 +++++++
 .../async/CseAsyncClientHttpRequestFactory.java    |  31 +++
 .../reference/async/CseAsyncRequestCallback.java   |  47 ++++
 .../reference/async/CseAsyncRestTemplate.java      |  47 ++++
 .../CseAsyncClientHttpRequestFactoryTest.java      |  33 +++
 .../async/CseAsyncClientHttpRequestTest.java       | 159 +++++++++++++
 .../async/CseAsyncRequestCallbackTest.java         |  55 +++++
 .../reference/async/CseAsyncRestTemplateTest.java  |  29 +++
 .../springmvc/consumer/SpringmvcConsumerMain.java  |  39 +++-
 11 files changed, 786 insertions(+), 32 deletions(-)

diff --git a/integration-tests/springmvc-tests/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java b/integration-tests/springmvc-tests/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java
index 1d2e1ba..a00dbbe 100644
--- a/integration-tests/springmvc-tests/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java
+++ b/integration-tests/springmvc-tests/src/test/java/org/apache/servicecomb/demo/springmvc/tests/SpringMvcIntegrationTestBase.java
@@ -50,6 +50,7 @@ import org.apache.servicecomb.common.rest.codec.RestObjectMapper;
 import org.apache.servicecomb.demo.compute.Person;
 import org.apache.servicecomb.demo.server.User;
 import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder;
+import org.apache.servicecomb.provider.springmvc.reference.async.CseAsyncRestTemplate;
 import org.junit.ClassRule;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -65,6 +66,9 @@ import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
+import org.springframework.util.concurrent.ListenableFuture;
+import org.springframework.util.concurrent.ListenableFutureCallback;
+import org.springframework.web.client.AsyncRestTemplate;
 import org.springframework.web.client.RestClientException;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.client.UnknownHttpStatusCodeException;
@@ -78,6 +82,8 @@ public class SpringMvcIntegrationTestBase {
 
   private final RestTemplate restTemplate = new RestTemplate();
 
+  private final AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate();
+
   private final String codeFirstUrl = baseUrl + "codeFirstSpringmvc/";
 
   private final String controllerUrl = baseUrl + "springmvc/controller/";
@@ -85,11 +91,12 @@ public class SpringMvcIntegrationTestBase {
   static void setUpLocalRegistry() {
     ClassLoader loader = Thread.currentThread().getContextClassLoader();
     URL resource = loader.getResource("registry.yaml");
+    assert resource != null;
     System.setProperty(LOCAL_REGISTRY_FILE_KEY, resource.getPath());
   }
 
   @Test
-  public void ableToQueryAtRootBasePath() {
+  public void ableToQueryAtRootBasePath() throws Exception {
     ResponseEntity<String> responseEntity = restTemplate
         .getForEntity(baseUrl + "sayHi?name=Mike", String.class);
 
@@ -101,28 +108,51 @@ public class SpringMvcIntegrationTestBase {
 
     assertThat(responseEntity.getStatusCode(), is(OK));
     assertThat(responseEntity.getBody(), is("Hi 小 强"));
+
+    //integration test for AsyncRestTemplate
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .getForEntity(baseUrl + "sayHi?name=Mike", String.class);
+    ResponseEntity<String> futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getStatusCode(), is(OK));
+    assertThat(futureResponse.getBody(), is("Hi Mike"));
+
+    listenableFuture = asyncRestTemplate.getForEntity(baseUrl + "sayHi?name={name}", String.class, "小 强");
+    futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getStatusCode(), is(OK));
+    assertThat(futureResponse.getBody(), is("Hi 小 强"));
   }
 
   @Test
-  public void ableToQueryAtRootPath() {
+  public void ableToQueryAtRootPath() throws Exception {
     ResponseEntity<String> responseEntity = restTemplate
         .getForEntity(baseUrl, String.class);
 
     assertThat(responseEntity.getStatusCode(), is(OK));
     assertThat(responseEntity.getBody(), is("Welcome home"));
+
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate.getForEntity(baseUrl, String.class);
+    ResponseEntity<String> futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getStatusCode(), is(OK));
+    assertThat(futureResponse.getBody(), is("Welcome home"));
   }
 
   @Test
-  public void ableToQueryAtNonRootPath() {
+  public void ableToQueryAtNonRootPath() throws Exception {
     ResponseEntity<String> responseEntity = restTemplate
         .getForEntity(baseUrl + "french/bonjour?name=Mike", String.class);
 
     assertThat(responseEntity.getStatusCode(), is(OK));
     assertThat(responseEntity.getBody(), is("Bonjour Mike"));
+
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .getForEntity(baseUrl + "french/bonjour?name=Mike", String.class);
+    ResponseEntity<String> futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getStatusCode(), is(OK));
+    assertThat(futureResponse.getBody(), is("Bonjour Mike"));
   }
 
   @Test
-  public void ableToPostMap() {
+  public void ableToPostMap() throws Exception {
     Map<String, User> users = new HashMap<>();
     users.put("user1", userOfNames("name11", "name12"));
     users.put("user2", userOfNames("name21", "name22"));
@@ -139,6 +169,17 @@ public class SpringMvcIntegrationTestBase {
     Map<String, User> body = responseEntity.getBody();
     assertArrayEquals(body.get("user1").getNames(), new String[] {"name11", "name12"});
     assertArrayEquals(body.get("user2").getNames(), new String[] {"name21", "name22"});
+
+    ListenableFuture<ResponseEntity<Map<String, User>>> listenableFuture = asyncRestTemplate
+        .exchange(codeFirstUrl + "testUserMap",
+            POST,
+            jsonRequest(users),
+            reference);
+    ResponseEntity<Map<String, User>> futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getStatusCode(), is(OK));
+    body = futureResponse.getBody();
+    assertArrayEquals(body.get("user1").getNames(), new String[] {"name11", "name12"});
+    assertArrayEquals(body.get("user2").getNames(), new String[] {"name21", "name22"});
   }
 
   private User userOfNames(String... names) {
@@ -148,19 +189,24 @@ public class SpringMvcIntegrationTestBase {
   }
 
   @Test
-  public void ableToConsumeTextPlain() {
+  public void ableToConsumeTextPlain() throws Exception {
     String body = "a=1";
-
     String result = restTemplate.postForObject(
         codeFirstUrl + "textPlain",
         body,
         String.class);
 
     assertThat(jsonOf(result, String.class), is(body));
+
+    HttpEntity<?> entity = new HttpEntity<>(body);
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .postForEntity(codeFirstUrl + "textPlain", entity, String.class);
+    ResponseEntity<String> responseEntity = listenableFuture.get();
+    assertThat(jsonOf(responseEntity.getBody(), String.class), is(body));
   }
 
   @Test
-  public void ableToPostBytes() throws IOException {
+  public void ableToPostBytes() throws Exception {
     byte[] body = new byte[] {0, 1, 2};
 
     byte[] result = restTemplate.postForObject(
@@ -174,10 +220,21 @@ public class SpringMvcIntegrationTestBase {
     assertEquals(1, result[1]);
     assertEquals(2, result[2]);
     assertEquals(3, result.length);
+
+    ListenableFuture<ResponseEntity<byte[]>> listenableFuture = asyncRestTemplate
+        .postForEntity(codeFirstUrl + "bytes",
+            jsonRequest(RestObjectMapper.INSTANCE.writeValueAsBytes(body)),
+            byte[].class);
+    ResponseEntity<byte[]> responseEntity = listenableFuture.get();
+    result = RestObjectMapper.INSTANCE.readValue(responseEntity.getBody(), byte[].class);
+    assertEquals(1, result[0]);
+    assertEquals(1, result[1]);
+    assertEquals(2, result[2]);
+    assertEquals(3, result.length);
   }
 
   @Test
-  public void ableToUploadFile() throws IOException {
+  public void ableToUploadFile() throws Exception {
     String file1Content = "hello world";
     String file2Content = "bonjour";
     String username = "mike";
@@ -195,10 +252,17 @@ public class SpringMvcIntegrationTestBase {
         String.class);
 
     assertThat(result, is(file1Content + file2Content + username));
+
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .postForEntity(codeFirstUrl + "upload",
+            new HttpEntity<>(map, headers),
+            String.class);
+    ResponseEntity<String> responseEntity = listenableFuture.get();
+    assertThat(responseEntity.getBody(), is(file1Content + file2Content + username));
   }
 
   @Test
-  public void ableToUploadFileFromConsumer() throws IOException {
+  public void ableToUploadFileFromConsumer() throws Exception {
     String file1Content = "hello world";
     String file2Content = "bonjour";
     String username = "mike";
@@ -216,10 +280,17 @@ public class SpringMvcIntegrationTestBase {
         String.class);
 
     assertThat(result, is(file1Content + file2Content + username));
+    AsyncRestTemplate cseAsyncRestTemplate = new CseAsyncRestTemplate();
+    ListenableFuture<ResponseEntity<String>> listenableFuture = cseAsyncRestTemplate
+        .postForEntity("cse://springmvc-tests/codeFirstSpringmvc/upload",
+            new HttpEntity<>(map, headers),
+            String.class);
+    ResponseEntity<String> responseEntity = listenableFuture.get();
+    assertThat(responseEntity.getBody(), is(file1Content + file2Content + username));
   }
 
   @Test
-  public void ableToUploadFileWithoutAnnotation() throws IOException {
+  public void ableToUploadFileWithoutAnnotation() throws Exception {
     String file1Content = "hello world";
     String file2Content = "bonjour";
     String username = "mike";
@@ -237,10 +308,17 @@ public class SpringMvcIntegrationTestBase {
         String.class);
 
     assertThat(result, is(file1Content + file2Content + username));
+
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .postForEntity(codeFirstUrl + "uploadWithoutAnnotation",
+            new HttpEntity<>(map, headers),
+            String.class);
+    ResponseEntity<String> responseEntity = listenableFuture.get();
+    assertThat(responseEntity.getBody(), is(file1Content + file2Content + username));
   }
 
   @Test
-  public void blowsUpWhenFileNameDoesNotMatch() throws IOException {
+  public void blowsUpWhenFileNameDoesNotMatch() throws Exception {
     String file1Content = "hello world";
     String file2Content = "bonjour";
 
@@ -277,44 +355,71 @@ public class SpringMvcIntegrationTestBase {
         seconds);
 
     assertThat(result, is(Date.from(date.plusSeconds(seconds).toInstant())));
+
+    ListenableFuture<ResponseEntity<Date>> listenableFuture = asyncRestTemplate
+        .postForEntity(codeFirstUrl + "addDate?seconds={seconds}",
+            new HttpEntity<>(body, headers),
+            Date.class,
+            seconds);
+    ResponseEntity<Date> dateResponseEntity = listenableFuture.get();
+    assertThat(dateResponseEntity.getBody(), is(Date.from(date.plusSeconds(seconds).toInstant())));
   }
 
   @Test
-  public void ableToDeleteWithQueryString() {
+  public void ableToDeleteWithQueryString() throws Exception {
     ResponseEntity<String> responseEntity = restTemplate.exchange(codeFirstUrl + "addstring?s=a&s=b",
         HttpMethod.DELETE,
         null,
         String.class);
 
     assertThat(responseEntity.getBody(), is("ab"));
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .exchange(codeFirstUrl + "addstring?s=a&s=b", HttpMethod.DELETE, null, String.class);
+    ResponseEntity<String> futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getBody(), is("ab"));
   }
 
   @Test
-  public void ableToGetBoolean() {
+  public void ableToGetBoolean() throws Exception {
     boolean result = restTemplate.getForObject(codeFirstUrl + "istrue", boolean.class);
 
     assertThat(result, is(true));
+
+    ListenableFuture<ResponseEntity<Boolean>> listenableFuture = asyncRestTemplate
+        .getForEntity(codeFirstUrl + "istrue", boolean.class);
+    ResponseEntity<Boolean> futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getBody(), is(true));
   }
 
   @Test
-  public void putsEndWithPathParam() {
+  public void putsEndWithPathParam() throws Exception {
     ResponseEntity<String> responseEntity = restTemplate
         .exchange(codeFirstUrl + "sayhi/{name}", PUT, null, String.class, "world");
 
     assertThat(responseEntity.getStatusCode(), is(ACCEPTED));
     assertThat(jsonOf(responseEntity.getBody(), String.class), is("world sayhi"));
+
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .exchange(codeFirstUrl + "sayhi/{name}", PUT, null, String.class, "world");
+    ResponseEntity<String> futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getStatusCode(), is(ACCEPTED));
+    assertThat(jsonOf(futureResponse.getBody(), String.class), is("world sayhi"));
   }
 
   @Test
-  public void putsContainingPathParam() {
+  public void putsContainingPathParam() throws Exception {
     ResponseEntity<String> responseEntity = restTemplate
         .exchange(codeFirstUrl + "sayhi/{name}/v2", PUT, null, String.class, "world");
 
     assertThat(jsonOf(responseEntity.getBody(), String.class), is("world sayhi 2"));
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .exchange(codeFirstUrl + "sayhi/{name}/v2", PUT, null, String.class, "world");
+    responseEntity = listenableFuture.get();
+    assertThat(jsonOf(responseEntity.getBody(), String.class), is("world sayhi 2"));
   }
 
   @Test
-  public void ableToPostWithHeader() {
+  public void ableToPostWithHeader() throws Exception {
     Person person = new Person();
     person.setName("person name");
 
@@ -327,10 +432,15 @@ public class SpringMvcIntegrationTestBase {
         .postForEntity(codeFirstUrl + "saysomething", requestEntity, String.class);
 
     assertThat(jsonOf(responseEntity.getBody(), String.class), is("prefix  prefix person name"));
+
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .postForEntity(codeFirstUrl + "saysomething", requestEntity, String.class);
+    responseEntity = listenableFuture.get();
+    assertThat(jsonOf(responseEntity.getBody(), String.class), is("prefix  prefix person name"));
   }
 
   @Test
-  public void ableToPostObjectAsJson() {
+  public void ableToPostObjectAsJson() throws Exception {
     Map<String, String> personFieldMap = new HashMap<>();
     personFieldMap.put("name", "person name from map");
 
@@ -343,10 +453,21 @@ public class SpringMvcIntegrationTestBase {
     person = restTemplate.postForObject(codeFirstUrl + "sayhello", jsonRequest(input), Person.class);
 
     assertThat(person.toString(), is("hello person name from Object"));
+
+    ListenableFuture<ResponseEntity<Person>> listenableFuture = asyncRestTemplate
+        .postForEntity(codeFirstUrl + "sayhello", jsonRequest(personFieldMap), Person.class);
+    ResponseEntity<Person> futureResponse = listenableFuture.get();
+    person = futureResponse.getBody();
+    assertThat(person.toString(), is("hello person name from map"));
+
+    listenableFuture = asyncRestTemplate.postForEntity(codeFirstUrl + "sayhello", jsonRequest(input), Person.class);
+    futureResponse = listenableFuture.get();
+    person = futureResponse.getBody();
+    assertThat(person.toString(), is("hello person name from Object"));
   }
 
   @Test
-  public void ableToPostForm() {
+  public void ableToPostForm() throws Exception {
     MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
     params.add("a", "5");
     params.add("b", "3");
@@ -357,10 +478,15 @@ public class SpringMvcIntegrationTestBase {
         .postForObject(codeFirstUrl + "add", new HttpEntity<>(params, headers), Integer.class);
 
     assertThat(result, is(8));
+
+    ListenableFuture<ResponseEntity<Integer>> listenableFuture = asyncRestTemplate
+        .postForEntity(codeFirstUrl + "add", new HttpEntity<>(params, headers), Integer.class);
+    ResponseEntity<Integer> futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getBody(), is(8));
   }
 
   @Test
-  public void ableToExchangeCookie() {
+  public void ableToExchangeCookie() throws Exception {
     Map<String, String> params = new HashMap<>();
     params.put("a", "5");
 
@@ -376,10 +502,20 @@ public class SpringMvcIntegrationTestBase {
         params);
 
     assertThat(result.getBody(), is(2));
+
+    ListenableFuture<ResponseEntity<Integer>> listenableFuture = asyncRestTemplate
+        .exchange(codeFirstUrl + "reduce?a={a}",
+            GET,
+            requestEntity,
+            Integer.class,
+            params);
+    result = listenableFuture.get();
+    assertThat(result.getBody(), is(2));
+
   }
 
   @Test
-  public void getsEndWithRequestVariables() {
+  public void getsEndWithRequestVariables() throws Exception {
     int result = restTemplate.getForObject(
         controllerUrl + "add?a={a}&b={b}",
         Integer.class,
@@ -387,10 +523,17 @@ public class SpringMvcIntegrationTestBase {
         4);
 
     assertThat(result, is(7));
+    ListenableFuture<ResponseEntity<Integer>> listenableFuture = asyncRestTemplate
+        .getForEntity(controllerUrl + "add?a={a}&b={b}",
+            Integer.class,
+            3,
+            4);
+    ResponseEntity<Integer> futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getBody(), is(7));
   }
 
   @Test
-  public void postsEndWithPathParam() {
+  public void postsEndWithPathParam() throws Exception {
     String result = restTemplate.postForObject(
         controllerUrl + "sayhello/{name}",
         null,
@@ -411,10 +554,26 @@ public class SpringMvcIntegrationTestBase {
 
     assertThat(result, is("hello 中 国"));
     restTemplate.setMessageConverters(convertersOld);
+
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .postForEntity(controllerUrl + "sayhello/{name}",
+            null,
+            String.class,
+            "world");
+    ResponseEntity<String> futureResonse = listenableFuture.get();
+    assertThat(jsonOf(futureResonse.getBody(), String.class), is("hello world"));
+    asyncRestTemplate.setMessageConverters(converters);
+    listenableFuture = asyncRestTemplate.postForEntity(controllerUrl + "sayhello/{name}",
+        null,
+        String.class,
+        "中 国");
+    futureResonse = listenableFuture.get();
+    assertThat(futureResonse.getBody(), is("hello 中 国"));
+    asyncRestTemplate.setMessageConverters(convertersOld);
   }
 
   @Test
-  public void ableToPostObjectAsJsonWithRequestVariable() {
+  public void ableToPostObjectAsJsonWithRequestVariable() throws Exception {
     Person input = new Person();
     input.setName("world");
 
@@ -441,15 +600,39 @@ public class SpringMvcIntegrationTestBase {
 
     assertThat(result, is("hello 中国"));
     restTemplate.setMessageConverters(convertersOld);
+
+    input.setName("world");
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .postForEntity(controllerUrl + "saysomething?prefix={prefix}",
+            jsonRequest(input),
+            String.class,
+            "hello");
+    ResponseEntity<String> futureResponse = listenableFuture.get();
+    assertThat(jsonOf(futureResponse.getBody(), String.class), is("hello world"));
+
+    asyncRestTemplate.setMessageConverters(converters);
+    input.setName("中国");
+    listenableFuture = asyncRestTemplate.postForEntity(controllerUrl + "saysomething?prefix={prefix}",
+        jsonRequest(input),
+        String.class,
+        "hello");
+    futureResponse = listenableFuture.get();
+    assertThat(futureResponse.getBody(), is("hello 中国"));
+    asyncRestTemplate.setMessageConverters(convertersOld);
   }
 
   @Test
-  public void ensureServerWorksFine() {
+  public void ensureServerWorksFine() throws Exception {
     String result = restTemplate.getForObject(
         controllerUrl + "sayhi?name=world",
         String.class);
 
     assertThat(jsonOf(result, String.class), is("hi world [world]"));
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate
+        .getForEntity(controllerUrl + "sayhi?name=world",
+            String.class);
+    ResponseEntity<String> futureResponse = listenableFuture.get();
+    assertThat(jsonOf(futureResponse.getBody(), String.class), is("hi world [world]"));
   }
 
   @Test
@@ -477,6 +660,25 @@ public class SpringMvcIntegrationTestBase {
         String.class);
 
     assertThat(jsonOf(result.getBody(), String.class), is("hei world"));
+
+    ListenableFuture<ResponseEntity<String>> listenableFuture = asyncRestTemplate.exchange(controllerUrl + "sayhei",
+        GET,
+        requestEntity,
+        String.class);
+//    ResponseEntity<String> responseEntity = listenableFuture.get();
+    listenableFuture.addCallback(
+        new ListenableFutureCallback<ResponseEntity<String>>() {
+          @Override
+          public void onFailure(Throwable ex) {
+          }
+
+          @Override
+          public void onSuccess(ResponseEntity<String> result) {
+            assertThat(jsonOf(result.getBody(), String.class), is("hei world"));
+          }
+        }
+    );
+
   }
 
   private <T> HttpEntity<T> jsonRequest(T body) {
diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java
index 2ff0ca5..947469d 100644
--- a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java
+++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java
@@ -17,7 +17,6 @@
 
 package org.apache.servicecomb.provider.springmvc.reference;
 
-import java.io.IOException;
 import java.io.OutputStream;
 import java.net.URI;
 import java.util.List;
@@ -73,6 +72,34 @@ public class CseClientHttpRequest implements ClientHttpRequest {
     this.method = method;
   }
 
+  public String getPath() {
+    return path;
+  }
+
+  public void setPath(String path) {
+    this.path = path;
+  }
+
+  protected RequestMeta getRequestMeta() {
+    return requestMeta;
+  }
+
+  protected void setRequestMeta(RequestMeta requestMeta) {
+    this.requestMeta = requestMeta;
+  }
+
+  public void setUri(URI uri) {
+    this.uri = uri;
+  }
+
+  public void setMethod(HttpMethod method) {
+    this.method = method;
+  }
+
+  protected void setQueryParams(Map<String, List<String>> queryParams) {
+    this.queryParams = queryParams;
+  }
+
   public InvocationContext getContext() {
     return context;
   }
@@ -101,12 +128,12 @@ public class CseClientHttpRequest implements ClientHttpRequest {
   }
 
   @Override
-  public OutputStream getBody() throws IOException {
+  public OutputStream getBody() {
     return null;
   }
 
   @Override
-  public ClientHttpResponse execute() throws IOException {
+  public ClientHttpResponse execute() {
     path = findUriPath(uri);
     requestMeta = createRequestMeta(method.name(), uri);
 
@@ -119,7 +146,7 @@ public class CseClientHttpRequest implements ClientHttpRequest {
     return this.invoke(args);
   }
 
-  private RequestMeta createRequestMeta(String httpMetod, URI uri) {
+  protected RequestMeta createRequestMeta(String httpMetod, URI uri) {
     String microserviceName = uri.getAuthority();
     ReferenceConfig referenceConfig = ReferenceConfigUtils.getForInvoke(microserviceName);
 
@@ -142,7 +169,7 @@ public class CseClientHttpRequest implements ClientHttpRequest {
     return uri.getRawPath();
   }
 
-  private CseClientHttpResponse invoke(Object[] args) {
+  protected Invocation prepareInvocation(Object[] args) {
     Invocation invocation =
         InvocationFactory.forConsumer(requestMeta.getReferenceConfig(),
             requestMeta.getOperationMeta(),
@@ -153,22 +180,26 @@ public class CseClientHttpRequest implements ClientHttpRequest {
     if (context != null) {
       invocation.addContext(context);
     }
-
     invocation.getHandlerContext().put(RestConst.CONSUMER_HEADER, httpHeaders);
+    return invocation;
+  }
+
+  private CseClientHttpResponse invoke(Object[] args) {
+    Invocation invocation = prepareInvocation(args);
     Response response = doInvoke(invocation);
 
     if (response.isSuccessed()) {
       return new CseClientHttpResponse(response);
     }
 
-    throw ExceptionFactory.convertConsumerException((Throwable) response.getResult());
+    throw ExceptionFactory.convertConsumerException(response.getResult());
   }
 
   protected Response doInvoke(Invocation invocation) {
     return InvokerUtils.innerSyncInvoke(invocation);
   }
 
-  private Object[] collectArguments() {
+  protected Object[] collectArguments() {
     HttpServletRequest mockRequest = new CommonToHttpServletRequest(requestMeta.getPathParams(), queryParams,
         httpHeaders, requestBody, requestMeta.getSwaggerRestOperation().isFormData());
     return RestCodec.restToArgs(mockRequest, requestMeta.getSwaggerRestOperation());
diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequest.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequest.java
new file mode 100644
index 0000000..1be86dc
--- /dev/null
+++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.provider.springmvc.reference.async;
+
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+
+import org.apache.servicecomb.common.rest.RestConst;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.provider.consumer.InvokerUtils;
+import org.apache.servicecomb.provider.springmvc.reference.CseClientHttpRequest;
+import org.apache.servicecomb.provider.springmvc.reference.CseClientHttpResponse;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.AsyncClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.concurrent.CompletableToListenableFutureAdapter;
+import org.springframework.util.concurrent.ListenableFuture;
+
+import io.netty.handler.codec.http.QueryStringDecoder;
+
+public class CseAsyncClientHttpRequest extends CseClientHttpRequest implements AsyncClientHttpRequest {
+
+  CseAsyncClientHttpRequest() {
+  }
+
+  CseAsyncClientHttpRequest(URI uri, HttpMethod method) {
+    this.setUri(uri);
+    this.setMethod(method);
+  }
+
+  @Override
+  public OutputStream getBody() {
+    return null;
+  }
+
+  @SuppressWarnings("unchecked")
+  private ListenableFuture<ClientHttpResponse> invoke(Object[] args) {
+    Invocation invocation = prepareInvocation(args);
+    invocation.getHandlerContext().put(RestConst.CONSUMER_HEADER, this.getHeaders());
+    CompletableFuture<ClientHttpResponse> clientHttpResponseCompletableFuture = doAsyncInvoke(invocation);
+    return new CompletableToListenableFutureAdapter(clientHttpResponseCompletableFuture);
+  }
+
+  protected CompletableFuture<ClientHttpResponse> doAsyncInvoke(Invocation invocation) {
+    CompletableFuture<ClientHttpResponse> completableFuture = new CompletableFuture<>();
+    InvokerUtils.reactiveInvoke(invocation, (Response response) -> {
+      if (response.isSuccessed()) {
+        completableFuture.complete(new CseClientHttpResponse(response));
+      } else {
+        completableFuture.completeExceptionally(response.getResult());
+      }
+    });
+    return completableFuture;
+  }
+
+
+  @Override
+  public ListenableFuture<ClientHttpResponse> executeAsync() {
+    this.setPath(findUriPath(this.getURI()));
+    this.setRequestMeta(createRequestMeta(this.getMethod().name(), this.getURI()));
+    QueryStringDecoder queryStringDecoder = new QueryStringDecoder(this.getURI().getRawSchemeSpecificPart());
+    this.setQueryParams(queryStringDecoder.parameters());
+    Object[] args = this.collectArguments();
+    return this.invoke(args);
+  }
+}
diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequestFactory.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequestFactory.java
new file mode 100644
index 0000000..d27cf78
--- /dev/null
+++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequestFactory.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.servicecomb.provider.springmvc.reference.async;
+
+import java.net.URI;
+
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.AsyncClientHttpRequest;
+import org.springframework.http.client.AsyncClientHttpRequestFactory;
+
+public class CseAsyncClientHttpRequestFactory implements AsyncClientHttpRequestFactory {
+  @Override
+  public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) {
+    return new CseAsyncClientHttpRequest(uri, httpMethod);
+  }
+}
diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRequestCallback.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRequestCallback.java
new file mode 100644
index 0000000..1ca4b4c
--- /dev/null
+++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRequestCallback.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.servicecomb.provider.springmvc.reference.async;
+
+import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.client.AsyncClientHttpRequest;
+import org.springframework.web.client.AsyncRequestCallback;
+
+public class CseAsyncRequestCallback<T> implements AsyncRequestCallback {
+  private HttpEntity<T> requestBody;
+
+  CseAsyncRequestCallback(HttpEntity<T> requestBody) {
+    this.requestBody = requestBody;
+  }
+
+  @Override
+  public void doWithRequest(AsyncClientHttpRequest request) {
+    CseAsyncClientHttpRequest cseAsyncClientHttpRequest = (CseAsyncClientHttpRequest) request;
+    if (requestBody != null) {
+      cseAsyncClientHttpRequest.setRequestBody(requestBody.getBody());
+    }
+
+    if (!CseHttpEntity.class.isInstance(requestBody)) {
+      return;
+    }
+
+    CseAsyncClientHttpRequest req = (CseAsyncClientHttpRequest) request;
+    CseHttpEntity<?> entity = (CseHttpEntity<?>) requestBody;
+    req.setContext(entity.getContext());
+  }
+}
diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRestTemplate.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRestTemplate.java
new file mode 100644
index 0000000..6f16f99
--- /dev/null
+++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRestTemplate.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.servicecomb.provider.springmvc.reference.async;
+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+
+import org.apache.servicecomb.provider.springmvc.reference.CseUriTemplateHandler;
+import org.springframework.http.HttpEntity;
+import org.springframework.web.client.AsyncRequestCallback;
+import org.springframework.web.client.AsyncRestTemplate;
+import org.springframework.web.client.CseHttpMessageConverter;
+
+public class CseAsyncRestTemplate extends AsyncRestTemplate {
+  public CseAsyncRestTemplate() {
+    setMessageConverters(Arrays.asList(new CseHttpMessageConverter()));
+    setAsyncRequestFactory(new CseAsyncClientHttpRequestFactory());
+    setUriTemplateHandler(new CseUriTemplateHandler());
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  protected <T> AsyncRequestCallback httpEntityCallback(HttpEntity<T> requestBody) {
+    return new CseAsyncRequestCallback(requestBody);
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  protected <T> AsyncRequestCallback httpEntityCallback(HttpEntity<T> requestBody, Type responseType) {
+    return new CseAsyncRequestCallback(requestBody);
+  }
+}
\ No newline at end of file
diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequestFactoryTest.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequestFactoryTest.java
new file mode 100644
index 0000000..ce10009
--- /dev/null
+++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequestFactoryTest.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.servicecomb.provider.springmvc.reference.async;
+
+import java.net.URI;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.http.HttpMethod;
+
+public class CseAsyncClientHttpRequestFactoryTest {
+  @Test
+  public void testCseAsyncClientHttpRequestFactory() {
+    CseAsyncClientHttpRequestFactory cseAsyncClientHttpRequestFactory = new CseAsyncClientHttpRequestFactory();
+    Assert.assertEquals(HttpMethod.GET,
+        cseAsyncClientHttpRequestFactory.createAsyncRequest(URI.create("/test"), HttpMethod.GET).getMethod());
+  }
+}
\ No newline at end of file
diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequestTest.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequestTest.java
new file mode 100644
index 0000000..4ae2bc7
--- /dev/null
+++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncClientHttpRequestTest.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.servicecomb.provider.springmvc.reference.async;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+
+import javax.xml.ws.Holder;
+
+import org.apache.servicecomb.common.rest.RestEngineSchemaListener;
+import org.apache.servicecomb.core.BootListener;
+import org.apache.servicecomb.core.CseContext;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.definition.SchemaMeta;
+import org.apache.servicecomb.core.provider.consumer.ReferenceConfigUtils;
+import org.apache.servicecomb.core.unittest.UnitTestMeta;
+import org.apache.servicecomb.provider.springmvc.reference.CseClientHttpResponse;
+import org.apache.servicecomb.serviceregistry.RegistryUtils;
+import org.apache.servicecomb.serviceregistry.ServiceRegistry;
+import org.apache.servicecomb.serviceregistry.registry.ServiceRegistryFactory;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.concurrent.ListenableFuture;
+import org.springframework.util.concurrent.ListenableFutureCallback;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+public class CseAsyncClientHttpRequestTest {
+
+  @Before
+  public void setup() {
+    ReferenceConfigUtils.setReady(true);
+  }
+
+  @After
+  public void teardown() {
+    ReferenceConfigUtils.setReady(false);
+  }
+
+  @RequestMapping(path = "SpringmvcImpl")
+  static class SpringmvcImpl {
+    @RequestMapping(path = "/bytes", method = RequestMethod.POST)
+    public byte[] bytes(@RequestBody byte[] input) {
+      input[0] = (byte) (input[0] + 1);
+      return input;
+    }
+  }
+
+  @Test
+  public void testNotReady() {
+    String exceptionMessage = "System is not ready for remote calls. "
+        + "When beans are making remote calls in initialization, it's better to "
+        + "implement " + BootListener.class.getName() + " and do it after EventType.AFTER_REGISTRY.";
+    ReferenceConfigUtils.setReady(false);
+    CseAsyncClientHttpRequest clientHttpRequest = new CseAsyncClientHttpRequest(URI.create("cse://app:test/"),
+        HttpMethod.POST);
+    try {
+      clientHttpRequest.executeAsync();
+    } catch (IllegalStateException e) {
+      Assert.assertEquals(exceptionMessage, e.getMessage());
+    }
+  }
+
+  @Test
+  public void testNormal() {
+    ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal();
+    serviceRegistry.init();
+    RegistryUtils.setServiceRegistry(serviceRegistry);
+    UnitTestMeta meta = new UnitTestMeta();
+    CseContext.getInstance()
+        .getSchemaListenerManager()
+        .setSchemaListenerList(Arrays.asList(new RestEngineSchemaListener()));
+
+    SchemaMeta schemaMeta = meta.getOrCreateSchemaMeta(CseAsyncClientHttpRequestTest.SpringmvcImpl.class);
+    CseContext.getInstance().getSchemaListenerManager().notifySchemaListener(schemaMeta);
+
+    Holder<Invocation> holder = new Holder<>();
+    CseAsyncClientHttpRequest client =
+        new CseAsyncClientHttpRequest(URI.create(
+            "cse://app:test/" + CseAsyncClientHttpRequestTest.SpringmvcImpl.class.getSimpleName() + "/bytes"),
+            HttpMethod.POST) {
+          @Override
+          protected CompletableFuture<ClientHttpResponse> doAsyncInvoke(Invocation invocation) {
+            CompletableFuture<ClientHttpResponse> completableFuture = new CompletableFuture<>();
+            holder.value = invocation;
+            completableFuture.complete(new CseClientHttpResponse(Response.ok("result")));
+            return completableFuture;
+          }
+        };
+    byte[] body = "abc".getBytes();
+    client.setRequestBody(body);
+    client.executeAsync();
+    Assert.assertArrayEquals(body, holder.value.getSwaggerArgument(0));
+  }
+
+  @Test
+  public void testFail() {
+    ServiceRegistry serviceRegistry = ServiceRegistryFactory.createLocal();
+    serviceRegistry.init();
+    RegistryUtils.setServiceRegistry(serviceRegistry);
+    UnitTestMeta meta = new UnitTestMeta();
+    CseContext.getInstance()
+        .getSchemaListenerManager()
+        .setSchemaListenerList(Arrays.asList(new RestEngineSchemaListener()));
+
+    SchemaMeta schemaMeta = meta.getOrCreateSchemaMeta(CseAsyncClientHttpRequestTest.SpringmvcImpl.class);
+    CseContext.getInstance().getSchemaListenerManager().notifySchemaListener(schemaMeta);
+
+    Throwable error = new Error("failed");
+    Response response = Response.createConsumerFail(error);
+
+    CseAsyncClientHttpRequest client =
+        new CseAsyncClientHttpRequest(URI.create(
+            "cse://app:test/" + CseAsyncClientHttpRequestTest.SpringmvcImpl.class.getSimpleName() + "/bytes"),
+            HttpMethod.POST) {
+          @Override
+          protected CompletableFuture<ClientHttpResponse> doAsyncInvoke(Invocation invocation) {
+            CompletableFuture<ClientHttpResponse> completableFuture = new CompletableFuture<>();
+            completableFuture.complete(new CseClientHttpResponse(response));
+            return completableFuture;
+          }
+        };
+    ListenableFuture<ClientHttpResponse> future = client.executeAsync();
+    future.addCallback(
+        new ListenableFutureCallback<ClientHttpResponse>() {
+          @Override
+          public void onFailure(Throwable ex) {
+            Assert.assertSame(error, ex);
+          }
+
+          @Override
+          public void onSuccess(ClientHttpResponse result) {
+          }
+        }
+    );
+  }
+}
\ No newline at end of file
diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRequestCallbackTest.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRequestCallbackTest.java
new file mode 100644
index 0000000..5a41a4d
--- /dev/null
+++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRequestCallbackTest.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.servicecomb.provider.springmvc.reference.async;
+
+import org.apache.servicecomb.provider.springmvc.reference.CseHttpEntity;
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.http.HttpEntity;
+
+import mockit.Injectable;
+
+public class CseAsyncRequestCallbackTest {
+  @Test
+  public void testNormal() {
+    CseAsyncClientHttpRequest request = new CseAsyncClientHttpRequest();
+    @SuppressWarnings("unchecked")
+    CseAsyncRequestCallback cb = new CseAsyncRequestCallback(null);
+    cb.doWithRequest(request);
+    Assert.assertEquals(null, request.getContext());
+  }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testHttpEntity(@Injectable HttpEntity<?> entity) {
+    CseAsyncRequestCallback cb = new CseAsyncRequestCallback(entity);
+    CseAsyncClientHttpRequest request = new CseAsyncClientHttpRequest();
+    cb.doWithRequest(request);
+    Assert.assertEquals(entity.getBody(), request.getBody());
+  }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void testCseEntity(@Injectable CseHttpEntity<?> entity) {
+    CseAsyncClientHttpRequest request = new CseAsyncClientHttpRequest();
+    entity.addContext("c1", "c2");
+    CseAsyncRequestCallback cb = new CseAsyncRequestCallback(entity);
+    cb.doWithRequest(request);
+    Assert.assertEquals(entity.getContext(), request.getContext());
+  }
+}
\ No newline at end of file
diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRestTemplateTest.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRestTemplateTest.java
new file mode 100644
index 0000000..3e5ce64
--- /dev/null
+++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/async/CseAsyncRestTemplateTest.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.servicecomb.provider.springmvc.reference.async;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CseAsyncRestTemplateTest {
+
+  @Test
+  public void testCseAsyncRestTemplate() {
+    Assert.assertEquals(CseAsyncRestTemplate.class, new CseAsyncRestTemplate().getClass());
+  }
+}
\ No newline at end of file
diff --git a/samples/springmvc-sample/springmvc-consumer/src/main/java/org/apache/servicecomb/samples/springmvc/consumer/SpringmvcConsumerMain.java b/samples/springmvc-sample/springmvc-consumer/src/main/java/org/apache/servicecomb/samples/springmvc/consumer/SpringmvcConsumerMain.java
index 1571aa8..417cac6 100644
--- a/samples/springmvc-sample/springmvc-consumer/src/main/java/org/apache/servicecomb/samples/springmvc/consumer/SpringmvcConsumerMain.java
+++ b/samples/springmvc-sample/springmvc-consumer/src/main/java/org/apache/servicecomb/samples/springmvc/consumer/SpringmvcConsumerMain.java
@@ -16,18 +16,28 @@
  */
 package org.apache.servicecomb.samples.springmvc.consumer;
 
+import java.lang.invoke.MethodHandles;
+
 import org.apache.servicecomb.foundation.common.utils.BeanUtils;
 import org.apache.servicecomb.foundation.common.utils.Log4jUtils;
 import org.apache.servicecomb.provider.pojo.RpcReference;
 import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder;
+import org.apache.servicecomb.provider.springmvc.reference.async.CseAsyncRestTemplate;
 import org.apache.servicecomb.samples.common.schema.Hello;
 import org.apache.servicecomb.samples.common.schema.models.Person;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
+import org.springframework.util.concurrent.ListenableFuture;
+import org.springframework.util.concurrent.ListenableFutureCallback;
 import org.springframework.web.client.RestTemplate;
 
 @Component
 public class SpringmvcConsumerMain {
-
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private static RestTemplate restTemplate = RestTemplateBuilder.create();
 
   @RpcReference(microserviceName = "springmvc", schemaId = "springmvcHello")
@@ -50,6 +60,33 @@ public class SpringmvcConsumerMain {
     // POJO Consumer
     System.out.println("POJO consumer sayhi services: " + hello.sayHi("Java Chassis"));
     System.out.println("POJO consumer sayhello services: " + hello.sayHello(person));
+
+    //AsyncRestTemplate Consumer
+    CseAsyncRestTemplate cseAsyncRestTemplate = new CseAsyncRestTemplate();
+    ListenableFuture<ResponseEntity<String>> responseEntityListenableFuture = cseAsyncRestTemplate
+        .postForEntity("cse://springmvc/springmvchello/sayhi?name=Java Chassis", null, String.class);
+    ResponseEntity<String> responseEntity = responseEntityListenableFuture.get();
+    System.out.println("AsyncRestTemplate Consumer sayHi services: " + responseEntity.getBody());
+
+    HttpEntity<Person> entity = new HttpEntity<>(person);
+    ListenableFuture<ResponseEntity<String>> listenableFuture = cseAsyncRestTemplate
+        .exchange("cse://springmvc/springmvchello/sayhello", HttpMethod.POST, entity, String.class);
+//    ResponseEntity<String> responseEntity1 = listenableFuture.get();
+//    System.out.println("AsyncRestTemplate Consumer sayHello services: " + responseEntity1.getBody());
+
+    listenableFuture.addCallback(
+        new ListenableFutureCallback<ResponseEntity<String>>() {
+          @Override
+          public void onFailure(Throwable ex) {
+            LOG.error("AsyncResTemplate Consumer catched exception when sayHello, ",ex);
+          }
+          @Override
+          public void onSuccess(ResponseEntity<String> result) {
+            System.out.println("AsyncRestTemplate Consumer sayHello services: " + result.getBody());
+          }
+        }
+    );
+
   }
 
   public static void init() throws Exception {

-- 
To stop receiving notification emails like this one, please contact
wujimin@apache.org.