微服务架构开发模式-接口定义契约(路由+API规范),Controller实现业务,Feign复用接口远程调用,附详细示例

Spring MVC + OpenFeign 标准设计模式详解

这是微服务架构中最标准、最主流的开发模式:

接口定义契约(路由 + API 规范),Controller 实现业务,Feign 直接复用接口做远程调用,

一套接口同时满足「本地 HTTP 接口」和「微服务远程调用」,彻底避免重复代码和契约不一致问题。

核心概念

  1. 核心设计模式:面向接口编程 + 契约优先

接口层:

只定义请求路由、参数、返回值、API 规范,不写业务代码(既是 MVC 的 API 契约,也是 Feign 的远程调用契约)

Controller 层:

实现接口,重写方法,编写真实业务逻辑(Spring MVC 对外暴露 REST 接口)

Feign Client:

直接继承接口,无需重复定义 URL / 参数,自动生成远程调用客户端

  1. 关键技术角色

Spring MVC:

负责将 Controller 注册为 HTTP 接口,处理前端 / 网关请求

Spring Cloud OpenFeign:

微服务远程调用组件,复用同一套接口作为调用契约

统一契约:接口 = REST 路由定义 = Feign 调用规范,一份代码两端通用

  1. 标准优势(微服务必备)

✅ 无重复代码:服务提供者和消费者共用一套 API 接口

✅ 契约一致:避免接口 URL、参数、返回值定义不一致

✅ 结构清晰:接口定义规范,Controller 专注业务

✅ 易于维护:修改接口只需改动一处,两端同步生效

标准项目结构

公共模块(放通用接口,被所有服务依赖)

common-api/

└── feign/

└── UserFeignApi.java # 核心:统一API接口(契约)

服务提供者(提供真实接口)

user-service/

└── controller/

└── UserController.java # 实现接口,编写业务

服务消费者(调用远程接口)

order-service/

└── feign/

└── UserFeignClient.java # 继承接口,Feign客户端

工作流程总结

公共接口定义所有 API 的路由、参数、返回值(契约)

服务提供者实现接口,编写业务,对外暴露 REST 接口

服务消费者继承接口,生成 Feign 客户端,远程调用

全程一套接口,两端复用,无冗余代码,契约完全一致

这套模式的本质 = 契约优先(Contract First)

这是整个设计的灵魂。

什么是契约?

契约 = 接口的统一标准

URL 地址

请求方式(GET/POST)

参数格式

返回值格式

一句话:接口就是契约,契约就是接口。

为什么要契约优先?

微服务里有多个服务:

用户服务(user-service)

订单服务(order-service)

商品服务(product-service)

如果没有统一契约:

A 服务写一套接口

B 服务调用时再写一套一模一样的

结果:写错 URL、参数不匹配、返回值解析失败、维护爆炸

解决方案

只写一次接口,所有服务共用。

服务提供者:实现接口(提供真实功能)

服务消费者:继承接口(远程调用)

核心概念逐字拆解

1. 接口定义路由(API 契约层)

位置:

公共模块(common-api)作用:只定义规范,不写任何业务逻辑。

包含 4 个关键信息:

请求路径(/api/user/{id})

请求方法(GET/POST/PUT/DELETE)

请求参数(路径参数、请求体、参数)

返回值类型

代码特征:

只有 Spring MVC 注解

没有任何业务代码

不被实例化

这就是:接口定义路由

2. Controller 类实现业务逻辑(服务提供层)

位置:服务提供者(user-service)作用:实现接口,写真正的业务代码。

Spring MVC 会自动:

扫描 @RestController

把接口里的路由注册成 HTTP 接口

对外提供可访问的 REST 服务

Controller 只干两件事:

实现接口方法

编写业务逻辑(查库、调用 Service、计算、返回数据)

这就是:Controller 实现业务

3. OpenFeign 客户端继承接口(服务调用层)

位置:服务消费者(order-service)作用:不用再写 URL、参数、请求方式,直接继承接口。

Feign 会自动:

生成动态代理

根据注解拼接 HTTP 请求

向目标服务发送远程调用

解析返回结果

Feign 客户端 = 远程调用的替身

这就是:API 接口既是 REST 端点定义,也是 Feign Client 契约

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

实现

IDEA搭建项目

一、创建父工程(空 Maven 项目)

打开 IDEA

File → New → Project

选择 Maven,不要选骨架(archetype)

注:新版IDEA若没有Maven选项,则选择Java项目,再选择BuildSystem为Maven

直接 Next

填写:

Name:spring-cloud-feign-demo

Location:自选目录

GroupId:com.example

ArtifactId:spring-cloud-feign-demo

Finish

得到一个空 Maven 项目,删除 src 目录(父工程不用代码)。

二、创建三个子模块

父工程上右键 → New → Module依次创建 3 个模块:

  1. 公共模块 common-api

New Module → Maven → Next

Name:common-api

父工程选择:spring-cloud-feign-demo

Finish

  1. 服务提供者 user-service

New Module → Spring Initializr

Next

Artifact:user-service

依赖勾选:Spring Web

Finish

  1. 服务消费者 order-service

New Module → Spring Initializr

Artifact:order-service

依赖勾选:Spring Web

Finish

项目框架如下:

三、修改父项目pom文件

复制代码
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>spring-cloud-feign-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- 父工程必须设置为pom打包 -->
    <packaging>pom</packaging>

    <modules>
        <module>common-api</module>
        <module>user-service</module>
        <module>order-service</module>
        <module>common-api</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.8</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.0.5.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

四、配置common-api

修改common-api的pom文件

复制代码
    <parent>
        <groupId>com.example</groupId>
        <artifactId>spring-cloud-feign-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>common-api</artifactId>

    <dependencies>
        <!-- scope provided 表示编译时需要,打包时不包含,节省体积 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <!-- 编译插件:指定JDK版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source> <!-- 对应你的JDK版本 -->
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

创建接口契约

在 common-api 新建包:

com.example.common新建类 UserApi 接口:

复制代码
package com.example.common;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

public interface UserApi {

    // 定义路由、请求方式、参数、返回值 → 契约
    @GetMapping("/api/user/{id}")
    String getUserInfo(@PathVariable("id") Long id);
}

五、配置 user-service(服务提供者)

user-service/pom.xml 加入依赖

复制代码
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>user-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>user-service</name>
    <description>user-service</description>

    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.example</groupId>
            <artifactId>common-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
       
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

Controller 实现接口

建包 com.example.user.controller

新建 UserController:

复制代码
package com.example.userservice.controller;

import com.example.common.UserApi;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController implements UserApi {

    @Override
    public String getUserInfo(@PathVariable Long id) {
        // 真实业务逻辑
        return "【用户服务】返回用户信息:id=" + id + ",姓名=张三";
    }
}

修改yml文件

复制代码
server:
  port: 8081

spring:
  application:
    name: user-service

六、配置 order-service(服务消费者)

修改pom

复制代码
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>order-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>order-service</name>
    <description>order-service</description>

    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- OpenFeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>3.1.8</version>
        </dependency>

        <!-- 公共接口 -->
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>common-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

修改yml:

复制代码
server:
  port: 8082

spring:
  application:
    name: order-service

新建FeignClient

复制代码
​
package com.example.orderservice.feign;


import com.example.common.UserApi;
import org.springframework.cloud.openfeign.FeignClient;

@FeignClient(name = "user-service", url = "http://localhost:8081")
public interface UserFeignClient extends UserApi {
    // 无需写任何代码 → 全部继承自 UserApi
}

​

新建测试controller

复制代码
import com.example.orderservice.feign.UserFeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class OrderTestController {

    @Resource
    private UserFeignClient userFeignClient;

    @GetMapping("/test/feign/{id}")
    public String testFeign(@PathVariable Long id) {
        // 调用远程用户服务
        return "【订单服务调用用户服务】\n" + userFeignClient.getUserInfo(id);
    }
}

启动类加 @EnableFeignClients

复制代码
@SpringBootApplication
@EnableFeignClients // 开启Feign
public class OrderServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }

}

启动与测试

  1. 启动 user-service(8081)

访问:

http://localhost:8081/api/user/100

返回:

【用户服务】返回用户信息:id=100,姓名=张三

  1. 启动 order-service(8082)

访问:

http://localhost:8082/test/feign/100

返回:

【订单服务调用用户服务】

【用户服务】返回用户信息:id=100,姓名=张三

完整升级:Spring Cloud + OpenFeign + Nacos 注册中心(标准微服务)

直接把上一版完整示例无缝加入 Nacos,这是企业真正生产环境的标准架构:

去掉 Feign 写死的 URL

服务自动注册到 Nacos

服务名自动发现 + 负载均衡

纯微服务模式,可直接用于项目

第一步:安装启动 Nacos(最简版)

  1. 下载 Nacos

https://github.com/alibaba/nacos/releases

或者

https://www.nacos.io/download/nacos-server/

  1. 启动(单机模式)

windows上:

startup.cmd -m standalone

  1. 访问 Nacos 控制台验证

http://localhost:8848/nacos

账号 / 密码:nacos / nacos

二、父工程 pom.xml 加入 Nacos 依赖

修改pom文件,添加如下

复制代码
            <!-- Spring Cloud Alibaba + Nacos 核心依赖 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2021.0.5.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

三、服务提供者(user-service)修改

pom.xml 加入 Nacos 注册中心

复制代码
        <!-- Nacos 服务注册 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.0.5.0</version>
        </dependency>

application.yml 注册到 Nacos

复制代码
server:
  port: 8081

spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  # Nacos 地址

四、服务消费者(order-service)修改

pom.xml 加入 Nacos

复制代码
        <!-- Nacos 服务注册 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2021.0.5.0</version>
        </dependency>

另外还需要加入loadbalance

因为Spring Cloud 2020+ 以后 抛弃了 Ribbon,OpenFeign 必须依赖 Spring Cloud LoadBalancer 才能做负载均衡

复制代码
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
            <version>3.1.8</version>
        </dependency>

application.yml 从 Nacos 发现服务

复制代码
server:
  port: 8082

spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  # 连接 Nacos

Feign Client 去掉 URL,只留服务名(核心!)

复制代码
package com.example.orderservice.feign;


import com.example.common.UserApi;
import org.springframework.cloud.openfeign.FeignClient;

@FeignClient(name = "user-service")
public interface UserFeignClient extends UserApi {
    // 无需写任何代码 → 全部继承自 UserApi
}

五、启动测试(真正微服务)

  1. 启动 Nacos

  2. 启动 user-service(8081)

  3. 启动 order-service(8082)

  4. 查看 Nacos 服务列表

http://localhost:8848/nacos

服务管理 → 服务列表能看到:

user-service

order-service

访问订单服务:

http://localhost:8082/test/feign/100

返回:

【订单服务调用用户服务】

【用户服务】返回用户信息:id=100,姓名=张三

相关推荐
天天进步20152 小时前
[架构篇] 解构项目蓝图:Toonflow 的模块化设计与 AI 管道流转
人工智能·架构
架构师老Y2 小时前
007、微服务架构设计与服务拆分策略
python·微服务·架构
Meme Buoy3 小时前
13.6其他架构评估方法-中间件
中间件·架构
skilllite作者3 小时前
SkillLite 多入口架构实战:CLI / Python SDK / MCP / Desktop / Swarm 一页理清
开发语言·人工智能·python·安全·架构·rust·agentskills
2501_933329553 小时前
技术深度剖析:Infoseek 字节探索舆情处置系统的全链路架构与核心实现
大数据·数据仓库·人工智能·自然语言处理·架构
倔强的胖蚂蚁3 小时前
AI 人工智能配置管理 Nginx
运维·nginx·云原生
正在走向自律4 小时前
企业级数据库存储运维实战:表空间自动创建与存储架构深度优化
运维·数据库·架构·表空间
风向决定发型丶4 小时前
K8S PDB介绍
云原生·容器·kubernetes
玖釉-4 小时前
图形 API 的前沿试车场:Vulkan 扩展体系深度解析与引擎架构实践
c++·架构·图形渲染