银行查询服务的规划和完成

  • 项目地址github:github.com/xl-echo/ban…
  • 项目地址gitee:gitee.com/xl-echo/ban…

银行查询服务的规划初衷是:为供给愈加便当的查询服务,咱们在分布式体系架构下,独立开发了与各大银行对接的查询服务。该独立服务支持用户轻松查询账户余额和消费明细的信息,一起确保用户消费的可见性。这种架构规划,不只提升了用户的查询体会、确保了用户的信息安全,更为整个分布式体系的功用和可保护性供给了保证,为用户和第三方付出机构的长期合作奠定了良好的根底。该服务规划以微服务为根底,运用多种规划形式。如:单例,工厂,模板,战略等。其间关于IOC和AOP的应用是体系逻辑结构规划的最中心部分,在高并发的服务下,为了确保数据的安全性,咱们运用IOC和工厂构建的办法,将每个恳求流程独立,确保每个恳求之间数据独立。AOP的运用为整个体系供给了更好的全局处理计划,并且对对接多家银行由于规范不一的问题供给了共同的处理平台,处理了多家银行之间不能够有用共同回来。 The original intention of designing bank query services is to provide more convenient query services. We have independently developed query services that interface with major banks under a distributed system architecture. This independent service supports users to easily query account balance and consumption details, while ensuring visibility of user consumption. This architecture design not only improves users’ query experience and ensures their information security, but also provides guarantees for the performance and maintainability of the entire distributed system, laying a good foundation for long-term cooperation between users and third-party payment institutions. The service design is based on microservices and uses multiple design patterns. For example: single instance, factory, template, strategy, etc. The application of IOC and AOP is the most core part of system logic structure design. In high concurrency services, in order to ensure data security, we use IOC and factory construction methods to make each request process independent and ensure data independence between each request. The utilization of AOP provides a better global processing solution for the entire system, and provides a unified solution platform for connecting multiple banks due to different standards, solving the problem of not being able to effectively unify returns between multiple banks.

开发环境

东西 版别 描绘
IDEA 2019.2
JDK 1.8
MySQL 5.7 5.7 +

技能结构

技能 描绘 用处 备注
SpringBoot 根底结构 构建体系架构
MyBatis ORM结构 构建数据层
Maven 项目办理 项目办理
ExecutorService 线程池 分批履行任务
drools 规矩引擎 简化if else 这儿仅简略完成,用于入参校验
tlog 日志结构 链路追寻 保证长流程下,日志能够方便查询
velocity 模板引擎 将bean转stringXml 银行交互用xml格局的数据
digester3 XML解析东西包 将stringXml转bean 银行回来的数据格局为stringXml(经过对应的解析模板,将银行回来的stringXml类型的信息转成Bean)
fastjson JSON解析东西 将stringJson转bean bean转stringJson
httpclient http东西 银行通讯

独立规划查询服务的来龙去脉

在较高服务恳求下,付出服务为了保证服务的高可用将查询整个链路都进行独立,为付出供给更多的操作内存。一起简化付出服务的保护流程,让付出服务代码结构愈加的明晰简练。付出服务内部(第二/三方)根本都需求对接各大银行,有些服务乃至接入的银行超越上千家,关于服务的容错、共同各行规范有着高教的要求,项目内部在不断的接入银行的情况,各种特别代码,补全查漏层出不穷。导致项目呈现横向臃肿,以及可保护度大幅下降。为了付出服务愈加的清晰,结构愈加有利于保护,查询直接独立。重构查询服务。

体系全体技能结构如下:

【开源项目】银行查询服务的设计和实现

两大规划形式保证逻辑结构清晰,代码规范共同

  • 模板形式 查询服务接入各大银行,在交融各银行恳求和回来规范的时分代码风格不共同,会导致项目紊乱,这儿引进模板形式,用一个接口界说详细履行办法,一切恳求都调用该接口,接口被抽象类完成。运用抽象类界说详细模板办法,一切银行的接入,都需求重写对应的几个模板办法。一起在抽象类里边,对前置如必传参数校验和公共补全部分进行补齐。详细完成如下:
public interface ProcessHandler {
    ResultContext invoke(QueryContext queryContext);
}
public abstract class AbstractProcessHandler implements ProcessHandler {
    @Override
    public ResultContext invoke(QueryContext queryContext) {
        String queryFlag = queryContext.getQueryFlag();
        log.info("开端履行查询,查询类型为:{}", queryFlag);
        // 参数校验
        checkParam(queryContext);
        // 公共参数补齐
        paramComplement(queryContext);
        // 负载均衡
        loadBalance(queryContext);
        // 履行查询
        if (QueryFlag.BALANCE.getDesc().equals(queryFlag)) {
            return this.queryBalance(queryContext);
        }
        if (QueryFlag.DETAIL.getDesc().equals(queryFlag)) {
            return this.queryDetail(queryContext);
        }
        return new ResultContext();
    }
    private void loadBalance(QueryContext queryContext) {
        SpringboardMachineAddrService springboardMachineAddrService = SpringContextUtils.getBean("springboardMachineAddrService", SpringboardMachineAddrService.class);
        List<SpringboardMachineAddr> springboardMachineAddrs = springboardMachineAddrService.selectByBankCode(queryContext.getBankCode());
        if(CollectionUtils.isEmpty(springboardMachineAddrs)) {
            throw new BankException("未查询到对应的负载均衡地址信息!");
        }
        Random rand = new Random();
        int randomIndex = rand.nextInt(springboardMachineAddrs.size());
        SpringboardMachineAddr springboardMachineAddr = springboardMachineAddrs.get(randomIndex);
        queryContext.setChannelId(springboardMachineAddr.getChannelId());
        queryContext.setMachineId(springboardMachineAddr.getMachineId());
        queryContext.setSignIp(springboardMachineAddr.getSignIp());
        queryContext.setSignPort(springboardMachineAddr.getSignPort());
        queryContext.setTradeIp(springboardMachineAddr.getTradeIp());
        queryContext.setTradePort(springboardMachineAddr.getTradePort());
    }
    private void paramComplement(QueryContext queryContext) {}
    private void checkParam(QueryContext queryContext) {
        log.info("开端履行规矩引擎参数校验");
        KieSession kieSession = SpringContextUtils.getBean("kieSession", KieSession.class);
        // 履行某个组的规矩
        kieSession.getAgenda().getAgendaGroup("checkParam").setFocus();
        kieSession.insert(queryContext);
        // 履行一切规矩
        kieSession.fireAllRules();
        // 履行指定规矩
//        kieSession.fireAllRules(new AgendaFilter() {
//            @Override
//            public boolean accept(Match match) {
//                String ruleName = match.getRule().getName();
//                return Objects.equals(ruleName, "checkQueryBalanceParamCurrencyCode");
//            }
//        });
        kieSession.dispose();
        log.info("规矩引擎参数校验完成");
    }
    protected abstract ResultContext queryBalance(QueryContext queryContext);
    protected abstract ResultContext queryDetail(QueryContext queryContext);
}
public class ICBC10201 extends AbstractProcessHandler {
    @Override
    protected ResultContext queryBalance(QueryContext queryContext) {
        log.info("开端履行余额查询!办法:queryBalance被履行");
        // bean to string xml
        String s = javaBeanToStrXml(queryContext);
        log.info("bean to string xml success! xml content: {}", s);
        // to bank sign
        String signMsg = "urj&*^534faj;";
        String signStr = sign(signMsg);
        log.info("sign success! signStr: {}", signStr);
        // send query content into bank
        String result = sendQueryContentIntoBank(signStr + JSON.toJSONString(queryContext));
        // string xml to bean
        BalanceResult balanceResult = strXmlToJavaBean(bankDataModel(), queryContext);
        log.info("string xml to bean sccess! bean content: {}", JSON.toJSONString(balanceResult));
        ResultContext resultContext = new ResultContext();
        resultContext.setStatus("0000");
        resultContext.setBalanceResult(balanceResult);
        return resultContext;
    }
    @Override
    protected ResultContext queryDetail(QueryContext queryContext) {
        log.info("开端履行明细查询!办法:queryDetail被履行");
        ResultContext resultContext = new ResultContext();
        resultContext.setStatus("0000");
        return new ResultContext();
    }
    private String sendQueryContentIntoBank(String queryContent) {
        SocketConnector socketConnector = new SocketConnector("127.0.0.1", 8081);
        return socketConnector.request(queryContent);
    }
    private String sign(String signMsg) {
        return HttpUtils.sendPostJson(signMsg, "");
    }
    private String javaBeanToStrXml(QueryContext queryContext) {
        String ruleModeName = "templates/request-" + queryContext.getBankCode() + "-" + queryContext.getQueryFlag() + ".vm";
        ParserEngine parserEngine = SpringContextUtils.getBean("parserEngine", ParserEngine.class);
        Map<String, Object> stringObjectMap = JSON.parseObject(JSON.toJSONString(queryContext), new TypeReference<Map<String, Object>>() {
        });
        return parserEngine.beanToStringXml(ruleModeName, stringObjectMap);
    }
    private BalanceResult strXmlToJavaBean(String bankResultStrXml, QueryContext queryContext) {
        ParserEngine parserEngine = SpringContextUtils.getBean("parserEngine", ParserEngine.class);
        return parserEngine.parseXml2Object(bankResultStrXml, "response-" + queryContext.getBankCode() + "-" + queryContext.getQueryFlag());
    }
    private String bankDataModel() {
        return "";
    }
}

模板办法规划形式是行为型规划形式中的一种,用在一个功用的完成需求经过一系列过程,这些过程是固定的,可是中间某些过程详细行为是待定的,在不同的场景中行为不同。这儿界说了详细的整个流程需求履行的过程,可是关于详细的对接某个银行的办法,仍是留给了详细的银行来完成,详细的UML图如下:

【开源项目】银行查询服务的设计和实现

  • 战略形式 在查询服务中,查询是咱们最中心功用,都是做了同一件事情,可是接入的银行超越两个的时分,查询的办法或许完成代码必定有所不同,在上面咱们用模版办法形式,界说了整个查询的流程根本构成,可是详细的查询完成却没有办法对每一个银行进行共同。一起假如咱们要走哪一个银行来查询,仅有模版形式必定会多出来许多的if else,这儿咱们用上战略形式就能完美的处理这些问题,运用了一个接口,就界说了整个查询,让后咱们的查询能够在各个银行之间有用的切换。详细完成代码如下:
public interface ProcessHandler {
    ResultContext invoke(QueryContext queryContext);
}
public abstract class AbstractProcessHandler implements ProcessHandler {
    // 此处省略……
}
@Component(value = "102")
public class ICBC10201 extends AbstractProcessHandler {
    // 此处省略……
}
// 战略表现
public class BankInquiryController {
    public Result<ResultContext> getDetail(@RequestBody QueryContext queryContext) {
        ProcessHandler bean = SpringContextUtils.getBean(queryContext.getBankCode(), ProcessHandler.class);
        ResultContext invoke = bean.invoke(queryContext);
    }
}

战略形式在这儿很有用的去除了if else,一起对外露出查询功用时,供给了多银行之间自在切换的才能。详细完成UML图如下:

【开源项目】银行查询服务的设计和实现

日志的选型,日志规划(链路追寻)

在高并发的情况下,查询假如呈现问题,保护也是一个比较复杂的作业,比方:咱们需求去排查某一次查询为什么呈现了问题的时分,这次呈现问题的时刻点内恳求量很大,那咱们需求费一些时刻来定位到咱们当时日志是不是某一次恳求,在整个项目傍边每次恳求有对应许多日志的情况下,那咱们需求把每一行日志对应到这次出问题的恳求上,就需求很长的耗时。特别是公司内部,假如都是微服务,把体系拆分的很细的情况下,那上下游的排查就会有很大的定位问题,当然有的小伙伴就会想到运用SkyWalking,Pinpoint等分布式追寻体系来处理,并且这些体系一般都是无侵入性的,一起也会供给相对友爱的办理界面来进行链路Span的查询,可是搭建分布式追寻体系仍是需求一定的本钱的,所以本文要说的并不是这些分布式追寻体系,而是一款简略、易用、几乎零侵入、适合中小型公司运用的日志追寻结构TLog。一起运用TLog也能有用的让咱们内部的日志,能够快速的定位到某一次恳求的一切日志。TLog会主动的对日志进行打标签,主动生成traceId贯穿你微服务的一整条链路,在排查日志的时分,能够依据traceId来快速定位恳求处理的链路。在项目内的表现如下:

【开源项目】银行查询服务的设计和实现

  • TLog的优点 TLog不搜集日志,只在原来打印的日志上增强,将恳求链路信息traceId绑定到打印的日志上

操作日志的搜集

关于一个查询来讲其实提到搜集日志,或许有人觉得这是多此一举,当咱们回忆这个服务的详细功用之后,咱们会发现与多家银行的交互,这是对外共同供给服务的,作为一个查询银行的服务,咱们需求有用的去保证对接的银行都能够有用的供给服务,这样咱们的操作日志就很有必要了。也能有用的去规避一些由于银行而导致的客户投诉。这儿的操作日志搜集,仍是用的老一套,AOP+注解来完成。SpringBoot中做日志的办法有许多,比方用拦截器,在拦截器中进行处理需求进行搜集日志的办法,一起也能够将日志存库,可是这种办法或许会有一个弊端,在拦截器中进行处理日志的话,关于恳求量过大的体系,或许处理次数过多的,以及并发过高的项目来说,都是一个可怕的功用耗费。aop做日志处理的关键原因,量级小,简略,一起运用线程池异步的办法,有用的环节高并发的恳求日志记载问题

界说的注解如下:

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
    /**
     * 履行办法功用描绘
     */
    String desc() default "";
    /**
     * 1:发送, 0:接纳
     */
    String type() default "0";
}

在银行查询服务中,数据最关键的不是落库,而是与银行交互,然后处理数据,终究共同回来,所以咱们不只要记载被恳求的日志,还需求记载向外发送的恳求日志,已报账发送的恳求都是符合规范的。这也是为什么需求记载操作日志的主要原因之一

AOP的完成如下:

package com.echo.bank.framework.aspect;
import com.alibaba.fastjson.JSONObject;
import com.echo.bank.enums.StatusCode;
import com.echo.bank.framework.ioc.SpringContextUtils;
import com.echo.bank.framework.thread.ExecutorServiceUtil;
import com.echo.bank.pojo.OperateLog;
import com.echo.bank.service.OperateLogService;
import com.echo.bank.utils.RequestUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.concurrent.ExecutorService;
/**
 * @author echo
 * @version 1.0
 * Create by 2023/5/25 9:19
 */
@Slf4j
@Aspect
@Component
public class OperationLogAspect {
    @Autowired
    private OperateLogService operateLogService;
    /**
     * 扫描运用这个注解的办法
     */
    @Pointcut("@annotation(com.echo.bank.framework.aspect.OperationLog)")
    public void logPointCut() {
    }
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Date beginTime = new Date();
        String result = null;
        String status = null;
        try {
            Object obj = point.proceed();
            if (obj != null) {
                result = JSONObject.toJSONString(obj);
            }
            status = StatusCode.SUCCESS.getCode() + "";
            return obj;
        } catch (Exception e) {
            //恳求履行犯错
            result = e.getMessage();
            status = StatusCode.ERROR.getCode() + "";
            throw e;
        } finally {
            saveLog(point, beginTime, new Date(), result, status);
        }
    }
    /**
     * 保存日志
     */
    private void saveLog(ProceedingJoinPoint joinPoint, Date beginTime, Date endTime, String result, String status) {
        OperateLog operateLog = new OperateLog();
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            OperationLog annotation = method.getAnnotation(OperationLog.class);
            if (annotation != null) {
                operateLog.setDesc(annotation.desc());
                operateLog.setType(annotation.type());
            }
            if (joinPoint.getArgs() != null) {
                try {
                    operateLog.setParamContext(JSONObject.toJSONString(joinPoint.getArgs()));
                } catch (Exception e) {
                    log.info("解析并记载恳求参数呈现过错! msg: {}", e.getMessage());
                }
            }
            operateLog.setCreateUser("echo");
            operateLog.setCreateTime(beginTime);
            operateLog.setUpdateTime(endTime);
            operateLog.setStatus(status);
            operateLog.setResultContext(result);
            setRequestIp(operateLog);
        } catch (Exception e) {
            log.info("记载恳求日志时初始化日志目标报错! msg: {}", e.getMessage());
        }
        ExecutorService instance = ExecutorServiceUtil.getInstance();
        instance.execute(new OperationLogSaveThread(operateLogService, operateLog));
    }
    private void setRequestIp(OperateLog operateLog) {
        try {
            RequestUtils requestUtils = SpringContextUtils.getBean("requestUtils", RequestUtils.class);
            HttpServletRequest httpServletRequest = requestUtils.getHttpServletRequest();
            String requestIp = requestUtils.getRequestIp(httpServletRequest);
            operateLog.setRequestIp(requestIp);
        } catch (Exception e) {
            // 向外恳求的时分,运用该IP
            operateLog.setRequestIp("127.0.0.1");
        }
    }
}

这儿只是运用AOP还不能有用的搜集日志,这个搜集日志的操作还会让咱们的体系功用下降,导致查询缓慢。所以咱们还需求异步的去履行,这样就能有用的保证日志能被有用的记载,一起不影响体系问题。线程池引进,如下:

@Slf4j
public class OperationLogSaveThread implements Runnable {
    private OperateLogService operateLogService;
    private OperateLog operateLog;
    public OperationLogSaveThread(OperateLogService operateLogService, OperateLog operateLog) {
        this.operateLogService = operateLogService;
        this.operateLog = operateLog;
    }
    @Override
    public void run() {
        log.info("开端异步履行记载恳求日志!");
        try {
            operateLogService.insert(operateLog);
        } catch (Exception e) {
            log.info("异步记载日志呈现报错!msg: {}", e.getMessage());
        }
    }
}
public class ExecutorServiceUtil {
    private static final Logger logger = LoggerFactory.getLogger(ExecutorServiceUtil.class);
    private static ThreadPoolExecutor executorService;
    private static final int INT_CORE_POOL_SIZE = 50;
    private static final int MAXIMUM_POOL_SIZE = 100;
    private static final int KEEP_ALIVE_TIME = 60;
    private static final int WORK_QUEUE_SIZE = 2000;
    static {
        executorService = new ThreadPoolExecutor(
                INT_CORE_POOL_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(WORK_QUEUE_SIZE),
                new CommunityThreadFactory(),
                new DataMigrationRejectedExecutionHandler());
    }
    private ExecutorServiceUtil() {
        if (executorService != null) {
            throw new BankException("Reflection call constructor terminates execution!!!");
        }
    }
    public static ExecutorService getInstance() {
        logger.info("queue size: {}", executorService.getQueue().size());
        logger.info("Number of active threads: {}", executorService.getActiveCount());
        logger.info("Number of execution completion threads: {}", executorService.getCompletedTaskCount());
        return executorService;
    }
}
public class DataMigrationRejectedExecutionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        try {
            // 中心改造点,由blockingqueue的offer改成put阻塞办法
            executor.getQueue().put(r);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class CommunityThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    CommunityThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        namePrefix = "dataMigration-" + poolNumber.getAndIncrement() + "-thread-";
    }
    /**
     * 创建线程
     *
     * @param r
     * @return
     */
    @Override
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

这样的完成,既保证了被调用的时分一切恳求的日志都记载了,一起也保证了发送出去的恳求也能有用记载。详细图示:

【开源项目】银行查询服务的设计和实现

规矩引擎

规矩引擎的引进主要的原因就两个:1、if else过多,用以简化代码 2、小试牛刀

规矩引擎最大的效果便是灵活的将规矩和事务剥离,处理了灵活改变规矩不影响详细事务的难点。选择Drools的理由:Drools 是用 Java 语言编写的具有一个易于拜访企业战略、易于调整以及易于办理的开源事务规矩引擎,其根据CHARLES FORGY’S的RETE算法符合业内规范,速度快且效率高

引进的Drools版别如下:

<!--drools规矩引擎-->
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-core</artifactId>
    <version>7.6.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.6.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-templates</artifactId>
    <version>7.6.0.Final</version>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-api</artifactId>
    <version>7.6.0.Final</version>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-spring</artifactId>
    <version>7.6.0.Final</version>
</dependency>
  • Drools规矩引擎详细在项目中做了件什么事情? 在咱们的代码中,有许多的参数校验代码:
if(checkParam1) throw new BankExcption("");
if(checkParam2) throw new BankExcption("");
if(checkParam3) throw new BankExcption("");
if(checkParam4) throw new BankExcption("");
if(checkParam5) throw new BankExcption("");
if(checkParam6) throw new BankExcption("");
if(checkParam7) throw new BankExcption("");

这类代码,不只在咱们接口被调用的时分会呈现,也会呈现在银行回来的时分,对某些特定数据校验,判断当时恳求是否成功。假如事务产生改变,或许银行内部改变,咱们这边就需求从头编码,然后上线。可是drools的引进就完美的处理了这一问题,咱们能够从头编写规矩文件,替换规矩文件之后,从头加载即可,不需求从头发布体系。一起简化了if else.运用了drools的详细代码如下:

private void checkParam(QueryContext queryContext) {
    log.info("开端履行规矩引擎参数校验");
    KieSession kieSession = SpringContextUtils.getBean("kieSession", KieSession.class);
    // 履行某个组的规矩
    kieSession.getAgenda().getAgendaGroup("checkParam").setFocus();
    kieSession.insert(queryContext);
    // 履行一切规矩
    kieSession.fireAllRules();
    kieSession.dispose();
    log.info("规矩引擎参数校验完成");
}

处理并发代码的时刻安全问题

对接银行或许说付出行业的全体技能更新是比较缓慢的,有或许有许多当地还在用ext等老古董结构。在这种前提下, 许多项目的内部往往总是有老结构固定的架构规范,导致内部代码结构难以真实的去修正。这儿直接全部打破,运用全新的技能,来重构项目,这样不只处理了老结构的限制,一起也为咱们重构供给了更好的环境。当然新结构带来的问题也会随之而来,比方并发的处理,在老结构中,许多static变量的兼容,尽管拜读了许多,并且也有很完美的处理计划去防止并发,可是假如将这些东西搬到新结构就会带来许多的数据安全问题。所以这儿采用了单次恳求内数据全部独立的办法。

其中心思想便是每次恳求,当时恳求的一切数据都不共享。完成的办法也是比较简略的,直接运用了Spring Bean的效果域来处理这类问题。

【开源项目】银行查询服务的设计和实现

中心代码如下:

@Scope(value = "prototype")
@Component(value = "102")
public class ICBC10201 extends AbstractProcessHandler {
    ……
}
ProcessHandler bean = SpringContextUtils.getBean(queryContext.getBankCode(), ProcessHandler.class);

每一次恳求都运用一个独立的目标,目标内无公共变量,保证整个恳求链上没有对其他的恳求露出出来的数据,真实有用的规避数据安全危险。

总结

处理高并发痛点,一起优化规划为对接多个银行供给共同规范。或许还有更多的优异计划能够完美处理这些痛点,当时项目也不一定完美,关于技能的运用或许也会有些不尽人意。这儿并不完美,希望更多的大佬们指正。