news 2026/10/21 4:00:17

SpringBoot+SAP RFC集成方案:Quartz重试与幂等设计避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+SAP RFC集成方案:Quartz重试与幂等设计避坑指南

简介:本资源是一套基于SpringBoot的Java后台管理系统源码,面向需要将SAP系统数据与外部系统打通的企业开发者和Java后端学习者。项目整合Spring、Spring MVC、MyBatis-Plus、Druid、Shiro、Thymeleaf、Quartz等技术栈,核心亮点是通过sapjco3调用SAP的RFC函数实现数据获取与回传,并以接口形式对外提供服务,同时借助Quartz完成定时同步任务,前端采用轻量级路由与MVVM方案,实现单页面、响应式、简约的后台界面。压缩包共336个文件,以98个java源码、41个js脚本、28个html页面、25个css样式为主,另含xml、yml、properties配置及sql脚本、字体与本地库文件,整体约9.46MB,结构完整便于二次开发。目前已有432人学习下载,适合想掌握SAP RFC集成、权限控制与定时任务调度的中高级开发者参考借鉴。

1. 从一次接口对账说起:这套 SAP 集成方案到底解决什么问题

做过企业系统对接的工程师大概都遇到过这种场景:业务系统里跑完一轮采购入库,财务同事拿着 SAP 里的凭证号来对账,两边数据对不上,查日志发现 RFC 调用超时了,但业务单据已经提交,SAP 那边却没收到。这种「业务已提交、SAP 未落账」的中间态,是 SAP 集成项目里最典型的翻车现场。这套方案要解决的核心问题就是:用 SpringBoot + MyBatis + Thymeleaf + Quartz 2 + Shiro + sapjco 搭一套 Web 系统,把与 SAP 的 RFC 交互做成可管理、可重试、可审计的闭环,而不是散落在业务代码里的几行 JCo 调用。它适合正在做企业级系统集成毕设的学生,也适合需要交付一套 SAP 外围系统方案的从业者。关键词 JavaScript 在这里的角色是前端交互层,Thymeleaf 模板里嵌的校验和异步请求都靠它兜底。

2. 技术选型与总体架构:六个组件各自扛什么活

2.1 为什么是这套组合而不是别的

先把选型逻辑讲清楚,不然后面模块划分没有依据。SpringBoot 负责容器和自动装配,省掉大量 XML;MyBatis 做数据持久化,因为 SAP 交互日志、任务记录、用户权限这些表结构相对固定,用注解加 XML 混合写 SQL 比 JPA 更可控;Thymeleaf 做服务端渲染,适合后台管理类页面,不需要前后端分离那套构建链;Quartz 2 承担定时任务和失败重试调度;Shiro 管认证授权;sapjco 是 SAP 官方提供的 Java 连接器,负责 RFC 调用。

这里有个容易踩的坑:sapjco 的本地库文件(.so 或 .dll)必须和 JVM 架构匹配,32 位 JDK 配 64 位 sapjco 直接报 UnsatisfiedLinkError。我一般会在项目启动时加一个环境自检,把 sapjco3.jar 和本地库路径打印出来,省得部署到服务器才发现。

2.2 分层结构与模块职责

整体分四层:接入层(Controller + Thymeleaf)、业务层(Service)、集成层(SAP RFC 调用封装)、调度层(Quartz Job)。每层职责边界要卡死,尤其是集成层,所有 JCo 连接、目标系统、仓库对象的获取和销毁都必须在这一层完成,业务层不允许直接碰 JCoDestination。

// 集成层:SAP RFC 调用统一封装 @Component public class SapRfcExecutor { // 从连接池获取目标,避免每次新建连接 public JCoFunction execute(String functionName, Map<String, Object> params) { JCoDestination destination = null; try { destination = JCoDestinationManager.getDestination("SAP_PRD"); JCoFunction function = destination.getRepository().getFunction(functionName); if (function == null) { throw new RuntimeException("RFC 函数不存在: " + functionName); } // 参数填充,按 SAP 结构逐字段映射 params.forEach((k, v) -> function.getImportParameterList().setValue(k, v)); function.execute(destination); return function; } catch (JCoException e) { // 记录失败日志,交给 Quartz 重试 throw new SapRfcException("RFC 调用失败: " + functionName, e); } } }

这段代码的关键点在于:目标系统通过JCoDestinationManager获取,连接参数配在sapjco3.properties或环境变量里;函数不存在时直接抛异常而不是返回 null,避免上层空指针;异常统一包装成自定义异常,方便 Quartz 判断是否重试。参数映射那块,SAP 的 import 参数有严格类型,字符串长度超了会截断,数字类型传错会报转换异常,这些都要在 Service 层做前置校验。

2.3 数据库表设计要点

MyBatis 这边至少需要四张核心表:用户权限表(Shiro 用)、RFC 调用日志表、定时任务配置表、业务单据与 SAP 凭证映射表。日志表要记录请求参数、返回消息、耗时、状态,方便对账时追溯。任务配置表存 Cron 表达式和重试次数上限。映射表是解决「业务已提交、SAP 未落账」的关键,每条业务单据生成一个唯一请求号,SAP 返回凭证号后回写,重试时先查映射表判断是否已成功。

3. RFC 交互模块实现:从连接管理到异常重试

3.1 连接池与目标系统配置

sapjco 本身没有连接池概念,但JCoDestination是线程安全的,可以复用。我一般会在 SpringBoot 启动时初始化一个JCoDestination单例,配在@Configuration里,而不是每次调用都getDestination。连接参数包括 ASHOST、SYSNR、CLIENT、USER、PASSWD、LANG,这些敏感信息不要硬编码,走环境变量或配置中心。

# application.yml 中 SAP 连接配置 sap: destination: SAP_PRD ashost: ${SAP_ASHOST} sysnr: "00" client: "100" user: ${SAP_USER} passwd: ${SAP_PASSWD} lang: ZH pool: max-active: 10 max-wait: 5000

参数说明:sysnr是 SAP 系统编号,两位字符串;client是客户端号,通常三位;pool那块是自定义的连接等待参数,sapjco 原生不支持,需要在封装层用信号量控制并发。如果并发量不大,直接复用单例目标即可,不用过度设计。

3.2 同步调用与异步任务的分工

不是所有 RFC 都适合同步调。查询类接口(比如查物料主数据)可以同步,用户点一下等两秒能接受;但创建采购订单、过账这类写操作,建议走 Quartz 异步任务。流程是:业务层先落本地单据,状态置为「待同步」,然后触发 Quartz Job,Job 里调 RFC,成功回写凭证号,失败按策略重试。

// Quartz Job:异步执行 RFC 并处理重试 public class SapSyncJob implements Job { @Override public void execute(JobExecutionContext context) { Long billId = context.getMergedJobDataMap().getLong("billId"); Bill bill = billMapper.selectById(billId); // 幂等检查:已同步成功的直接跳过 if ("SYNCED".equals(bill.getSyncStatus())) { return; } try { JCoFunction result = sapRfcExecutor.execute("ZRFC_CREATE_PO", buildParams(bill)); String sapDocNo = result.getExportParameterList().getString("EV_DOCNO"); bill.setSapDocNo(sapDocNo); bill.setSyncStatus("SYNCED"); billMapper.updateById(bill); } catch (SapRfcException e) { // 重试次数从 JobDataMap 取,超过上限置为 FAILED int retry = context.getMergedJobDataMap().getInt("retry"); if (retry >= 3) { bill.setSyncStatus("FAILED"); billMapper.updateById(bill); } else { // 重新调度,延迟 5 分钟 scheduleRetry(billId, retry + 1); } } } }

这段逻辑的核心是幂等和重试上限。幂等靠syncStatus判断,重试靠 Quartz 的JobDataMap传参。注意scheduleRetry要新建 Trigger,不能复用原 Trigger,否则会覆盖。重试间隔建议递增,第一次 1 分钟,第二次 5 分钟,第三次 15 分钟,给 SAP 侧足够的恢复时间。

3.3 返回结果解析与错误码映射

SAP RFC 的返回结构通常是EV_TYPE(S 成功 / E 错误)、EV_MESSAGE(消息文本)、EV_DOCNO(凭证号)。解析时要先判EV_TYPE,是 E 就直接抛业务异常,不要往下走。错误码映射建议维护一张配置表,把 SAP 的原始消息转成业务方能看懂的中文提示,比如「物料不存在」对应「请检查物料编码是否正确」。

4. 权限与页面层:Shiro 和 Thymeleaf 怎么配合

4.1 Shiro 认证授权的最小闭环

Shiro 的配置分三块:SecurityManager、Realm、FilterChainDefinitionMap。Realm 里做数据库查询,doGetAuthenticationInfo校验用户名密码,doGetAuthorizationInfo加载角色和权限。FilterChain 里把/sap/**路径拦下来,要求sap:execute权限。

@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean(); factory.setSecurityManager(securityManager); Map<String, String> chain = new LinkedHashMap<>(); chain.put("/login", "anon"); chain.put("/sap/**", "authc, perms[sap:execute]"); chain.put("/**", "authc"); factory.setFilterChainDefinitionMap(chain); return factory; } }

注意LinkedHashMap的顺序,/**必须放最后,否则前面的规则会被覆盖。权限字符串建议按「模块:操作」命名,方便后期扩展。

4.2 Thymeleaf 页面里的 JavaScript 交互

Thymeleaf 负责渲染,但页面上的异步操作、表单校验、RFC 调用进度轮询还是靠 JavaScript。比如触发同步后,前端每隔 3 秒查一次任务状态,成功就刷新列表,失败就弹提示。

// 轮询同步状态,避免用户手动刷新 function pollSyncStatus(billId) { const timer = setInterval(async () => { const res = await fetch(`/sap/sync/status?billId=${billId}`); const data = await res.json(); if (data.status === 'SYNCED') { clearInterval(timer); location.reload(); } else if (data.status === 'FAILED') { clearInterval(timer); alert('同步失败,请联系管理员查看日志'); } }, 3000); }

这段代码的关键是clearInterval的时机,成功和失败都要清,否则轮询会一直跑。另外接口要加超时和最大轮询次数,防止死循环。

5. 避坑与常见问题排查

5.1 RFC 调用超时但 SAP 实际已执行

现象:日志显示JCoException: timeout,但 SAP 里凭证已经生成了。原因:网络抖动或 SAP 侧处理慢,客户端超时先返回,服务端其实完成了。解决:所有写操作必须做幂等,用业务唯一请求号在 SAP 侧查重,重试前先查一次是否已存在。

5.2 sapjco 本地库加载失败

现象:启动报UnsatisfiedLinkError: no sapjco3 in java.library.path。原因:本地库路径没配或架构不匹配。解决:-Djava.library.path=/path/to/sapjco启动参数加上,确认 JDK 位数和库文件一致,Linux 下还要检查LD_LIBRARY_PATH。

5.3 Quartz 任务重复执行

现象:同一条单据被同步了两次,SAP 里出现两张凭证。原因:集群环境下多个节点同时触发,或者 Trigger 配置了misfire策略不当。解决:用数据库锁或 Quartz 的@DisallowConcurrentExecution注解,任务执行前先查状态。

5.4 Shiro 权限缓存导致改权限不生效

现象:管理员改了用户权限,但用户不重新登录还是旧权限。原因:Shiro 默认缓存了 AuthorizationInfo。解决:改权限后调shiroRealm.clearCachedAuthorizationInfo(principals),或者配一个较短的缓存过期时间。

5.5 Thymeleaf 页面 JavaScript 被转义

现象:页面里写的<script>标签内容被当成文本显示。原因:Thymeleaf 的th:inline没开或用了th:text输出。解决:内联脚本用th:inline="javascript",或者把 JS 抽到独立文件用th:src引入。

6. 验收与答辩:怎么证明这套方案真的跑通了

验收分三块:功能、异常、性能。功能上,至少跑通一条完整链路——登录、建单据、触发同步、SAP 生成凭证、回写凭证号、页面展示。异常上,模拟 SAP 宕机、网络超时、权限不足三种场景,看系统是否按预期重试或报错。性能上,用 JMeter 压 50 并发同步请求,观察响应时间和成功率。

答辩时导师最爱问的是「你怎么保证不重复过账」。这时候把幂等设计讲清楚:业务唯一请求号 + SAP 侧查重 + 本地状态机。最好现场演示一次「手动把状态改回待同步,再触发一次,看 SAP 是否拒绝重复创建」。这个演示比任何 PPT 都有说服力。

验收项合格标准验证方式
RFC 同步成功率正常网络下 100%连续跑 20 条单据
异常重试3 次内自动恢复模拟 SAP 短暂不可用
幂等性重复触发不产生新凭证手动改状态后重跑
权限控制无权限用户无法调用用低权限账号访问 /sap/**
日志完整性每次调用有请求和返回记录查日志表条数

我一般会在答辩前把「SAP 宕机后恢复」这个场景录成视频,因为现场演示依赖 SAP 环境,万一网络出问题就尴尬了。从那以后我每次做集成项目,都强制走一遍「断网重试 + 幂等校验」的验收流程,宁可多花半小时,也不在答辩现场赌运气。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 4:00:14

LDC1000金属探测程序完整版:从驱动到滤波的工程实践

简介&#xff1a;这份资源是面向电子设计竞赛选手与嵌入式开发者的LDC1000金属探测完整程序&#xff0c;源自TI杯金属位置探测器项目。包内共40个文件&#xff0c;以C源码、头文件、Keil工程文件&#xff08;uvproj/uvopt&#xff09;、编译产物&#xff08;hex/obj/lst/m51&am…

作者头像 李华
网站建设 2026/10/21 4:00:13

YOLO13-C3k2-ConvAttn:充电桩车位线检测实战与优化

做充电桩车位线检测有一段时间了&#xff0c;先给结论&#xff1a;这套基于YOLO13-C3k2-ConvAttn的检测方案&#xff0c;在露天和地下停车场混合的实拍数据集上&#xff0c;车位线类别的mAP0.5稳定做到85%以上&#xff0c;单帧推理在Jetson Orin级别的边缘设备上能压到30ms左右…

作者头像 李华
网站建设 2026/10/21 3:59:07

Unity全屏三角形渲染原理与实战

1. 项目概述&#xff1a;为什么一个“三角形”能撑满整个屏幕&#xff1f;在Unity里写个全屏效果&#xff0c;第一反应往往是拉一个Canvas&#xff0c;放个Image&#xff0c;调个RawImage贴图——这没错&#xff0c;但当你开始深入图形管线、做后处理、写自定义渲染器&#xff…

作者头像 李华
网站建设 2026/10/21 3:59:05

从技能清单到技能系统:盘点、规划与展示的实用指南

有时候我觉得&#xff0c;“skills”这个词可能是简历上被滥用得最厉害的一个英文单词。程序员把它写在技能清单里&#xff0c;产品经理把它挂在嘴上&#xff0c;知识付费把它拆成各种能力模型&#xff0c;但如果你随便抓一个职场人问一句“你最核心的技能是什么&#xff0c;能…

作者头像 李华
网站建设 2026/10/21 3:58:02

foobar2000 皮肤安装教程:foobox 中文皮肤,5 分钟装完即用

foobar2000 皮肤安装教程:foobox 中文皮肤,5 分钟装完即用 【免费下载链接】foobox-cn DUI 配置 for foobar2000 项目地址: https://gitcode.com/GitHub_Trending/fo/foobox-cn 用了多年的 foobar2000,界面还是灰底加一条列表,想上歌词、封面、频谱,得自己满世界找组件。…

作者头像 李华
网站建设 2026/10/21 3:56:09

基于YOLOv11的焊接缺陷检测识别系统实战全流程

1. 为什么焊接缺陷检测是值得做的毕业设计&#xff1a;从选题价值到技术选型如果你正在为毕业设计题目发愁&#xff0c;焊接缺陷检测识别系统是一个相当合适的方向。焊接质量检测在压力容器、船舶、管道、汽车制造等行业都有真实需求&#xff0c;不是纸上谈兵的样子工程。用目标…

作者头像 李华