From ebe801f6bb50565a35662110ef91c0c384bd8a12 Mon Sep 17 00:00:00 2001 From: gewuyou Date: Thu, 5 Jun 2025 16:06:44 +0800 Subject: [PATCH] feat(context): Core functions of context propagation - Added FieldDef class to define field properties - Create Scope enumeration class, define the context storage location - Implement AbstractContext and Context interfaces - Add the ContextFieldContributor interface and the FieldRegistry interface - Implement context processors: HeaderProcessor, GeneratorProcessor, MdcProcessor, ReactorProcessor - Add context filters: ContextServletFilter, ContextWebFilter - Implement the automatic configuration class ForgeContextAutoConfiguration - Add StringContextHolder - Implement the default field registry DefaultFieldRegistry --- forgeboot-context/.gitattributes | 3 + forgeboot-context/.gitignore | 40 +++ forgeboot-context/build.gradle.kts | 8 + .../forgeboot-context-api/.gitattributes | 3 + .../forgeboot-context-api/.gitignore | 40 +++ .../forgeboot-context-api/build.gradle.kts | 4 + .../forgeboot/context/api/AbstractContext.kt | 57 ++++ .../gewuyou/forgeboot/context/api/Context.kt | 44 +++ .../context/api/ContextFieldContributor.kt | 20 ++ .../forgeboot/context/api/ContextProcessor.kt | 53 ++++ .../forgeboot/context/api/FieldRegistry.kt | 29 ++ .../context/api/entities/FieldDef.kt | 21 ++ .../forgeboot/context/api/enums/Scope.kt | 29 ++ .../.gitattributes | 3 + .../.gitignore | 40 +++ .../build.gradle.kts | 13 + .../ForgeContextAutoConfiguration.kt | 253 ++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../forgeboot-context-impl/.gitattributes | 3 + .../forgeboot-context-impl/.gitignore | 40 +++ .../forgeboot-context-impl/build.gradle.kts | 8 + .../context/impl/DefaultFieldRegistry.kt | 45 ++++ .../context/impl/StringContextHolder.kt | 11 + .../impl/filter/ContextServletFilter.kt | 62 +++++ .../context/impl/filter/ContextWebFilter.kt | 66 +++++ .../impl/processor/GeneratorProcessor.kt | 47 ++++ .../context/impl/processor/HeaderProcessor.kt | 72 +++++ .../context/impl/processor/MdcProcessor.kt | 48 ++++ .../impl/processor/ReactorProcessor.kt | 49 ++++ 29 files changed, 1112 insertions(+) create mode 100644 forgeboot-context/.gitattributes create mode 100644 forgeboot-context/.gitignore create mode 100644 forgeboot-context/build.gradle.kts create mode 100644 forgeboot-context/forgeboot-context-api/.gitattributes create mode 100644 forgeboot-context/forgeboot-context-api/.gitignore create mode 100644 forgeboot-context/forgeboot-context-api/build.gradle.kts create mode 100644 forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/AbstractContext.kt create mode 100644 forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/Context.kt create mode 100644 forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/ContextFieldContributor.kt create mode 100644 forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/ContextProcessor.kt create mode 100644 forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/FieldRegistry.kt create mode 100644 forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/entities/FieldDef.kt create mode 100644 forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/enums/Scope.kt create mode 100644 forgeboot-context/forgeboot-context-autoconfigure/.gitattributes create mode 100644 forgeboot-context/forgeboot-context-autoconfigure/.gitignore create mode 100644 forgeboot-context/forgeboot-context-autoconfigure/build.gradle.kts create mode 100644 forgeboot-context/forgeboot-context-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/context/autoconfigure/ForgeContextAutoConfiguration.kt create mode 100644 forgeboot-context/forgeboot-context-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 forgeboot-context/forgeboot-context-impl/.gitattributes create mode 100644 forgeboot-context/forgeboot-context-impl/.gitignore create mode 100644 forgeboot-context/forgeboot-context-impl/build.gradle.kts create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/DefaultFieldRegistry.kt create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/StringContextHolder.kt create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/ContextServletFilter.kt create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/ContextWebFilter.kt create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/GeneratorProcessor.kt create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/HeaderProcessor.kt create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/MdcProcessor.kt create mode 100644 forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/ReactorProcessor.kt diff --git a/forgeboot-context/.gitattributes b/forgeboot-context/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-context/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-context/.gitignore b/forgeboot-context/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-context/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-context/build.gradle.kts b/forgeboot-context/build.gradle.kts new file mode 100644 index 0000000..a51e0e0 --- /dev/null +++ b/forgeboot-context/build.gradle.kts @@ -0,0 +1,8 @@ +extra { + // 标记为根项目 + setProperty(ProjectFlags.IS_ROOT_MODULE, true) +} + +dependencies { + +} diff --git a/forgeboot-context/forgeboot-context-api/.gitattributes b/forgeboot-context/forgeboot-context-api/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-context/forgeboot-context-api/.gitignore b/forgeboot-context/forgeboot-context-api/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-context/forgeboot-context-api/build.gradle.kts b/forgeboot-context/forgeboot-context-api/build.gradle.kts new file mode 100644 index 0000000..52ab83a --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/build.gradle.kts @@ -0,0 +1,4 @@ + +dependencies { + +} diff --git a/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/AbstractContext.kt b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/AbstractContext.kt new file mode 100644 index 0000000..85662f1 --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/AbstractContext.kt @@ -0,0 +1,57 @@ +package com.gewuyou.forgeboot.context.api + +/** + *抽象上下文 + * + * @since 2025-06-04 13:36:54 + * @author gewuyou + */ +abstract class AbstractContext: Context { + private val local= ThreadLocal.withInitial { mutableMapOf() } + /** + * 将指定的键值对存入上下文中。 + * + * @param key 要存储的键 + * @param value 要存储的值,可以为 null + */ + override fun put(key: K, value: V?) { + value?.let { + local.get()[key] = it + } + } + + /** + * 根据指定的键从上下文中获取对应的值。 + * + * @param key 要查找的键 + * @return 对应的值,如果不存在则返回 null + */ + override fun get(key: K): V? { + return local.get()[key] + } + + /** + * 获取当前上下文的一个快照,包含所有键值对。 + * + * @return 一个 Map,表示当前上下文中的所有键值对 + */ + override fun snapshot(): Map { + return HashMap(local.get()) + } + + /** + * 清除上下文中的所有键值对。 + */ + override fun clear() { + local.remove() + } + + /** + * 从上下文中移除指定的键值对。 + * + * @param key 要移除的键 + */ + override fun remove(key: K) { + local.get().remove(key) + } +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/Context.kt b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/Context.kt new file mode 100644 index 0000000..b8700dc --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/Context.kt @@ -0,0 +1,44 @@ +package com.gewuyou.forgeboot.context.api + +/** + * 上下文接口,用于管理键值对的存储、获取和清理操作。 + * + * @since 2025-06-04 13:34:04 + * @author gewuyou + */ +interface Context { + /** + * 将指定的键值对存入上下文中。 + * + * @param key 要存储的键 + * @param value 要存储的值,可以为 null + */ + fun put(key: K, value: V?) + + /** + * 根据指定的键从上下文中获取对应的值。 + * + * @param key 要查找的键 + * @return 对应的值,如果不存在则返回 null + */ + fun get(key: K): V? + + /** + * 获取当前上下文的一个快照,包含所有键值对。 + * + * @return 一个 Map,表示当前上下文中的所有键值对 + */ + fun snapshot(): Map + + /** + * 清除上下文中的所有键值对。 + */ + fun clear() + + /** + * 从上下文中移除指定的键值对。 + * + * @param key 要移除的键 + */ + fun remove(key: K) +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/ContextFieldContributor.kt b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/ContextFieldContributor.kt new file mode 100644 index 0000000..4ce64c1 --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/ContextFieldContributor.kt @@ -0,0 +1,20 @@ +package com.gewuyou.forgeboot.context.api + +import com.gewuyou.forgeboot.context.api.entities.FieldDef + +/** + * 上下文字段贡献者接口,用于定义上下文所需字段的契约。 + * + * 实现此接口的类应提供一组上下文字段定义([FieldDef]),用于描述当前上下文的数据结构。 + * + * @since 2025-06-04 13:32:39 + * @author gewuyou + */ +fun interface ContextFieldContributor { + /** + * 提供上下文字段定义集合。 + * + * @return 返回一个不可变的[Set]集合,包含当前上下文所需的所有字段定义对象[FieldDef]。 + */ + fun fields(): Set +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/ContextProcessor.kt b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/ContextProcessor.kt new file mode 100644 index 0000000..fe51767 --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/ContextProcessor.kt @@ -0,0 +1,53 @@ +package com.gewuyou.forgeboot.context.api + +/** + * 上下文处理器 + * + * 用于定义上下文的处理逻辑,包括顺序控制、上下文提取与注入。 + * + * @since 2025-06-04 15:07:13 + * @author gewuyou + */ +interface ContextProcessor : Comparable { + + /** + * 获取当前处理器的执行顺序优先级。 + * + * 默认实现返回0,数值越小优先级越高。 + * + * @return Int 表示当前处理器的顺序值 + */ + fun order(): Int = 0 + + /** + * 从给定的载体中提取上下文信息,并填充到上下文对象中。 + * + * 默认实现为空方法,子类可根据需要重写此方法。 + * + * @param carrier 载体对象,通常包含上下文数据 + * @param ctx 可变映射,用于存储提取出的上下文键值对 + */ + fun extract(carrier: Any, ctx: MutableMap) { + // do nothing + } + + /** + * 将上下文信息注入到给定的载体中。 + * + * 默认实现为空方法,子类可根据需要重写此方法。 + * + * @param carrier 载体对象,将上下文数据注入其中 + * @param ctx 包含上下文键值对的映射 + */ + fun inject(carrier: Any, ctx: MutableMap) { + // do nothing + } + + /** + * 根据处理器的执行顺序进行比较,实现Comparable接口的方法。 + * + * @param other 待比较的另一个上下文处理器 + * @return Int 当前对象与other对象的顺序差值,用于排序 + */ + override fun compareTo(other: ContextProcessor) = order() - other.order() +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/FieldRegistry.kt b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/FieldRegistry.kt new file mode 100644 index 0000000..4e09bed --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/FieldRegistry.kt @@ -0,0 +1,29 @@ +package com.gewuyou.forgeboot.context.api + +import com.gewuyou.forgeboot.context.api.entities.FieldDef + +/** + * 字段注册表接口 + * + * 该接口定义了字段注册表的基本操作,包括获取所有字段定义和通过字段头查询字段定义。 + * + * @since 2025-06-04 14:44:40 + * @author gewuyou + */ +interface FieldRegistry { + + /** + * 获取所有字段定义的集合 + * + * @return 返回包含所有字段定义的集合 + */ + fun all(): Collection + + /** + * 根据字段头查找对应的字段定义 + * + * @param header 字段头信息 + * @return 如果找到匹配的字段定义则返回,否则返回 null + */ + fun findByHeader(header: String): FieldDef? +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/entities/FieldDef.kt b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/entities/FieldDef.kt new file mode 100644 index 0000000..1d6424c --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/entities/FieldDef.kt @@ -0,0 +1,21 @@ +package com.gewuyou.forgeboot.context.api.entities + +import com.gewuyou.forgeboot.context.api.enums.Scope + +/** + * 字段定义,用于描述数据结构中的字段属性。 + * + * @property header 字段的显示名称,通常用于界面展示。 + * @property key 字段的唯一标识符,用于数据映射和识别。 + * @property generator 生成字段值的函数,默认为 null,表示不使用动态生成。 + * @property scopes 定义该字段适用的上下文范围,默认包括 HEADER 和 MDC。 + * + * @since 2025-06-04 13:31:32 + * @author gewuyou + */ +data class FieldDef( + val header: String, + val key: String, + val generator: (() -> String)? = null, + val scopes: Set = setOf(Scope.HEADER, Scope.MDC) +) \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/enums/Scope.kt b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/enums/Scope.kt new file mode 100644 index 0000000..e9ca0c7 --- /dev/null +++ b/forgeboot-context/forgeboot-context-api/src/main/kotlin/com/gewuyou/forgeboot/context/api/enums/Scope.kt @@ -0,0 +1,29 @@ +package com.gewuyou.forgeboot.context.api.enums + +/** + * 范围枚举类,定义了不同上下文信息的存储位置 + * + * @since 2025-06-04 13:29:57 + * @author gewuyou + */ +enum class Scope { + /** + * 存储在请求头(Header)中 + */ + HEADER, + + /** + * 存储在 MDC(Mapped Diagnostic Context)中,用于日志跟踪 + */ + MDC, + + /** + * 存储在 Reactor 上下文中 + */ + REACTOR, + + /** + * 存储在 Baggage 中,用于分布式追踪 + */ + BAGGAGE +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-autoconfigure/.gitattributes b/forgeboot-context/forgeboot-context-autoconfigure/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-context/forgeboot-context-autoconfigure/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-context/forgeboot-context-autoconfigure/.gitignore b/forgeboot-context/forgeboot-context-autoconfigure/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-context/forgeboot-context-autoconfigure/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-context/forgeboot-context-autoconfigure/build.gradle.kts b/forgeboot-context/forgeboot-context-autoconfigure/build.gradle.kts new file mode 100644 index 0000000..556613a --- /dev/null +++ b/forgeboot-context/forgeboot-context-autoconfigure/build.gradle.kts @@ -0,0 +1,13 @@ +plugins{ + alias(libs.plugins.kotlin.plugin.spring) +} +dependencies { + implementation(platform(libs.springBootDependencies.bom)) + implementation(libs.springBoot.autoconfigure) + compileOnly(project(Modules.Context.API)) + compileOnly(project(Modules.Context.IMPL)) + compileOnly(libs.springCloudDependencies.bom) + compileOnly(libs.springBootStarter.web) + compileOnly(libs.springBootStarter.webflux) + compileOnly(libs.springCloudStarter.openfeign) +} diff --git a/forgeboot-context/forgeboot-context-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/context/autoconfigure/ForgeContextAutoConfiguration.kt b/forgeboot-context/forgeboot-context-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/context/autoconfigure/ForgeContextAutoConfiguration.kt new file mode 100644 index 0000000..57ad333 --- /dev/null +++ b/forgeboot-context/forgeboot-context-autoconfigure/src/main/kotlin/com/gewuyou/forgeboot/context/autoconfigure/ForgeContextAutoConfiguration.kt @@ -0,0 +1,253 @@ +package com.gewuyou.forgeboot.context.autoconfigure + + +import com.gewuyou.forgeboot.context.api.* +import com.gewuyou.forgeboot.context.impl.* +import com.gewuyou.forgeboot.context.impl.filter.ContextServletFilter +import com.gewuyou.forgeboot.context.impl.filter.ContextWebFilter +import com.gewuyou.forgeboot.context.impl.processor.* + + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.boot.autoconfigure.condition.* +import org.springframework.boot.web.reactive.function.client.WebClientCustomizer +import org.springframework.core.task.TaskDecorator +import org.springframework.web.reactive.function.client.ClientRequest + +/** + * 配置类,用于自动配置上下文相关的 Bean。 + * + * 该配置类根据不同的运行时依赖和配置条件,定义了一系列的 Bean, + * 实现了上下文字段在不同场景下的传播与管理机制。 + * + * @since 2025-06-04 11:48:01 + * @author gewuyou + */ +@Configuration +class ForgeContextAutoConfiguration { + /* ─────────────────────────────────────────────────────────────── + 0️⃣ 通用 Bean:不依赖 Web / Feign / Reactor 等外部包 + ─────────────────────────────────────────────────────────────── */ + + /** + * 创建 FieldRegistry Bean,用于注册上下文中所有字段定义。 + * + * FieldRegistry 是上下文字段的核心注册中心,聚合所有 ContextFieldContributor 提供的字段定义。 + * + * @param contributors 提供字段定义的贡献者列表 + * @return 构建完成的 FieldRegistry 实例 + */ + @Bean + @ConditionalOnMissingBean + fun fieldRegistry(contributors: List): FieldRegistry = + DefaultFieldRegistry(contributors.flatMap { it.fields() }.toSet()) + + /** + * 创建 HeaderProcessor Bean,用于处理请求头中的上下文字段。 + * + * HeaderProcessor 负责从请求头中提取上下文字段并注入到当前线程上下文中。 + * + * @param reg 字段注册表 + * @return 构建完成的 HeaderProcessor 实例 + */ + @Bean("headerProcessor") + fun headerProcessor(reg: FieldRegistry) = HeaderProcessor(reg) + + /** + * 创建 GeneratorProcessor Bean,用于生成上下文字段值。 + * + * GeneratorProcessor 根据字段定义生成默认值(如 traceId、spanId 等),适用于首次进入系统的情况。 + * + * @param reg 字段注册表 + * @return 构建完成的 GeneratorProcessor 实例 + */ + @Bean("generatorProcessor") + fun generatorProcessor(reg: FieldRegistry) = GeneratorProcessor(reg) + + /** + * 创建 MdcProcessor Bean,用于将上下文字段写入 MDC(Mapped Diagnostic Context)。 + * + * MdcProcessor 使得日志框架(如 Logback)可以访问当前上下文字段,便于日志追踪。 + * + * @param reg 字段注册表 + * @return 构建完成的 MdcProcessor 实例 + */ + @Bean("mdcProcessor") + fun mdcProcessor(reg: FieldRegistry) = MdcProcessor(reg) + + /* ─────────────────────────────────────────────────────────────── + 1️⃣ Reactor 支持(只有 classpath 有 Reactor 时才激活) + ─────────────────────────────────────────────────────────────── */ + + /** + * 配置类,提供对 Reactor 上下文传播的支持。 + */ + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(name = ["reactor.util.context.Context"]) + class ReactorSupport { + + /** + * 创建 ReactorProcessor Bean,用于在 Reactor 上下文中传播上下文字段。 + * + * ReactorProcessor 适配了 Reactor 的 Context 接口,确保上下文字段在响应式流中正确传递。 + * + * @param reg 字段注册表 + * @return 构建完成的 ReactorProcessor 实例 + */ + @Bean("reactorProcessor") + fun reactorProcessor(reg: FieldRegistry) = ReactorProcessor(reg) + } + + /* ─────────────────────────────────────────────────────────────── + 2️⃣ WebFlux 过滤器(依赖 WebFlux + Reactor) + ─────────────────────────────────────────────────────────────── */ + + /** + * 配置类,注册 WebFlux 环境下的上下文传播过滤器。 + */ + @Configuration(proxyBeanMethods = false) + @ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "reactive") + @ConditionalOnClass(name = ["org.springframework.web.server.WebFilter"]) + class WebFluxPart { + + /** + * 注册 ContextWebFilter Bean,用于在 WebFlux 请求链中传播上下文字段。 + * + * 该过滤器利用 ReactorProcessor 在 WebFlux 的过滤器链中维护上下文一致性。 + * + * @param chain 处理器链 + * @param reactorProcessor ReactorProcessor 实例 + * @return 构建完成的 ContextWebFilter 实例 + */ + @Bean + @ConditionalOnMissingBean + fun contextWebFilter( + chain: List, + reactorProcessor: ReactorProcessor, + ) = ContextWebFilter(chain, reactorProcessor) + } + + /* ─────────────────────────────────────────────────────────────── + 3️⃣ Servlet 过滤器(依赖 Servlet API) + ─────────────────────────────────────────────────────────────── */ + + /** + * 配置类,注册 Servlet 环境下的上下文传播过滤器。 + */ + @Configuration(proxyBeanMethods = false) + @ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "servlet", matchIfMissing = true) + @ConditionalOnClass(name = ["jakarta.servlet.Filter"]) + class ServletPart { + + /** + * 注册 ContextServletFilter Bean,用于在 Servlet 请求链中传播上下文字段。 + * + * 该过滤器负责在同步阻塞的 Servlet 请求链中维护上下文一致性。 + * + * @param chain 处理器链 + * @return 构建完成的 ContextServletFilter 实例 + */ + @Bean + @ConditionalOnMissingBean + fun contextServletFilter(chain: List) = + ContextServletFilter(chain) + } + + /* ─────────────────────────────────────────────────────────────── + 4️⃣ Feign 请求拦截器(依赖 OpenFeign) + ─────────────────────────────────────────────────────────────── */ + + /** + * 配置类,注册 Feign 客户端的请求拦截器。 + */ + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(name = ["feign.RequestInterceptor"]) + class FeignPart { + + /** + * 注册 Feign 请求拦截器,用于在 Feign 调用中传播上下文字段。 + * + * 拦截器会在每次 Feign 请求发起前,将当前上下文字段写入 HTTP 请求头。 + * + * @param registry 字段注册表 + * @return 构建完成的 feign.RequestInterceptor 实例 + */ + @Bean + @ConditionalOnMissingBean + fun feignInterceptor(registry: FieldRegistry) = + feign.RequestInterceptor { tpl -> + val ctx = StringContextHolder.snapshot() + registry.all().forEach { def -> + ctx[def.key]?.let { tpl.header(def.header, it) } + } + } + } + + /* ─────────────────────────────────────────────────────────────── + 5️⃣ 线程池 TaskDecorator(纯 Spring,安全通用) + ─────────────────────────────────────────────────────────────── */ + + /** + * 配置类,注册异步执行上下文保持支持。 + */ + @Configuration(proxyBeanMethods = false) + class TaskDecoratorPart { + + /** + * 创建 TaskDecorator Bean,用于在异步执行中保持上下文一致性。 + * + * 通过装饰线程池任务,确保异步任务继承调用线程的上下文状态。 + * + * @param processors 所有处理器列表 + * @return 构建完成的 TaskDecorator 实例 + */ + @Bean + fun contextTaskDecorator(processors: List) = + TaskDecorator { delegate -> + val snap = StringContextHolder.snapshot() + Runnable { + try { + snap.forEach(StringContextHolder::put) + processors.forEach { it.inject(Unit, snap.toMutableMap()) } + delegate.run() + } finally { + processors.forEach { it.inject(Unit, mutableMapOf()) } + StringContextHolder.clear() + } + } + } + } + + /* ─────────────────────────────────────────────────────────────── + 6️⃣ WebClient 过滤器 + ─────────────────────────────────────────────────────────────── */ + + /** + * 配置类,注册 WebClient 自定义器以支持上下文传播。 + */ + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(name = ["org.springframework.web.reactive.function.client.WebClient"]) + class WebClientPart(private val registry: FieldRegistry) { + + /** + * 注册 WebClientCustomizer,用于定制 WebClient 的请求行为。 + * + * 在每次请求发出前,将当前上下文字段写入 HTTP 请求头。 + * + * @return 构建完成的 WebClientCustomizer 实例 + */ + @Bean + fun contextWebClientCustomizer() = WebClientCustomizer { builder -> + builder.filter { req, next -> + val ctx = StringContextHolder.snapshot() + val mutated = ClientRequest.from(req).apply { + registry.all().forEach { def -> + ctx[def.key]?.let { value -> header(def.header, value) } + } + }.build() + next.exchange(mutated) + } + } + } +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/forgeboot-context/forgeboot-context-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..0ebbb8f --- /dev/null +++ b/forgeboot-context/forgeboot-context-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.gewuyou.forgeboot.context.autoconfigure.ForgeContextAutoConfiguration diff --git a/forgeboot-context/forgeboot-context-impl/.gitattributes b/forgeboot-context/forgeboot-context-impl/.gitattributes new file mode 100644 index 0000000..8af972c --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/.gitattributes @@ -0,0 +1,3 @@ +/gradlew text eol=lf +*.bat text eol=crlf +*.jar binary diff --git a/forgeboot-context/forgeboot-context-impl/.gitignore b/forgeboot-context/forgeboot-context-impl/.gitignore new file mode 100644 index 0000000..5a979af --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/.gitignore @@ -0,0 +1,40 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Kotlin ### +.kotlin diff --git a/forgeboot-context/forgeboot-context-impl/build.gradle.kts b/forgeboot-context/forgeboot-context-impl/build.gradle.kts new file mode 100644 index 0000000..4d14916 --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/build.gradle.kts @@ -0,0 +1,8 @@ + +dependencies { + compileOnly(project(Modules.Context.API)) + compileOnly(libs.springBootStarter.web) + compileOnly(libs.springBootStarter.webflux) + implementation(platform(libs.springBootDependencies.bom)) + implementation(libs.springBoot.autoconfigure) +} diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/DefaultFieldRegistry.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/DefaultFieldRegistry.kt new file mode 100644 index 0000000..0ef3483 --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/DefaultFieldRegistry.kt @@ -0,0 +1,45 @@ +package com.gewuyou.forgeboot.context.impl + +import com.gewuyou.forgeboot.context.api.FieldRegistry +import com.gewuyou.forgeboot.context.api.entities.FieldDef + +/** + * 默认字段注册表 + * + * 该类实现了字段的注册和查找功能,通过字段的 key 和 header 建立索引, + * 提供了基于 header 的快速查找能力。 + * + * @param defs 初始化字段定义集合,用于构建注册表 + * @since 2025-06-04 14:51:39 + * @author gewuyou + */ +class DefaultFieldRegistry(defs: Set) : FieldRegistry { + /** + * 按字段 key 构建的有序映射表,用于通过 key 快速访问字段定义。 + * 保持字段注册顺序的一致性,适用于需要按注册顺序遍历的场景。 + */ + private val byKey: Map = + LinkedHashMap().apply { + defs.forEach { put(it.key, it) } + } + + /** + * 按字段 header(小写形式)构建的映射表,用于通过 header 快速查找字段定义。 + */ + private val byHeader = defs.associateBy { it.header.lowercase() } + + /** + * 获取注册表中所有的字段定义。 + * + * @return 字段定义的集合 + */ + override fun all() = byKey.values + + /** + * 根据字段 header 查找对应的字段定义。 + * + * @param header 要查找的字段 header(不区分大小写) + * @return 匹配到的字段定义,若未找到则返回 null + */ + override fun findByHeader(header: String) = byHeader[header.lowercase()] +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/StringContextHolder.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/StringContextHolder.kt new file mode 100644 index 0000000..1b882d0 --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/StringContextHolder.kt @@ -0,0 +1,11 @@ +package com.gewuyou.forgeboot.context.impl + +import com.gewuyou.forgeboot.context.api.AbstractContext + +/** + *字符串上下文容器 + * + * @since 2025-06-04 15:05:43 + * @author gewuyou + */ +object StringContextHolder: AbstractContext() \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/ContextServletFilter.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/ContextServletFilter.kt new file mode 100644 index 0000000..da7e21a --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/ContextServletFilter.kt @@ -0,0 +1,62 @@ +package com.gewuyou.forgeboot.context.impl.filter + +import com.gewuyou.forgeboot.context.api.ContextProcessor +import com.gewuyou.forgeboot.context.impl.StringContextHolder +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.web.filter.OncePerRequestFilter + +/** + * 上下文 Servlet 过滤器 + * + * 该过滤器用于在 HTTP 请求处理过程中提取和注入上下文信息, + * 确保请求链中各组件可以共享上下文数据。 + * + * @property chain 处理上下文的处理器链表,按顺序依次执行 + * + * @since 2025-06-04 16:08:33 + * @author gewuyou + */ +class ContextServletFilter( + private val chain: List, +) : OncePerRequestFilter() { + + /** + * 执行内部过滤逻辑 + * + * 在请求进入业务逻辑前,从请求中提取上下文信息并存储到上下文持有者中; + * 在请求完成后,清理上下文以避免内存泄漏或上下文污染。 + * + * @param request 当前 HTTP 请求对象 + * @param response 当前 HTTP 响应对象 + * @param filterChain 过滤器链,用于继续执行后续过滤器或目标处理逻辑 + */ + override fun doFilterInternal( + request: HttpServletRequest, + response: HttpServletResponse, + filterChain: FilterChain, + ) { + // 创建当前线程上下文快照的可变副本,确保后续操作不影响原始上下文 + val ctx = StringContextHolder.snapshot().toMutableMap() + + // 遍历上下文处理器链,依次从请求中提取上下文信息并更新临时上下文容器 + chain.forEach { it.extract(request, ctx) } + + // 将提取后的上下文写入当前线程的上下文持有者,供后续组件访问 + ctx.forEach(StringContextHolder::put) + + // 调用下一个过滤器或最终的目标处理器 + chain.forEach { it.inject(request, ctx) } + + try { + filterChain.doFilter(request, response) + } finally { + // 确保在请求结束时清理所有上下文资源 + // 向处理器链注入空上下文,触发清理操作(如有) + chain.forEach { it.inject(Unit, mutableMapOf()) } + // 显式清除当前线程的上下文持有者,防止上下文泄露 + StringContextHolder.clear() + } + } +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/ContextWebFilter.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/ContextWebFilter.kt new file mode 100644 index 0000000..533dc29 --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/filter/ContextWebFilter.kt @@ -0,0 +1,66 @@ +package com.gewuyou.forgeboot.context.impl.filter + +import com.gewuyou.forgeboot.context.api.ContextProcessor +import com.gewuyou.forgeboot.context.impl.StringContextHolder +import com.gewuyou.forgeboot.context.impl.processor.ReactorProcessor +import org.springframework.web.server.ServerWebExchange +import org.springframework.web.server.WebFilter +import org.springframework.web.server.WebFilterChain +import reactor.core.publisher.Mono + +/** + * 上下文 Web 过滤器 + * + * 用于在 Web 请求处理过程中维护和传递上下文信息,包括 ThreadLocal、MDC 和 Reactor Context。 + * 通过一系列的 [ContextProcessor] 实现对上下文的提取、注入和清理操作。 + * + * @property contextProcessors 上下文处理器列表,用于处理上下文的提取与注入逻辑。 + * @property reactorProc 反应式上下文处理器,用于将上下文写入 Reactor 的 Context 中。 + * + * @since 2025-06-04 15:54:43 + * @author gewuyou + */ +class ContextWebFilter( + private val contextProcessors: List, + private val reactorProc: ReactorProcessor +) : WebFilter { + /** + * 执行过滤逻辑,在请求链中插入上下文管理操作。 + * + * 此方法依次完成以下步骤: + * 1. 提取当前上下文并构建新的上下文数据; + * 2. 将上下文填充到 ThreadLocal 和 MDC; + * 3. 将上下文注入到请求头中以构建新地请求; + * 4. 继续执行请求链,并将上下文写入 Reactor Context; + * 5. 最终清理 ThreadLocal、MDC 中的上下文。 + * + * @param exchange 当前的 ServerWebExchange 对象,代表 HTTP 请求交换。 + * @param chain Web 过滤器链,用于继续执行后续的过滤器或目标处理逻辑。 + * @return 返回一个 Mono 表示异步完成的操作。 + */ + override fun filter( + exchange: ServerWebExchange, + chain: WebFilterChain, + ): Mono { + // 从 StringContextHolder 快照获取当前上下文并转换为可变 Map + val ctx = StringContextHolder.snapshot().toMutableMap() + // 遍历所有 ContextProcessor,从请求中提取上下文信息到 ctx + contextProcessors.forEach { it.extract(exchange, ctx) } + // 将上下文写入 StringContextHolder(ThreadLocal) + ctx.forEach(StringContextHolder::put) + // 使用 MdcProcessor 将上下文注入到 MDC 中 + contextProcessors.forEach { it.inject(Unit, ctx) } + // 构建新的 ServerWebExchange 实例 + val mutated = exchange.mutate() + // 注入上下文到请求头中 + contextProcessors.forEach { it.inject(mutated, ctx) } + // 继续执行过滤器链,同时将上下文写入 Reactor Context + return chain.filter(mutated.build()) + .contextWrite(reactorProc.injectToReactor(ctx)) + .doFinally { + // 清理 ThreadLocal + MDC 上下文 + contextProcessors.forEach { it.inject(Unit, mutableMapOf()) } + StringContextHolder.clear() + } + } +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/GeneratorProcessor.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/GeneratorProcessor.kt new file mode 100644 index 0000000..5652a85 --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/GeneratorProcessor.kt @@ -0,0 +1,47 @@ +package com.gewuyou.forgeboot.context.impl.processor + +import com.gewuyou.forgeboot.context.api.ContextProcessor +import com.gewuyou.forgeboot.context.api.FieldRegistry + +/** + * 生成器处理器 + * + * 用于通过字段注册表(FieldRegistry)动态填充上下文中的空白字段。 + * 当前类实现了 ContextProcessor 接口,提供了一种自动化的上下文字段填充机制。 + * + * @since 2025-06-04 15:35:11 + * @author gewuyou + */ +class GeneratorProcessor( + private val reg: FieldRegistry +) : ContextProcessor { + /** + * 获取当前处理器的执行顺序优先级。 + * + * 在多个 ContextProcessor 实现中,该方法决定本处理器的执行顺序。 + * 数值越小,优先级越高,在上下文处理流程中就越早被调用。 + * + * @return Int 表示当前处理器的顺序值,默认为20 + */ + override fun order(): Int { + return 20 + } + + /** + * 从给定的载体中提取上下文信息,并填充到上下文对象中。 + * + * 遍历 FieldRegistry 中注册的所有字段定义: + * - 如果当前字段在上下文中不存在或为空白,则尝试使用其关联的生成器函数进行填充。 + * - 生成器函数非空时会被调用,并将结果存入上下文映射中。 + * + * @param carrier 载体对象,通常包含上下文数据(未使用于当前实现) + * @param ctx 可变映射,用于存储提取出的上下文键值对 + */ + override fun extract(carrier: Any, ctx: MutableMap) { + reg.all().forEach { def -> + if (ctx[def.key].isNullOrBlank()) { + def.generator?.invoke()?.let { ctx[def.key] = it } + } + } + } +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/HeaderProcessor.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/HeaderProcessor.kt new file mode 100644 index 0000000..8de57ec --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/HeaderProcessor.kt @@ -0,0 +1,72 @@ +package com.gewuyou.forgeboot.context.impl.processor + +import com.gewuyou.forgeboot.context.api.ContextProcessor +import com.gewuyou.forgeboot.context.api.FieldRegistry +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.web.server.ServerWebExchange + +/** + * 请求头处理器 + * + * @since 2025-06-04 15:14:57 + * @author gewuyou + */ +class HeaderProcessor(private val reg: FieldRegistry) : ContextProcessor { + /** + * 获取当前处理器的执行顺序优先级。 + * + * 默认实现返回0,数值越小优先级越高。 + * + * @return Int 表示当前处理器的顺序值 + */ + override fun order(): Int { + return 10 + } + + /** + * 从给定的载体中提取上下文信息,并填充到上下文对象中。 + * + * 默认实现为空方法,子类可根据需要重写此方法。 + * + * @param carrier 载体对象,通常包含上下文数据 + * @param ctx 可变映射,用于存储提取出的上下文键值对 + */ + override fun extract(carrier: Any, ctx: MutableMap) { + when (carrier) { + is ServerWebExchange -> reg.all().forEach { def -> + // 从ServerWebExchange请求头中提取指定字段并存入上下文 + carrier.request.headers[def.header]?.firstOrNull()?.let { ctx[def.key] = it } + } + is HttpServletRequest -> reg.all().forEach { def -> + // 从HttpServletRequest请求头中提取指定字段并存入上下文 + carrier.getHeader(def.header)?.let { ctx[def.key] = it } + } + } + } + + /** + * 将上下文信息注入到给定的载体中。 + * + * 默认实现为空方法,子类可根据需要重写此方法。 + * + * @param carrier 载体对象,将上下文数据注入其中 + * @param ctx 包含上下文键值对的映射 + */ + override fun inject(carrier: Any, ctx: MutableMap) { + when (carrier) { + is ServerWebExchange.Builder -> reg.all().forEach { def -> + // 向ServerWebExchange构建器中注入请求头字段 + ctx[def.key]?.let { value -> + carrier.request { reqBuilder -> + reqBuilder.header(def.header, value) + } + } + } + is HttpServletResponse -> reg.all().forEach { def -> + // 向HttpServletResponse中设置对应的响应头字段 + ctx[def.key]?.let { carrier.setHeader(def.header, it) } + } + } + } +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/MdcProcessor.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/MdcProcessor.kt new file mode 100644 index 0000000..42a3e97 --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/MdcProcessor.kt @@ -0,0 +1,48 @@ +package com.gewuyou.forgeboot.context.impl.processor + +import com.gewuyou.forgeboot.context.api.ContextProcessor +import com.gewuyou.forgeboot.context.api.FieldRegistry +import org.slf4j.MDC + +/** + * MDC 处理器 + * 把 ctx 写入 SLF4J MDC,方便日志打印;请求结束或线程任务结束时清理。 + * + * + * @since 2025-06-04 15:39:35 + * @author gewuyou + */ +class MdcProcessor( + private val reg: FieldRegistry, +) : ContextProcessor { + /** + * 获取当前处理器的执行顺序优先级。 + * + * 默认实现返回0,数值越小优先级越高。 + * + * @return Int 表示当前处理器的顺序值 + */ + override fun order(): Int { + return 30 + } + + /** + * 将上下文信息注入到给定的载体中。 + * + * 默认实现为空方法,子类可根据需要重写此方法。 + * + * @param carrier 载体对象,将上下文数据注入其中 + * @param ctx 包含上下文键值对的映射 + */ + override fun inject(carrier: Any, ctx: MutableMap) { + if (ctx.isEmpty()) { + // 视为空 ctx → 清理 + reg.all().forEach { def -> MDC.remove(def.key) } + } else { + // 正常写入 + reg.all().forEach { def -> + ctx[def.key]?.let { MDC.put(def.key, it) } + } + } + } +} \ No newline at end of file diff --git a/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/ReactorProcessor.kt b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/ReactorProcessor.kt new file mode 100644 index 0000000..48a7092 --- /dev/null +++ b/forgeboot-context/forgeboot-context-impl/src/main/kotlin/com/gewuyou/forgeboot/context/impl/processor/ReactorProcessor.kt @@ -0,0 +1,49 @@ +package com.gewuyou.forgeboot.context.impl.processor + +import com.gewuyou.forgeboot.context.api.ContextProcessor +import com.gewuyou.forgeboot.context.api.FieldRegistry +import reactor.util.context.Context +import java.util.function.Function +/** + * 反应器处理器 + * + * 用于在 WebFlux/Reactor 流中透传字段。当进入异步链时,将字段写入 reactor.util.context.Context。 + * Context 在链尾无需手动清理,会自动复制和管理。 + * + * @property reg 字段注册表,用于获取所有需要处理的字段定义 + * @since 2025-06-04 15:43:20 + * @author gewuyou + */ +class ReactorProcessor( + private val reg: FieldRegistry +) : ContextProcessor { + /** + * 获取当前处理器的执行顺序优先级。 + * + * 默认实现返回0,数值越小优先级越高。 + * + * @return Int 表示当前处理器的顺序值 + */ + override fun order(): Int { + return 40 + } + + /** + * 创建一个函数,用于将给定的上下文数据注入到 Reactor 的 Context 中。 + * + * 遍历字段注册表中的所有字段定义,并将对应的值从输入上下文中取出, + * 然后放入 Reactor 的 Context 中。 + * + * @param ctx 包含要注入字段的映射表(key-value) + * @return Function 返回一个函数,该函数接受原始的 Reactor Context 并返回更新后的 Context + */ + fun injectToReactor(ctx: Map): Function = + Function { rCtx -> + var updated = rCtx + // 遍历所有字段定义,并将对应的值注入到 Reactor Context 中 + reg.all().forEach { def -> + ctx[def.key]?.let { value -> updated = updated.put(def.key, value) } + } + updated + } +} \ No newline at end of file