Spring Cloud 教程(四) | OpenFeign 的作用
- [前言:为什么要使用 OpenFeign?(核心解决的问题)](#前言:为什么要使用 OpenFeign?(核心解决的问题))
- [一、Feign 核心使用规则(必记)](#一、Feign 核心使用规则(必记))
- 二、业务场景说明
- [三、被调用方 ------ 用户服务代码](#三、被调用方 —— 用户服务代码)
-
- [3.1 用户实体类](#3.1 用户实体类)
- [3.2 用户Controller(提供对外接口)](#3.2 用户Controller(提供对外接口))
- [四、调用方 ------ 订单服务完整Feign配置](#四、调用方 —— 订单服务完整Feign配置)
-
- [4.1 订单服务标准包结构](#4.1 订单服务标准包结构)
- [4.2 引入核心依赖(调用方必加)](#4.2 引入核心依赖(调用方必加))
- [4.3 启动类开启Feign功能](#4.3 启动类开启Feign功能)
- [4.4 编写Feign接口(复制用户服务Controller)](#4.4 编写Feign接口(复制用户服务Controller))
- [4.5 注入Feign接口,实现远程调用](#4.5 注入Feign接口,实现远程调用)
- [4.6 订单配套Controller与实体类](#4.6 订单配套Controller与实体类)
- [五、本地Service VS Feign接口 核心对比](#五、本地Service VS Feign接口 核心对比)
- 六、全文总结
前言:为什么要使用 OpenFeign?(核心解决的问题)
在微服务架构 中,服务与服务是完全独立的,每个服务运行在不同端口、不同进程,甚至不同服务器上。
比如:订单服务(8082) 想要获取用户服务(8081) 的用户数据,本质上是发起一次HTTP网络请求。
不用Feign的痛点
如果不使用Feign,我们只能手动用RestTemplate发起HTTP调用:
- 手动拼接请求URL、处理请求参数
- 手动处理序列化、异常、请求头
- 代码冗余、可读性差、维护成本极高
- 调用方式和本地Service调用完全割裂,不符合Java编码习惯
Feign的核心价值
OpenFeign是Spring Cloud提供的声明式远程调用组件 ,它的作用只有一个:
让跨服务的HTTP调用,变得和调用本地Service方法一模一样简单
- 无需手写HTTP请求代码
- 只需定义接口+注解,自动生成网络请求逻辑
- 支持
@Autowired注入,和本地方法调用无区别 - 适配微服务注册中心(Nacos),自动负载均衡
一、Feign 核心使用规则(必记)
所有微服务跨服务调用,只需要调用方操作,被调用方无需任何修改,核心规则3条:
- 谁调用别人,谁引入Feign依赖
发起调用的服务(如订单服务),添加OpenFeign相关依赖 - 谁调用别人,谁创建Feign包
在调用方项目内新建feign包,统一管理所有远程服务调用接口 - 谁调用别人,谁复制对方Controller接口
把被调用服务(如用户服务)的Controller接口原样复制 到Feign接口中
(请求方式、路径、参数、返回值完全一致,仅复制接口,不复制实现)
二、业务场景说明
我们以两个最常见的微服务为例,演示跨服务调用:
- 用户服务(user-service)
- 端口:8081
- 角色:被调用方,提供根据ID查询用户的接口
- 订单服务(order-service)
- 端口:8082
- 角色:调用方,需要调用用户服务获取订单对应用户信息
三、被调用方 ------ 用户服务代码
被调用方只需要编写正常的Controller接口,无需任何Feign配置,完全无侵入。
3.1 用户实体类
java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
}
3.2 用户Controller(提供对外接口)
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/users")
public class UserController {
// 模拟数据库数据
private final Map<Long, User> userMap = new HashMap<>();
{
userMap.put(1L, new User(1L, "张三", 25));
userMap.put(2L, new User(2L, "李四", 30));
}
/**
* 订单服务需要远程调用的核心接口
*/
@GetMapping("/{id}")
public User getUserById(@PathVariable("id") Long id) {
return userMap.get(id);
}
}
四、调用方 ------ 订单服务完整Feign配置
订单服务作为调用方,严格按照核心规则完成配置,实现远程调用。
4.1 订单服务标准包结构
com.example.order
├── controller/ // 订单自身接口
├── service/ // 订单自身业务逻辑
├── entity/ // 订单实体、DTO
└── feign/ // 🔥 统一存放所有远程调用Feign接口
└── UserFeignClient.java // 调用用户服务的接口
4.2 引入核心依赖(调用方必加)
在订单服务pom.xml中添加Nacos服务发现+OpenFeign依赖:
xml
<!-- Nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- OpenFeign 声明式调用核心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
4.3 启动类开启Feign功能
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient // 开启微服务发现
@EnableFeignClients // 🔥 开启Feign远程调用功能
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
4.4 编写Feign接口(复制用户服务Controller)
在feign包下创建接口,完全复制用户服务Controller的接口定义:
java
import com.example.order.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* Feign客户端:调用用户服务
* 规则:请求方式、路径、参数、返回值 与对方Controller完全一致
*/
@FeignClient(name = "user-service") // 指定被调用服务在Nacos的注册名称
public interface UserFeignClient {
// 👇 原样复制 UserController 的 getUserById 方法
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
4.5 注入Feign接口,实现远程调用
和注入本地Service完全一致,直接@Autowired注入使用:
java
import com.example.order.entity.Order;
import com.example.order.entity.OrderDTO;
import com.example.order.entity.User;
import com.example.order.feign.UserFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class OrderService {
// 🔥 注入Feign接口,和本地Service调用无区别
@Autowired
private UserFeignClient userFeignClient;
// 模拟订单数据
private final Map<Long, Order> orderMap = new HashMap<>();
{
orderMap.put(1001L, new Order(1001L, "订单1", 1L));
orderMap.put(1002L, new Order(1002L, "订单2", 2L));
}
/**
* 查询订单详情(包含用户信息,需跨服务调用)
*/
public OrderDTO getOrderDetail(Long orderId) {
// 1. 查询本地订单信息
Order order = orderMap.get(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 2. 🔥 跨服务调用用户服务,像本地方法一样使用
User user = userFeignClient.getUserById(order.getUserId());
// 3. 组装返回数据
OrderDTO orderDTO = new OrderDTO();
orderDTO.setOrderId(order.getOrderId());
orderDTO.setOrderName(order.getOrderName());
orderDTO.setUserName(user.getName());
orderDTO.setUserAge(user.getAge());
return orderDTO;
}
}
4.6 订单配套Controller与实体类
java
// 订单Controller
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/{id}")
public OrderDTO getOrderDetail(@PathVariable("id") Long id) {
return orderService.getOrderDetail(id);
}
}
// 订单实体
@Data
@NoArgsConstructor
@AllArgsConstructor
class Order {
private Long orderId;
private String orderName;
private Long userId;
}
// 订单返回DTO
@Data
class OrderDTO {
private Long orderId;
private String orderName;
private String userName;
private Integer userAge;
}
五、本地Service VS Feign接口 核心对比
| 对比维度 | 本地Service接口 | Feign客户端接口 |
|---|---|---|
| 作用 | 执行本地业务逻辑 | 发起跨服务HTTP请求 |
| 实现类来源 | 自己手动编写 | Feign框架自动生成(动态代理) |
| 定义规则 | 自定义即可 | 必须复制被调用方Controller接口 |
| 调用方式 | @Autowired注入本地调用 | @Autowired注入远程调用 |
| 运行环境 | 同一服务、同一进程 | 跨服务、跨端口、网络请求 |
六、全文总结
- Feign解决的核心问题
简化微服务跨服务HTTP调用,让远程调用和本地Service调用体验完全一致。 - Feign使用核心原则
谁调用别人,谁加依赖;谁调用别人,谁建Feign包;谁调用别人,谁复制对方Controller。 - 无侵入特性
被调用服务无需任何修改,仅调用方配置即可完成远程调用。 - 工程规范
所有远程调用接口统一放在feign包下,项目结构清晰、易维护、易扩展。