In the Spring Cloud family, inter-process communication can be done using RestTemplate or OpenFeign. (Of course there are other ways such as message-driven microservices based on messaging middleware or gRPC-based calls).

RestTemplate can be treated as a common HTTP calling tool, and is particularly convenient for calling RESTful-style interfaces, as opposed to other HTTP clients.

However, more convenient than RestTemplate is OpenFeign, through the interface declaration can achieve remote calls, the specific use of these in the previous article, here will not repeat.

Previously, if we wanted to use declarative HTTP calls, we needed to implement them through OpenFeign, which requires third-party dependencies. Starting with Spring6 (Spring Boot3), Spring itself provides similar functionality through the @HttpExchange annotation, which also makes it easy to implement declarative HTTP calls. This is one more option for cross-service calls.

@HttpExchange Usage

First we create a common Spring Boot project called server. This common Spring Boot project only needs to provide a simple test interface, as follows.

1
2
3
4
5
6
7
8
9
@RestController
public class HelloController {

    @GetMapping("/server/hello")
    public String hello(String name) {
        return "hello " + name;
    }

}

This should not be difficult for everyone, so I won’t go into it.

Now suppose I have another service named client, and I want to call the interface provided by the server in the client.

First of all, let’s create the client project. Note that when creating it, we need to add not only Web dependencies, but also Reactive Web, because the underlying @HttpExchange is based on WebClient, which is provided by Reactive Web.

Spring Reactive Web

Once created, we can then declare the Http interface.

1
2
3
4
5
@HttpExchange("/server")
public interface ToDoService {
    @GetExchange("/hello")
    String hello(@RequestParam String name);
}

These uses are particularly similar to what we commonly use in SpringMVC, such as @RequestMapping and @GetMapping.

  • @HttpExchange is similar to @RequestMapping in that it can be placed on a class to play a request narrowing role, or on a method, where we can specify the specific request method via the method attribute, which is also similar to @RequestMapping: @ HttpExchange(value = "/server",method = "GET").
  • @GetExchange is similar to @GetMapping, and this will not be repeated. Other similar annotations are @DeleteExchange, @PatchExchange, @PostExchange, @PutExchange, etc.
  • Another thing to note is that the request method parameters need to be annotated with @RequestParam, which is similar to OpenFeign.

Once the interface is declared, we still need to configure it to use it. As follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Configuration
public class WebConfig {
    @Bean
    WebClient webClient() {
        return WebClient.builder()
                .baseUrl("http://localhost:8080")
                .build();
    }
    @Bean
    ToDoService toDoService() {
        HttpServiceProxyFactory httpServiceProxyFactory =
                HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient()))
                        .build();
        return httpServiceProxyFactory.createClient(ToDoService.class);
    }
}

This configuration is mainly twofold.

  1. @HttpExchange is based on WebClient, so we first need to configure WebClient, configure WebClient, also incidentally configure the specific address of the request (because in the @HttpExchange annotation does not specify the specific domain name port of the request or something); at the same time, for HTTP requests headers and so on, if you need to customize, is also through the configuration of WebClient to achieve.
  2. Since the ToDoService we provided earlier is an interface, we also need to provide an implementation class for that interface.

Once all the configuration is done, we can then directly inject the ToDoService instance wherever we need to use it, as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@SpringBootTest
class ClientApplicationTests {

    @Autowired
    ToDoService toDoService;

    @Test
    void contextLoads() {
        String hello = toDoService.hello("javaboy");
        System.out.println("hello = " + hello);
    }

}

Reference: https://mp.weixin.qq.com/s/x_kgVd8Qg2dPjZyfxzYh5Q