微服务架构开发模式-接口定义契约(路由+API规范),Controller实现业务,Feign复用接口远程调用,附详细示例
Spring MVC OpenFeign 标准设计模式详解这是微服务架构中最标准、最主流的开发模式接口定义契约路由 API 规范Controller 实现业务Feign 直接复用接口做远程调用一套接口同时满足「本地 HTTP 接口」和「微服务远程调用」彻底避免重复代码和契约不一致问题。核心概念1. 核心设计模式面向接口编程 契约优先接口层只定义请求路由、参数、返回值、API 规范不写业务代码既是 MVC 的 API 契约也是 Feign 的远程调用契约Controller 层实现接口重写方法编写真实业务逻辑Spring MVC 对外暴露 REST 接口Feign Client直接继承接口无需重复定义 URL / 参数自动生成远程调用客户端2. 关键技术角色Spring MVC负责将 Controller 注册为 HTTP 接口处理前端 / 网关请求Spring Cloud OpenFeign微服务远程调用组件复用同一套接口作为调用契约统一契约接口 REST 路由定义 Feign 调用规范一份代码两端通用3. 标准优势微服务必备✅ 无重复代码服务提供者和消费者共用一套 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 项目打开 IDEAFile → New → Project选择 Maven不要选骨架archetype注新版IDEA若没有Maven选项则选择Java项目再选择BuildSystem为Maven直接 Next填写Namespring-cloud-feign-demoLocation自选目录GroupIdcom.exampleArtifactIdspring-cloud-feign-demoFinish得到一个空 Maven 项目删除 src 目录父工程不用代码。二、创建三个子模块父工程上右键 → New → Module依次创建 3 个模块1. 公共模块 common-apiNew Module → Maven → NextNamecommon-api父工程选择spring-cloud-feign-demoFinish2. 服务提供者 user-serviceNew Module → Spring InitializrNextArtifactuser-service依赖勾选Spring WebFinish3. 服务消费者 order-serviceNew Module → Spring InitializrArtifactorder-service依赖勾选Spring WebFinish项目框架如下三、修改父项目pom文件parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent groupIdcom.example/groupId artifactIdspring-cloud-feign-demo/artifactId version1.0-SNAPSHOT/version !-- 父工程必须设置为pom打包 -- packagingpom/packaging modules modulecommon-api/module moduleuser-service/module moduleorder-service/module modulecommon-api/module /modules dependencyManagement dependencies dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-dependencies/artifactId version2021.0.8/version typepom/type scopeimport/scope /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version2021.0.5.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement properties maven.compiler.source8/maven.compiler.source maven.compiler.target8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties四、配置common-api修改common-api的pom文件parent groupIdcom.example/groupId artifactIdspring-cloud-feign-demo/artifactId version1.0-SNAPSHOT/version /parent artifactIdcommon-api/artifactId dependencies !-- scope provided 表示编译时需要打包时不包含节省体积 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId scopeprovided/scope /dependency /dependencies properties maven.compiler.source8/maven.compiler.source maven.compiler.target8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties build plugins !-- 编译插件指定JDK版本 -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.8.1/version configuration source1.8/source !-- 对应你的JDK版本 -- target1.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 groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent groupIdcom.example/groupId artifactIduser-service/artifactId version0.0.1-SNAPSHOT/version nameuser-service/name descriptionuser-service/description properties java.version1.8/java.version maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency dependency groupIdcom.example/groupId artifactIdcommon-api/artifactId version1.0-SNAPSHOT/version /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /buildController 实现接口建包 com.example.user.controller新建 UserControllerpackage 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服务消费者修改pomparent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent groupIdcom.example/groupId artifactIdorder-service/artifactId version0.0.1-SNAPSHOT/version nameorder-service/name descriptionorder-service/description properties java.version1.8/java.version maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency !-- OpenFeign -- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId version3.1.8/version /dependency !-- 公共接口 -- dependency groupIdcom.example/groupId artifactIdcommon-api/artifactId version1.0-SNAPSHOT/version /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-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 } 新建测试controllerimport 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); } }启动类加 EnableFeignClientsSpringBootApplication EnableFeignClients // 开启Feign public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } }启动与测试1. 启动 user-service8081访问http://localhost:8081/api/user/100返回【用户服务】返回用户信息id100姓名张三2. 启动 order-service8082访问http://localhost:8082/test/feign/100返回【订单服务调用用户服务】【用户服务】返回用户信息id100姓名张三完整升级Spring Cloud OpenFeign Nacos 注册中心标准微服务直接把上一版完整示例无缝加入 Nacos这是企业真正生产环境的标准架构去掉 Feign 写死的 URL服务自动注册到 Nacos服务名自动发现 负载均衡纯微服务模式可直接用于项目第一步安装启动 Nacos最简版1. 下载 Nacoshttps://github.com/alibaba/nacos/releases或者https://www.nacos.io/download/nacos-server/2. 启动单机模式windows上startup.cmd -m standalone3. 访问 Nacos 控制台验证http://localhost:8848/nacos账号 / 密码nacos / nacos二、父工程 pom.xml 加入 Nacos 依赖修改pom文件添加如下!-- Spring Cloud Alibaba Nacos 核心依赖 -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version2021.0.5.0/version typepom/type scopeimport/scope /dependency三、服务提供者user-service修改pom.xml 加入 Nacos 注册中心!-- Nacos 服务注册 -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId version2021.0.5.0/version /dependencyapplication.yml 注册到 Nacosserver: port: 8081 spring: application: name: user-service cloud: nacos: discovery: server-addr: localhost:8848 # Nacos 地址四、服务消费者order-service修改pom.xml 加入 Nacos!-- Nacos 服务注册 -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId version2021.0.5.0/version /dependency另外还需要加入loadbalance因为Spring Cloud 2020 以后 抛弃了 RibbonOpenFeign 必须依赖 Spring Cloud LoadBalancer 才能做负载均衡dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-loadbalancer/artifactId version3.1.8/version /dependencyapplication.yml 从 Nacos 发现服务server: port: 8082 spring: application: name: order-service cloud: nacos: discovery: server-addr: localhost:8848 # 连接 NacosFeign 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. 启动 Nacos2. 启动 user-service80813. 启动 order-service80824. 查看 Nacos 服务列表http://localhost:8848/nacos服务管理 → 服务列表能看到user-serviceorder-service访问订单服务http://localhost:8082/test/feign/100返回【订单服务调用用户服务】【用户服务】返回用户信息id100姓名张三