From d63cab92dc65a5d9c4b473baf1d3084619dcc30d Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Sun, 28 Jun 2026 21:54:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor(context):=20PaymentContext=E8=84=B1?= =?UTF-8?q?=E7=A6=BBSpring=20Scope,=20=E6=94=B9=E4=B8=BA=E7=BA=AFThreadLoc?= =?UTF-8?q?al+=E9=97=A8=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除PaymentScope(自定义Scope)和PaymentScopeConfig(BeanFactoryPostProcessor); 新建PaymentContextHolder纯ThreadLocal管理上下文数据(bind/unbind/current); PaymentContext从@Scope CGLIB代理改为@Component单例门面, 方法代理到Holder, 17个注入点(CustomTenantLineHandler/MchTenantMetaObjectHandler/管理端Service等)零改动透明兼容; PaymentScopeManager的start/end改为Holder bind/unbind。 --- .../common/context/PaymentContext.java | 25 ++---- .../common/context/PaymentContextHolder.java | 56 ++++++++++++ .../payment/common/context/PaymentScope.java | 86 ------------------- .../common/context/PaymentScopeConfig.java | 22 ----- .../common/context/PaymentScopeManager.java | 9 +- 5 files changed, 69 insertions(+), 129 deletions(-) create mode 100644 daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentContextHolder.java delete mode 100644 daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScope.java delete mode 100644 daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScopeConfig.java diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentContext.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentContext.java index 05d15274e..202b758f0 100644 --- a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentContext.java +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentContext.java @@ -1,28 +1,21 @@ package cn.daxpay.open.payment.common.context; -import lombok.Getter; -import lombok.Setter; -import lombok.experimental.Accessors; -import org.springframework.context.annotation.Scope; -import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.stereotype.Component; -/// # 统一支付上下文 +/// # 统一支付上下文(门面) /// -/// 合并原 PaymentContextLocal/MchContextLocal 两个ThreadLocal的信息 -/// 使用自定义作用域 "payment", 底层基于ThreadLocal, 兼容HTTP/定时任务/MQ场景 +/// 保持 @Component 单例,对 17 个注入点透明。实际数据由 [PaymentContextHolder] 通过 ThreadLocal 管理。 +/// MyBatis 拦截器(租户隔离、自动填充)通过此门面获取当前线程的商户/应用信息。 @Component -@Scope(value = PaymentScope.SCOPE_NAME, proxyMode = ScopedProxyMode.TARGET_CLASS) -@Getter -@Setter -@Accessors(chain = true) public class PaymentContext { - // ===== 子对象 ===== - /// 交易信息(商户/应用/合作方) - private final TradeInfo tradeInfo = new TradeInfo(); + public TradeInfo getTradeInfo() { + return PaymentContextHolder.getTradeInfo(); + } /// 回调相关信息 - private final CallbackInfo callbackInfo = new CallbackInfo(); + public CallbackInfo getCallbackInfo() { + return PaymentContextHolder.getCallbackInfo(); + } } diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentContextHolder.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentContextHolder.java new file mode 100644 index 000000000..20b560e52 --- /dev/null +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentContextHolder.java @@ -0,0 +1,56 @@ +package cn.daxpay.open.payment.common.context; + +/// # 支付上下文 ThreadLocal 持有者 +/// +/// 纯 ThreadLocal 实现,替代原 Spring 自定义 Scope(PaymentScope)。 +/// 由 Filter/Manager 在请求入口 bind,出口 unbind;MyBatis 拦截器通过 PaymentContext 门面间接访问。 +public final class PaymentContextHolder { + + private PaymentContextHolder() { + } + + private static final ThreadLocal HOLDER = new ThreadLocal<>(); + + /// 绑定新上下文到当前线程 + public static void bind() { + if (HOLDER.get() != null) { + throw new IllegalStateException("PaymentContext already active on this thread"); + } + HOLDER.set(new ContextData()); + } + + /// 解除当前线程的上下文绑定 + public static void unbind() { + HOLDER.remove(); + } + + /// 当前线程是否已绑定上下文 + public static boolean isBound() { + return HOLDER.get() != null; + } + + /// 获取当前线程的交易信息 + public static TradeInfo getTradeInfo() { + return current().tradeInfo; + } + + /// 获取当前线程的回调信息 + public static CallbackInfo getCallbackInfo() { + return current().callbackInfo; + } + + /// 获取当前上下文数据(未绑定则抛异常) + private static ContextData current() { + ContextData data = HOLDER.get(); + if (data == null) { + throw new IllegalStateException("PaymentContext not active on this thread"); + } + return data; + } + + /// 每次请求的上下文数据 + private static class ContextData { + final TradeInfo tradeInfo = new TradeInfo(); + final CallbackInfo callbackInfo = new CallbackInfo(); + } +} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScope.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScope.java deleted file mode 100644 index f9e1272b1..000000000 --- a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScope.java +++ /dev/null @@ -1,86 +0,0 @@ -package cn.daxpay.open.payment.common.context; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.ObjectFactory; -import org.springframework.beans.factory.config.Scope; - -import java.util.concurrent.ConcurrentHashMap; - -/// # 支付自定义作用域, 底层基于ThreadLocal实现 -/// -/// 兼容HTTP请求、定时任务、MQ消费者等场景 -public class PaymentScope implements Scope, DisposableBean { - - public static final String SCOPE_NAME = "payment"; - - private final ThreadLocal threadLocal = new ThreadLocal<>(); - - private ObjectFactory objectFactory; - - @Override - public Object get(String name, ObjectFactory objectFactory) { - if (this.objectFactory == null) { - this.objectFactory = objectFactory; - } - PaymentContext context = threadLocal.get(); - if (context == null) { - context = (PaymentContext) objectFactory.getObject(); - threadLocal.set(context); - } - return context; - } - - @Override - public Object remove(String name) { - PaymentContext context = threadLocal.get(); - threadLocal.remove(); - return context; - } - - /// 开启作用域, 创建PaymentContext并绑定到当前线程 - public void start() { - if (threadLocal.get() != null) { - throw new IllegalStateException("PaymentScope already active on this thread"); - } - if (objectFactory != null) { - get(SCOPE_NAME, objectFactory); - } else { - threadLocal.set(new PaymentContext()); - } - } - - /// 结束作用域, 清除当前线程的PaymentContext - public void end() { - threadLocal.remove(); - } - - /// 获取当前上下文 - /// 用于非Spring管理的类或需要静态访问的场景 - public PaymentContext current() { - PaymentContext context = threadLocal.get(); - if (context == null) { - throw new IllegalStateException("PaymentScope not active on this thread"); - } - return context; - } - - @Override - public void registerDestructionCallback(String name, Runnable callback) { - } - - @Override - public Object resolveContextualObject(String key) { - return null; - } - - @Override - public String getConversationId() { - return Thread.currentThread().getName(); - } - - @Override - public void destroy() { - threadLocal.remove(); - } -} - diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScopeConfig.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScopeConfig.java deleted file mode 100644 index 486523c83..000000000 --- a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScopeConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.daxpay.open.payment.common.context; - -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/// # 支付作用域注册配置 -/// -@Configuration -public class PaymentScopeConfig implements BeanFactoryPostProcessor { - - @Bean - public PaymentScope paymentScope() { - return new PaymentScope(); - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { - beanFactory.registerScope(PaymentScope.SCOPE_NAME, beanFactory.getBean(PaymentScope.class)); - } -} diff --git a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScopeManager.java b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScopeManager.java index 7d79daadf..7ddda29fa 100644 --- a/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScopeManager.java +++ b/daxpay-payment/daxpay-payment-common/src/main/java/cn/daxpay/open/payment/common/context/PaymentScopeManager.java @@ -1,5 +1,6 @@ package cn.daxpay.open.payment.common.context; +import cn.daxpay.open.payment.common.context.PaymentContextHolder; import cn.daxpay.open.payment.old.pay.service.assist.PaymentAssistService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -13,25 +14,23 @@ import java.util.function.Supplier; @RequiredArgsConstructor public class PaymentScopeManager { - private final PaymentScope paymentScope; - private final PaymentAssistService paymentAssistService; /// 开启作用域(空上下文) public void start() { - paymentScope.start(); + PaymentContextHolder.bind(); } /// 开启作用域并初始化商户信息 /// 用于定时任务/MQ消费者等非HTTP场景 public void startWithMch(String mchNo, String appId) { - paymentScope.start(); + PaymentContextHolder.bind(); paymentAssistService.initMchAndApp(mchNo, appId); } /// 结束作用域 public void end() { - paymentScope.end(); + PaymentContextHolder.unbind(); } /// 在作用域内执行(自动管理生命周期, 同步执行)