按DDD领域分析Openfeign

news/2025/12/10 21:39:41/文章来源:https://www.cnblogs.com/wasp520/p/19333283

按DDD领域分析Openfeign

请关注微信公众号:阿呆-bot

1. 入口类及说明

1.1 入口类:Feign 和 ReflectiveFeign

Feign 是抽象工厂类,ReflectiveFeign 是其基于反射的实现,负责创建 HTTP API 代理实例。

public abstract class Feign {public static Builder builder() {return new Builder();}public abstract <T> T newInstance(Target<T> target);
}public class ReflectiveFeign<C> extends Feign {public <T> T newInstance(Target<T> target) {Map<Method, MethodHandler> methodToHandler = targetToHandlersByName.apply(target, requestContext);InvocationHandler handler = factory.create(target, methodToHandler);return (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler);}
}

2. DDD 业务域划分

Core 模块可以划分为以下业务域:

2.1 契约域(Contract Domain)

职责: 解析接口方法上的注解,生成方法元数据。

核心类:

  • Contract: 契约接口
  • BaseContract: 基础契约实现
  • DeclarativeContract: 声明式契约基类
  • Contract.Default: 默认契约实现
  • MethodMetadata: 方法元数据

领域边界: 负责将 Java 接口方法转换为 HTTP 请求元数据,不涉及实际的 HTTP 请求执行。

PlantUML 类关系图

image.png

2.2 方法处理域(MethodHandler Domain)

职责: 处理接口方法的调用,执行 HTTP 请求并解码响应。

核心类:

  • MethodHandler: 方法处理器接口
  • SynchronousMethodHandler: 同步方法处理器
  • AsynchronousMethodHandler: 异步方法处理器
  • DefaultMethodHandler: 默认方法处理器(处理接口默认方法)
  • MethodHandlerConfiguration: 方法处理器配置

领域边界: 负责方法调用的执行流程,包括请求构建、HTTP 执行、响应处理,但不涉及注解解析。

PlantUML 类关系图

image.png

2.3 客户端域(Client Domain)

职责: 执行实际的 HTTP 请求,是 Feign 与底层 HTTP 库的抽象。

核心类:

  • Client: HTTP 客户端接口
  • Client.Default: 默认实现(基于 HttpURLConnection)
  • AsyncClient: 异步客户端接口

领域边界: 负责 HTTP 请求的执行,不涉及请求构建和响应解码。

PlantUML 类关系图

image.png

2.4 编解码域(Encoder/Decoder Domain)

职责: 将 Java 对象编码为 HTTP 请求体,将 HTTP 响应体解码为 Java 对象。

核心类:

  • Encoder: 编码器接口
  • Encoder.Default: 默认编码器
  • Decoder: 解码器接口
  • Decoder.Default: 默认解码器
  • ErrorDecoder: 错误解码器接口

领域边界: 负责对象序列化和反序列化,不涉及 HTTP 传输。

PlantUML 类关系图

image.png

2.5 目标域(Target Domain)

职责: 表示要调用的远程 HTTP 服务,包含服务类型、名称和基础 URL。

核心类:

  • Target: 目标接口
  • Target.HardCodedTarget: 硬编码目标实现
  • Target.EmptyTarget: 空目标实现

领域边界: 负责目标服务的表示和请求模板的应用,不涉及具体的 HTTP 请求执行。

PlantUML 类关系图

image.png

2.6 请求模板域(RequestTemplate Domain)

职责: 构建 HTTP 请求模板,支持 URI 模板表达式和参数替换。

核心类:

  • RequestTemplate: 请求模板类
  • RequestTemplate.Factory: 请求模板工厂接口(内部接口)
  • RequestTemplateFactoryResolver: 请求模板工厂解析器
  • template.UriTemplate: URI 模板
  • template.BodyTemplate: 请求体模板
  • template.HeaderTemplate: 请求头模板
  • template.QueryTemplate: 查询参数模板

领域边界: 负责请求模板的构建和参数替换,不涉及实际的 HTTP 请求发送。

PlantUML 类关系图

image.png

3. 领域间关系

3.1 领域协作流程

各业务域通过以下方式协作:

  1. 契约域方法处理域: Contract 解析注解生成 MethodMetadata,MethodHandler 使用 MethodMetadata 构建请求
  2. 方法处理域请求模板域: MethodHandler 使用 RequestTemplateFactory 创建请求模板
  3. 方法处理域客户端域: MethodHandler 调用 Client 执行 HTTP 请求
  4. 方法处理域编解码域: MethodHandler 使用 Encoder 编码请求体,使用 Decoder 解码响应体
  5. 方法处理域目标域: MethodHandler 使用 Target 应用请求模板

PlantUML 时序图:方法调用流程

image.png

4. 关键代码片段

4.1 Contract 解析方法元数据

protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {final MethodMetadata data = new MethodMetadata();data.targetType(targetType);data.method(method);data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));data.configKey(Feign.configKey(targetType, method));processAnnotationOnClass(data, targetType);for (final Annotation methodAnnotation : method.getAnnotations()) {processAnnotationOnMethod(data, methodAnnotation, method);}// 处理参数注解for (int i = 0; i < parameterAnnotations.length; i++) {processAnnotationsOnParameter(data, parameterAnnotations[i], i);}return data;
}

4.2 SynchronousMethodHandler 执行请求

public Object invoke(Object[] argv) throws Throwable {RequestTemplate template = methodHandlerConfiguration.getBuildTemplateFromArgs().create(argv);Options options = findOptions(argv);Retryer retryer = this.methodHandlerConfiguration.getRetryer().clone();while (true) {try {return executeAndDecode(template, options);} catch (RetryableException e) {retryer.continueOrPropagate(e);continue;}}
}Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {Request request = targetRequest(template);Response response = client.execute(request, options);return responseHandler.handleResponse(methodHandlerConfiguration.getMetadata().configKey(), response,methodHandlerConfiguration.getMetadata().returnType(), elapsedTime);
}

4.3 RequestTemplate 参数解析

public RequestTemplate resolve(Map<String, Object> variables) {if (variables == null || variables.isEmpty()) {return this;}RequestTemplate resolved = new RequestTemplate(this);if (uriTemplate != null) {resolved.uriTemplate = uriTemplate.expand(variables);}// 解析查询参数for (Map.Entry<String, QueryTemplate> entry : queries.entrySet()) {resolved.queries.put(entry.getKey(), entry.getValue().expand(variables));}// 解析请求头for (Map.Entry<String, HeaderTemplate> entry : headers.entrySet()) {resolved.headers.put(entry.getKey(), entry.getValue().expand(variables));}return resolved;
}

5. 实现关键点说明

5.1 领域驱动设计原则

Core 模块通过以下方式体现 DDD 原则:

  1. 领域边界清晰: 每个业务域职责单一,边界明确
  2. 领域模型: 每个域都有核心实体(如 Contract、MethodHandler、Client)
  3. 领域服务: 通过接口定义领域服务,支持可插拔实现
  4. 聚合根: Feign 和 ReflectiveFeign 作为聚合根,协调各领域协作

5.2 依赖倒置

各领域通过接口交互,实现依赖倒置:

  • MethodHandler 依赖 Client 接口,而非具体实现
  • Contract 生成 MethodMetadata,MethodHandler 使用 MethodMetadata
  • Encoder/Decoder 通过接口注入,支持替换

5.3 策略模式应用

各领域大量使用策略模式:

  • Contract: 支持不同的注解解析策略(Default、JAX-RS、Spring)
  • Client: 支持不同的 HTTP 客户端实现(Default、OkHttp、Apache HttpClient)
  • Encoder/Decoder: 支持不同的序列化策略(Default、Jackson、Gson)

5.4 模板方法模式

RequestTemplate 使用模板方法模式:

  • 定义请求模板结构(URI、Headers、Query、Body)
  • 支持参数替换和展开
  • 最终生成完整的 Request 对象

5.5 工厂模式

多处使用工厂模式:

  • Feign.builder(): 创建 Builder 工厂
  • MethodHandler.Factory: 创建 MethodHandler
  • RequestTemplateFactory: 创建 RequestTemplate(RequestTemplate.Factory 的内部接口)

6. 总结说明

Core 模块通过 DDD 领域划分,实现了清晰的职责分离:

  1. 契约域: 负责注解解析和元数据生成,是 Feign 声明式编程的基础
  2. 方法处理域: 负责方法调用的执行,是 Feign 的核心执行引擎
  3. 客户端域: 负责 HTTP 请求执行,是 Feign 与底层 HTTP 库的抽象
  4. 编解码域: 负责对象序列化,支持多种序列化框架
  5. 目标域: 负责目标服务表示,支持动态目标配置
  6. 请求模板域: 负责请求构建,支持灵活的 URI 模板和参数替换

各领域通过清晰的接口协作,实现了高内聚、低耦合的架构设计。这种设计使得 Feign 既灵活又可扩展,支持多种集成场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/997405.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

东方博宜OJ 4567:树的根 ← 邻接表 or 链式前向星

​【题目来源】https://oj.czos.cn/p/4567【题目描述】一棵有 N 个结点的树,树上结点编号为 1 到 N。已知树上 N-1 条边,且已知每条边的父子关系。请编程求出树上根结点的编号。【输入格式】第 1 行输入一个整数 N 代…

Python threading.Lock() thread lambda

import uuid from datetime import datetime import time import threadingprint(datetime.now().strftime(%Y%m%d%H%M%S%f)) idx=0 idx_lock=threading.Lock()def get_uuid_time():global idxwith idx_lock:idx+=1cur…

准确率和召回率的平衡点

目录🚪 决策阈值和 Agent 的“信心”1. 追求高准确率(Precision)2. 追求高召回率(Recall)总结:二者间的权衡曲线 您的问题非常关键,这是理解所有分类模型(包括智能体 Agent)性能评估时最核心的逻辑之一:准确…

Python 面向对象编程 (OOP) 核心:类、封装与继承

如果说函数式编程像是按照食谱一步步做菜,那么面向对象编程 (Object-Oriented Programming, OOP) 就像是管理一个餐厅。你需要设计不同的角色(厨师、服务员、经理),赋予他们职责,并让他们协同工作。 在 Python 中…

12/10

今天无事,就两节课,明天篮球课考试

完整教程:分享一个基于服务端地图服务裁剪的方法

完整教程:分享一个基于服务端地图服务裁剪的方法pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

并发编程的三大基石:从底层逻辑聊透“同步、互斥与分工”

并发编程的三大基石:从底层逻辑聊透“同步、互斥与分工”当单核性能的狂飙突进时代缓缓落幕,多核架构已成为算力增长的主旋律。然而,更多的核心并不天然等同于更强的性能。这就像将一条单行道拓宽为多车道高速公路,…

个人电脑本地私有知识库解决方案:访答知识库全面解析

个人电脑本地私有知识库解决方案:访答知识库全面解析 什么是本地私有知识库 本地私有知识库是一种安装在个人电脑上的知识管理软件,所有数据都存储在本地设备而非云端。这种解决方案特别适合注重数据隐私和安全性的用…

【Agent】MemOS 源码笔记---(4)---KV Cache

【Agent】MemOS 源码笔记---(4)---KV Cache 目录【Agent】MemOS 源码笔记---(4)---KV Cache0x00 概要0x01 原理1.1 技术路径1.2 对比1.3 协同工作0x02 定义2.1 KV Cache的记忆结构2.2 API总结 (KVCacheMemory)2.3 KVCa…

2025.12.10

做四级题,吃烤肉然后来学校换衣服,开会

大数据存储新范式:RustFS与Hadoop生态无缝集成实战指南

大数据存储新范式:RustFS与Hadoop生态无缝集成实战指南在数据量爆炸式增长的今天,我们团队面临一个现实问题:原有HDFS集群在成本、性能和运维复杂度上的三重压力。经过半年的探索实践,我们成功用RustFS替代HDFS作为…

Ai元人文构想:黑箱之渡,白箱之锚——大行为模型践行意义行为原生

Ai元人文构想:黑箱之渡,白箱之锚——大行为模型践行意义行为原生 引言:从意义通胀到意义行为 我们正身处一场深刻的“意义通胀”。大型语言模型(LLM)的“黑箱”以前所未有的规模吞吐符号、编织叙事,生产出海量流…

在 .Net 8 WEBAPI 中实现实体框架的 Code First 办法

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

60

所学时间:10小时

Coppersmith 学习笔记

基础不牢 地动山摇我咋到现在了才会这玩意 Coppersmith:求解 \(f(x) \equiv 0 \pmod p\),其中 \(p | N\) 是 N 的某个因子。 我们可以构造若干新的多项式,使得它的根与原多项式是相同的。例如,可以构造 \(f(x)^2, …

python —— 树的遍历 —— 深度优先遍历(先序、中序、后序) —— 非递归方式(使用栈数据结构进行辅助)

python —— 树的遍历 —— 深度优先遍历(先序、中序、后序) —— 非递归方式(使用栈数据结构进行辅助)代码:(以下示例使用先序遍历) # 2**n - 1 # 全二叉树 # n=4 2**4 - 1 = 15 import random node_v = [2,…

【SQL技术】不同数据库引擎 SQL 优化方案剖析 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

C++ 循环结构:控制程序重复执行的核心机制 - 教程

C++ 循环结构:控制程序重复执行的核心机制 - 教程2025-12-10 21:08 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; displ…

ASP.NET 实战:用 CSS 选择器打造一个可搜索、响应式的书籍管理系统 - 教程

ASP.NET 实战:用 CSS 选择器打造一个可搜索、响应式的书籍管理系统 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-famil…

Python list all files in dir recursivelly

import osidx=0 def list_dir_files(dir_name):global idxif os.path.exists(dir_name):all_items=os.listdir(dir_name)for item in all_items:full_path=os.path.join(dir_name,item)if os.path.isfile(full_path):i…