SpringBoot与OpenFeign整合,实现微服务之间的声明式API调用系统

开发 前端
SpringBoot与OpenFeign的整合为构建微服务架构提供了一种强大且灵活的方式。通过声明式的API调用,开发者可以专注于业务逻辑的实现,而不必担心底层的网络通信细节。

SpringBoot与OpenFeign的整合为构建微服务架构提供了一种强大且灵活的方式。通过声明式的API调用,开发者可以专注于业务逻辑的实现,而不必担心底层的网络通信细节。结合Spring Cloud生态中的其他组件,可以进一步增强系统的可伸缩性、可靠性和安全性。

与OpenFeign的整合的好处

简化HTTP客户端开发

  • 声明式编程:通过注解的方式定义HTTP客户端接口,使得代码更加简洁和易于理解。
  • 减少样板代码:无需手动编写底层的HTTP请求代码,减少了重复的工作量。

集成Spring Cloud生态

  • 服务发现:结合Spring Cloud Eureka或Consul等服务注册中心,可以通过服务名称自动发现并调用相应的服务实例。
  • 负载均衡:内置支持Ribbon或其他负载均衡策略,确保请求均匀分布到各个服务实例。
  • 熔断机制:结合Hystrix或Resilience4j等库,可以轻松实现服务间的熔断保护。

强大的配置能力

  • 全局配置:可以通过配置文件统一管理Feign客户端的行为,如超时设置、重试机制等。
  • 自定义配置:可以通过configuration属性指定自定义的配置类,覆盖默认行为。

易于测试

  • Mocking:可以很容易地使用Mockito等工具对Feign客户端进行单元测试,提高代码的健壮性。
  • 集成测试:通过SpringBootTest框架,可以方便地进行集成测试,验证服务间的通信是否正常。

Feign客户端的注册与初始化

当 SpringBoot应用启动时,@EnableFeignClients 注解会触发Feign客户端的扫描和初始化过程。

1. 扫描Feign客户端接口

  • SpringBoot在启动过程中会扫描带有 @FeignClient 注解的接口,并将其注册到 Spring 上下文中。

2. 创建FeignContext

  • 每个Feign客户端都有一个对应的 FeignContext,用于存储相关的配置信息,如 Encoder、Decoder、Interceptor 等。

3. 解析接口注解

  • Feign使用 Contract 接口来解析接口上的注解(如 @GetMapping@PostMapping 等),并生成元数据。

4. 创建 Target

  • Feign使用 Target 接口来表示远程服务的目标。Target 包含了服务名称、URL 和类型等信息。

5. 创建Feign.Builder

  • Feign使用 Builder 类来构建Feign客户端实例。Builder 可以配置各种选项,如 Encoder、Decoder、Interceptor 等。

6. 创建Feign.Client

  • Feign使用 Client 接口来执行实际的 HTTP 请求。默认情况下,Feign使用 JDK 的 HttpURLConnection,但也可以配置为使用 Apache HttpClient 或 OkHttp。

7. 创建 InvocationHandlerFactory

  • Feign使用 InvocationHandlerFactory 来创建动态代理对象的 InvocationHandler,从而实现在方法调用时自动生成 HTTP 请求。

8. 创建动态代理对象

  • 最后,Feign使用 Java 动态代理机制,根据 InvocationHandler 创建具体的Feign客户端实例。

关键组件说明

  • @EnableFeignClients:启用 Feign客户端扫描。
  • @FeignClient:定义 Feign客户端接口。
  • FeignContext:存储 Feign客户端的相关配置。
  • Targeter:负责创建 Feign客户端实例。
  • Contract:解析接口上的注解,生成元数据。
  • Encoder/Decoder:处理请求体和响应体的序列化和反序列化。
  • Interceptor:拦截 HTTP 请求和响应,进行额外的处理。
  • Client:实际执行 HTTP 请求的客户端,如 Apache HttpClient 或 OkHttp。

代码实操

创建Maven多模块项目,父pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>microservices-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>

    <modules>
        <module>provider-service</module>
        <module>consumer-service</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.

创建Provider Service

<?xml version="1.0" encoding="UTF-8"?>
<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>com.example</groupId>
        <artifactId>microservices-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>provider-service</artifactId>
</project>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

Provider Service - application.properties

# 配置服务器端口
server.port=8081

# 日志级别设置为INFO
logging.level.root=INFO
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

Provider Service - Application

package com.example.providerservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 主应用程序类,用于启动Provider Service
 */
@SpringBootApplication
public class ProviderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderServiceApplication.class, args);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

Provider Service - 数据传输对象(DTO)

package com.example.providerservice.dto;

import lombok.Data;

/**
 * 数据传输对象,用于接收和返回请求信息
 */
@Data
public class RequestDto {
    private String name;
    private int age;
}

/**
 * 响应数据传输对象,用于返回处理结果
 */
@Data
public class ResponseDto {
    private String message;
    private String name;
    private int age;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

Provider Service - 随便搞一个Controller给别人调用

package com.example.providerservice.controller;

import com.example.providerservice.dto.RequestDto;
import com.example.providerservice.dto.ResponseDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 提供者服务的REST控制器
 */
@RestController
@RequestMapping("/api/provider")
@Slf4j
public class ProviderController {

    /**
     * 处理POST请求的方法
     *
     * @param requestDto 请求体中的数据
     * @return 处理后的响应对象
     */
    @PostMapping("/process")
    public ResponseDto processRequest(@RequestBody RequestDto requestDto) {
        log.info("Received request with name: {} and age: {}", requestDto.getName(), requestDto.getAge());
        
        // 构建响应对象
        ResponseDto responseDto = new ResponseDto();
        responseDto.setMessage("Processed request for " + requestDto.getName() + " who is " + requestDto.getAge() + " years old.");
        responseDto.setName(requestDto.getName());
        responseDto.setAge(requestDto.getAge());
        
        return responseDto;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.

创建Consumer Service

上面的Provider Service代码都不是重点,随便写一下就可以了。

下面的代码就要认真看了!

Consumer Service - 引入OpenFeign依赖

<?xml version="1.0" encoding="UTF-8"?>
<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>com.example</groupId>
        <artifactId>microservices-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>consumer-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.

Consumer Service - application.properties

# 配置服务器端口
server.port=8082

# 日志级别设置为INFO
logging.level.root=INFO
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

Consumer Service - Application

加上@EnableFeignClients

package com.example.consumerservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * 消费者服务的主应用程序类
 */
@SpringBootApplication
@EnableFeignClients
public class ConsumerServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerServiceApplication.class, args);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

Consumer Service - 重点来了

调用API就是这么简单!

package com.example.consumerservice.client;

import com.example.consumerservice.dto.RequestDto;
import com.example.consumerservice.dto.ResponseDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * Feign客户端接口,用于调用Provider Service的API
 */
@FeignClient(name = "providerService", url = "http://localhost:8081")
public interface ProviderClient {

    /**
     * 调用Provider Service的/process接口
     *
     * @param requestDto 请求体中的数据
     * @return 处理后的响应对象
     */
    @PostMapping("/api/provider/process")
    ResponseDto processRequest(@RequestBody RequestDto requestDto);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

Consumer Service - 数据传输对象(DTO)

package com.example.consumerservice.dto;

import lombok.Data;

/**
 * 数据传输对象,用于发送请求信息
 */
@Data
public class RequestDto {
    private String name;
    private int age;
}

/**
 * 响应数据传输对象,用于接收处理结果
 */
@Data
public class ResponseDto {
    private String message;
    private String name;
    private int age;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

Consumer Service - 用于测试的Controller

package com.example.consumerservice.controller;

import com.example.consumerservice.client.ProviderClient;
import com.example.consumerservice.dto.RequestDto;
import com.example.consumerservice.dto.ResponseDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 消费者服务的REST控制器
 */
@RestController
@RequestMapping("/api/consumer")
@Slf4j
public class ConsumerController {

    private final ProviderClient providerClient;

    @Autowired
    public ConsumerController(ProviderClient providerClient) {
        this.providerClient = providerClient;
    }

    /**
     * 处理POST请求的方法
     *
     * @param requestDto 请求体中的数据
     * @return 处理后的响应对象
     */
    @PostMapping("/process")
    public ResponseDto processRequest(@RequestBody RequestDto requestDto) {
        ResponseDto responseDto = providerClient.processRequest(requestDto);
        log.info("Received response from Provider Service: {}", responseDto.getMessage());
        return responseDto;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.

测试

curl -X POST http://localhost:8082/api/consumer/process \
     -H "Content-Type: application/json" \
     -d '{"name": "John Doe", "age": 30}'
  • 1.
  • 2.
  • 3.

Respons:

{
    "message": "Processed request for John Doe who is 30 years old.",
    "name": "John Doe",
    "age": 30
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.


责任编辑:武晓燕 来源: Java知识日历
相关推荐

2025-03-12 08:42:28

2021-07-16 07:57:35

SpringBootOpenFeign微服务

2024-04-19 08:49:50

微服务RPC事件驱动

2022-06-21 08:12:17

K8sAPI对象Kubernetes

2025-03-10 00:15:00

Axon开源框架

2025-02-28 08:40:28

ZooKeeperSpringBoot计费系统

2025-03-03 07:30:00

SpringBootJGraphT网络建模

2020-09-29 07:00:00

微服务API架构

2025-03-18 08:55:48

语言服务开发框架

2025-02-26 08:46:31

2019-09-24 08:44:09

OpenrestyAPI网关

2021-08-09 11:35:40

设计实践应用

2025-03-11 09:28:34

2020-05-18 14:00:01

Dubbo分布式架构

2020-12-17 07:59:46

声明式代码命令式代码代码

2023-01-10 09:48:03

RESTAPIJersey

2018-05-04 14:34:06

微服务SOAAPI

2022-01-10 19:45:40

微服务GO系统

2021-12-26 23:34:00

微服务Istio压缩

2025-02-26 09:24:54

SpringMySQLMyBatis
点赞
收藏

51CTO技术栈公众号