mirror of
https://gitee.com/dromara/dax-pay
synced 2026-08-11 23:25:33 +08:00
refactor(context): PaymentContext脱离Spring Scope, 改为纯ThreadLocal+门面
删除PaymentScope(自定义Scope)和PaymentScopeConfig(BeanFactoryPostProcessor); 新建PaymentContextHolder纯ThreadLocal管理上下文数据(bind/unbind/current); PaymentContext从@Scope CGLIB代理改为@Component单例门面, 方法代理到Holder, 17个注入点(CustomTenantLineHandler/MchTenantMetaObjectHandler/管理端Service等)零改动透明兼容; PaymentScopeManager的start/end改为Holder bind/unbind。
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ContextData> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<PaymentContext> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/// 在作用域内执行(自动管理生命周期, 同步执行)
|
||||
|
||||
Reference in New Issue
Block a user