# feign-unwrap-spring-boot-starter **Repository Path**: BeALaity/feign-unwrap-spring-boot-starter ## Basic Information - **Project Name**: feign-unwrap-spring-boot-starter - **Description**: No description available - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-08 - **Last Updated**: 2026-08-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # feign-unwrap-spring-boot-starter > Feign 响应结果自动解包 Starter ## 简介 在微服务架构中,Feign 接口通常返回统一的包装类型(如 `Result`、`R`),调用方需要手动校验成功标识并提取 `data` 字段。本 Starter 通过自定义注解 `@FeignUnwrap` 实现自动解包——Feign 方法直接声明业务返回类型(如 `User`),框架自动完成包装类的反序列化、成功校验和数据提取。 ## 核心特性 - **注解驱动**:`@FeignUnwrap(Result.class)` 标注在 Feign 类或方法上即可生效 - **自动解包**:`Result` → `User`,`Result>` → `List` - **灵活配置**:全局配置 + 注解级覆盖,适配不同包装类 - **策略可扩展**:提供 `ResultUnwrapStrategy` 接口,支持自定义解包逻辑 - **零侵入**:基于 `FeignBuilderCustomizer` 装配,不修改现有 Feign 配置 ## 快速开始 ### 1. 引入依赖 ```xml com.xiaokong feign-unwrap-spring-boot-starter 1.0.0-SNAPSHOT ``` ### 2. 定义统一响应包装类 ```java @Data public class Result { private int code; private String message; private T data; public boolean isSuccess() { return code == 200; } } ``` ### 3. 在 Feign 接口上使用注解 ```java // 方式一:方法级标注 @FeignClient(name = "user-service") public interface UserFeignClient { @GetMapping("/users/{id}") @FeignUnwrap(Result.class) User getUser(@PathVariable Long id); @GetMapping("/users") @FeignUnwrap(Result.class) List listUsers(); } // 方式二:类级标注(对所有方法生效) @FeignClient(name = "user-service") @FeignUnwrap(Result.class) public interface UserFeignClient { @GetMapping("/users/{id}") User getUser(@PathVariable Long id); @GetMapping("/users") List listUsers(); } ``` ### 4. 直接使用 ```java @Service public class UserService { private final UserFeignClient userFeignClient; public UserService(UserFeignClient userFeignClient) { this.userFeignClient = userFeignClient; } public User getUser(Long id) { // 直接返回 User,无需手动解包 Result return userFeignClient.getUser(id); } } ``` ## 全局配置 ```yaml feign-unwrap: enabled: true # 是否启用(默认 true) success-field: code # 成功标识字段名(默认 code) success-value: "200" # 成功标识值(默认 200) data-field: data # 数据字段名(默认 data) message-field: message # 消息字段名(默认 message) ``` ## 注解参数 | 参数 | 类型 | 默认值 | 说明 | | ---------------- | --------- | ------ | ------------------------------ | | `value` | `Class` | - | 包装类型(必填),如 `Result.class` | | `successField` | `String` | `""` | 成功标识字段名,空则用全局配置 | | `successValue` | `String` | `""` | 成功标识值,空则用全局配置 | | `dataField` | `String` | `""` | 数据字段名,空则用全局配置 | | `messageField` | `String` | `""` | 消息字段名,空则用全局配置 | | `throwOnFailure` | `boolean` | `true` | 校验失败时是否抛出异常 | ## 不同包装类适配示例 ### 示例一:code = 0 表示成功 ```java @Data public class R { private int code; // 0 = 成功 private String msg; private T data; } ``` ```yaml feign-unwrap: success-field: code success-value: "0" message-field: msg ``` ```java @FeignUnwrap(R.class) User getUser(Long id); ``` ### 示例二:boolean success 字段 ```java @Data public class ApiResponse { private boolean success; private String error; private T result; } ``` ```yaml feign-unwrap: success-field: success success-value: "true" data-field: result message-field: error ``` ```java @FeignUnwrap(ApiResponse.class) User getUser(Long id); ``` ### 示例三:注解级覆盖(不同接口使用不同包装类) ```java // 使用 Result,全局配置 @FeignUnwrap(Result.class) User getUser(Long id); // 使用 R,注解级覆盖配置 @FeignUnwrap(value = R.class, successField = "code", successValue = "0", messageField = "msg") Order getOrder(Long id); ``` ## 自定义解包策略 实现 `ResultUnwrapStrategy` 接口,覆盖默认的反射式解包逻辑: ```java @Component public class CustomResultUnwrapStrategy implements ResultUnwrapStrategy { @Override public Object unwrap(Object result, FeignUnwrap annotation) { if (result instanceof Result) { Result r = (Result) result; if (!r.isSuccess()) { throw new RuntimeException("调用失败: " + r.getMessage()); } return r.getData(); } return result; } } ``` 注册自定义策略后,默认的 `DefaultResultUnwrapStrategy` 不会加载。 ## 工作原理 ``` ┌─────────────────────────────────────────────────────────────────┐ │ Feign Client 调用 │ │ userFeignClient.getUser(1L) → 期望返回 User │ └──────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ FeignUnwrapInvocationHandlerFactory │ │ ① 拦截方法调用 │ │ ② 将当前 Method 存入 ThreadLocal (FeignUnwrapContext) │ │ ③ 委托原始 InvocationHandler 执行 │ └──────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Feign 发起 HTTP 请求,获取 Response │ └──────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ FeignUnwrapDecoder │ │ ① 从 ThreadLocal 读取当前 Method │ │ ② 检查 @FeignUnwrap 注解 │ │ ③ 将返回类型 User 包装为 Result │ │ ④ 委托内部 Decoder 反序列化为 Result │ │ ⑤ 调用 ResultUnwrapStrategy.unwrap() │ │ → 校验 code == 200 │ │ → 提取 data 字段 │ │ ⑥ 返回 User │ └──────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ FeignUnwrapInvocationHandlerFactory │ │ ④ 清除 ThreadLocal │ │ ⑤ 返回 User │ └─────────────────────────────────────────────────────────────────┘ ``` ## 项目结构 ``` feign-unwrap-spring-boot-starter/ ├── pom.xml ├── README.md └── src/main/ ├── java/com/xiaokong/feignunwrap/ │ ├── FeignUnwrapAutoConfiguration.java # 自动配置类 │ ├── annotation/ │ │ └── FeignUnwrap.java # 核心注解 │ ├── context/ │ │ └── FeignUnwrapContext.java # ThreadLocal上下文 │ ├── decoder/ │ │ ├── FeignUnwrapDecoder.java # 解包解码器 │ │ └── FeignUnwrapInvocationHandlerFactory.java # 调用处理器工厂 │ ├── exception/ │ │ └── FeignUnwrapException.java # 自定义异常 │ ├── impl/ │ │ └── DefaultResultUnwrapStrategy.java # 默认解包策略 │ ├── properties/ │ │ └── FeignUnwrapProperties.java # 配置属性 │ └── strategy/ │ └── ResultUnwrapStrategy.java # 解包策略接口 └── resources/META-INF/ ├── spring.factories # Spring Boot 2.x 自动配置 └── spring/ └── ...AutoConfiguration.imports # Spring Boot 3.x 自动配置 ``` ## 技术栈 - Java 8+ - Spring Boot 2.5.x - Spring Cloud OpenFeign 3.0.x - Hutool 5.8.x - Lombok 1.18.x