热爱技术,追求卓越
不断求索,精益求精

分布式支付中心设计,配置中心@RefreshScope注意事项

对接上游支付接口,定义一个BaseHandler,其中getPayType用于获取当前Handler的支付类型。支付的核心逻辑和公共处理都在BaseHandler,个性化的处理交给各个实现类。

public abstract class BaseHandler {
    /**
     * 获取支付类型
     * @return
     */
    public abstract PayType getPayType();
...
}

不同的支付渠道,有不同的Handler,如AHandler、BHandler、CHandler,由于每个支付渠道的参数不同还有可能随时改动,所以我们使用了spring cloud的分布式配置中心,自然就用到了@RefreshScope注解。

@RefreshScope
@Component
public class AHandler extends BaseHandler {
...
}


@RefreshScope
@Component
public class BHandler extends BaseHandler {
...
}


@RefreshScope
@Component
public class CHandler extends BaseHandler {
...
}

支付工厂类进行接口分发:

如下的PayFactory,通过getHandler来获取处理器,不同的支付渠道有不同的处理器。springboot项目启动时接收到ApplicationReadyEvent事件,则通过获取所有继承了BaseHandler的类的相关实例,并缓存到handlerMap中。

@Component
public class PayFactory implements ApplicationListener<ApplicationReadyEvent>{

     protected static Logger log = LoggerFactory.getLogger("run") ;

    /**支付处理器map*/
    private Map<Integer, BaseHandler> handlerMap = new HashMap<Integer, BaseHandler>();

    @Autowired
    private ApplicationContext applicationContext;



    /* (non-Javadoc)
     * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
     */
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        log.info(" ==============初始化支付接口处理器开始 ==============");
        Map<String, BaseHandler> handlerBeanMap = applicationContext.getBeansOfType(BaseHandler.class, false, false);
        //用下面的方式获取bean,可能会获取到同一个类的多个实例
        //Map<String, BaseHandler> handlerBeanMap = applicationContext.getBeansOfType(BaseHandler.class);
        for(BaseHandler handler : handlerBeanMap.values()){
            log.info("初始化支付接口处理器:{}" , handler.getClass().getName());
            PayType payType = handler.getPayType();
            if(payType == null){
                throw new EcommerceException("支付处理器未明确支付类型");
            }else if(handlerMap.containsKey(payType.getCode())){
                throw new EcommerceException("同一种支付方式发现多个处理器," + payType.getCode());
            }else{
                handlerMap.put(payType.getCode(), handler);
            }
        }
        log.info(" ==============初始化支付接口处理器结束 ==============");
    }


    public PayResult pay(PayContext context) throws EcommerceException {
        BaseHandler handler = getHandler(context.getPayTypeCode());
        return handler.pay(context);
    }


    public RefundResult refund(RefundContext context) throws EcommerceException {
        BaseHandler handler = getHandler(context.getPayTypeCode());
        return handler.refund(context);
    }

    /**
     * 同步调用
     *
     * @param request
     * @param response
     * @throws IOException
     */
    public void synNotify(PayType payType, HttpServletRequest request, HttpServletResponse response) throws IOException {
        BaseHandler handler = getHandler(payType.getCode());
        handler.synNotify(request, response);
    }


    /**
     * 向第三方发起请求,检查支付状态或退款状态,若状态发生变化,则进行
     * 数据库更新以及业务通知处理
     * @param payInfo
     * @return 返回true则状态有变化,返回false检测结果无变化
     */
    public boolean check(PayInfo payInfo) {
        BaseHandler handler = getHandler(payInfo.getPayType());
        return handler.check(payInfo);
    }

    /**
     * 异步调用
     *
     * @param request
     * @param response
     * @throws IOException
     */
    public void asynNotify(PayType payType, HttpServletRequest request, HttpServletResponse response) throws IOException {
        BaseHandler handler = getHandler(payType.getCode());
        handler.asynNotify(request, response);
    } 

    /**
     * 支付报关接口,跨境电商使用
     * @param query
     * @return
     * @throws EcommerceException
     */
    public PayDeclareResult customsDeclare(PayDeclareQuery query) throws EcommerceException{
        BaseHandler handler = getHandler(query.getPayTypeCode());
        return handler.customsDeclare(query);
    }

    public PayDeclareResult getCustomsDeclareResult(PayDeclareQuery query) throws EcommerceException {
        BaseHandler handler = getHandler(query.getPayTypeCode());
        return handler.getCustomsDeclareResult(query);
    }

    /**
     * 根据支付类型code获取处理器
     * @param payTypeCode
     * @return
     */
    private BaseHandler getHandler(Integer payTypeCode){
        BaseHandler handler = handlerMap.get(payTypeCode);
        if(handler == null){
            throw new EcommerceException("不支持的支付类型:" + payTypeCode);
        }
        return handler;
    }

}

@RefreshScope的相关注意事项:

在PayFactory中的onApplicationEvent方法中,最开始没有使用分布式配置中心时,时这样获取bean的:

Map<String, BaseHandler> handlerBeanMap = applicationContext.getBeansOfType(BaseHandler.class);

使用了分布式配置中心后,报错如下:

2023-03-31 14:34:57.143 |-INFO  [main] run [cn.x.x.pay.factory.PayFactory-64] -| 初始化支付接口处理器:cn.x.x.handler.AHandlerEnhancerBySpringCGLIBb9a535ff
2023-03-31 14:34:57.182 |-INFO  [main] run [cn.x.x.pay.factory.PayFactory-64] -| 初始化支付接口处理器:cn.x.x.pay.handler.AHandlerEnhancerBySpringCGLIB20480174
2023-03-31 14:34:57.205 |-ERROR [main] org.springframework.boot.SpringApplication [org.springframework.boot.SpringApplication-830] -| Application run failed
cn.x.x.pay.common.exception.GlobleException: 同一种支付方式发现多个处理器,1

原因是创建Bean的时候如果是RefreshScope就缓存在一个专门管理的ScopeMap中,这样就可以管理Scope是Refresh的Bean的生命周期了(所以含RefreshScope的其实一共创建了两个bean)。

所以如果使用了RefreshScope,我们获取bean的时候使用下入方式就可以了!

Map<String, BaseHandler> handlerBeanMap = applicationContext.getBeansOfType(BaseHandler.class, false, false);
赞(1)
未经允许不得转载:LoveCTO » 分布式支付中心设计,配置中心@RefreshScope注意事项

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

热爱技术 追求卓越 精益求精