SpringBoot 通过 JNA 调用 C 接口攻略

YOLOv11改进 | 损失函数篇 | YOLOv11引入引MPDIoU, GIoU,DIoU, CIoU, SIoU,EIoU, WIoU, LMPDIoU等损失函数 到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv11改进有效涨点专栏,本专栏目前为新开的,后期我会根据各种前沿顶会进行论文复现,也会对一些老的改进机制进行补充,如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~1. YOLOv11中添加MPDIoU, GIoU,DIoU, CIoU, SIoU等损失函数的方式。文件中用给出的代码替换掉。 阅读详情

引言

在实际项目开发中,文件解析是高频且核心的业务场景,而我们近期遇到了一个典型的技术挑战:核心的文件解析逻辑需依赖第三方以 C 语言编写的底层接口,无法直接通过 Java 代码实现。为了高效复用这套成熟的 C 接口能力,同时兼顾 SpringBoot 项目的整体技术架构,我们最终选择基于 JNA(Java Native Access)技术实现 Java 与 C 代码的跨语言调用,既规避了重复开发的成本,也保证了接口调用的稳定性。

引入依赖

JDK 版本推荐 JNA 版本可选 JNA 版本
JDK 6/74.5.2(生产推荐)4.0.0 ~ 4.5.2
JDK 85.13.0(生产推荐)5.0.0 ~ 5.15.0
JDK 9/10/115.15.0(生产推荐)5.8.0 ~ 5.19.0
JDK 17+(LTS)5.19.0(生产推荐)5.15.0 ~ 最新 5.x
<!-- JNA核心 -->
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>5.13.0</version> <!-- 稳定版,兼容JDK8,不踩null坑 -->
</dependency>
<!-- JNA平台适配(跨系统必备,自动识别win/linux库) -->
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna-platform</artifactId>
    <version>5.13.0</version>
</dependency>

环境变量

  • 概念介绍:.dll是 Windows 系统的动态链接库(存 C 写的接口),.so 文件是 Linux 系统的同款文件,Java 通过 JNA 就能调用这两个文件里的 C 接口实现文件解析。
  • 由于第三方提供的可能是
    • 单个.dll文件(或单个.so文件)
    • 也有可能是一整个文件夹,文件夹里面有要使用的.dll动态链接库文件,还有很多其他的dll依赖(Qt5Gui.dll、QtSvg.dll等)还有很多文件夹(iconengines、imageformats、platforms、styles、translations等文件夹)

在这里插入图片描述

  • 如果这个.dll动态链接库是用QT写的代码的话,可能就会是第二种情况,这种情况需要我们配置QT插件环境变量(尝试过用代码解决,耗时两天,解决不了配置环境变量问题,所以还是需要我们手动配一下QT插件的环境变量)
  • 假设上图的文件夹名为library,路径为D:\library

在这里插入图片描述

  • QT_QPA_PLATFORM_PLUGIN_PATH 该环境变量配置主要是为了让QT打包的C代码去找到他所需要的依赖,告诉动态库文件的
  • 注意:后续这整个文件夹都会拉到项目里面,所以最后配置的路径还要改的

代码实现

  • 我们需要在项目根目录下创建 static 文件夹并放入 library,这样做能避免该文件夹被打包进 jar 包;部署时将 static 文件夹放在 jar 包同级目录,可方便配置 QT 依赖的环境变量。
  • 注意:如果假设有两个动态链接库的话,下方的接口映射区就要写两个接口,接口里面对应各自的动态库的方法,三个动态库也是同理
public class ClibraryService{
    private static ObjectMapper objectMapper = new ObjectMapper();
    private static final TypeReference<Map<String, Object>> MAP_TYPE_REF = new TypeReference<Map<String, Object>>() {};
    
    // =========== 静态代码块动态库地址配置区 ===========
    static {
        // 1、拼接路径
        String path = System.getProperty("user.dir")+File.separator+"static"+File.separator+"library";
        
        // 2、告诉jna去哪里找动态库
        // 写法一:这种写法在jna5.0+的时候被标记为已过时,这么写如果有多个动态库就需要写好几行的这个add
        NativeLibrary.addSearchPath("ProcessLib",path); // 找ProcessLib这个动态库的时候去这个文件夹路径下找(就是接口文件)
        
        // 写法二:直接告诉jna所有的动态库都去这个路径下找
        System.setProperty("jna.library.path",path);
        
        // 拓展:如果多个dll在不同路径下
        // String path1 = System.getProperty("user.dir")+File.separator+"static"+File.separator+"library1";
        // String path2 = System.getProperty("user.dir")+File.separator+"static"+File.separator+"library2";
        // String allPaths = path1 + File.pathSeparator + path2;
        // System.setProperty("jna.library.path",allPaths);
    }
    
    // =========== 内嵌接口区 ===========
    private interface ProcessLib extends Library{
        ProcessLib INSTANCE = Native.load( // 参数一 动态库名称,参数二:接口字节码我就
        	"ProcessLib", // jna在windows环境会用ProcessLib.dll,linux的会用ProcessLib.so的动态库
            ProcessLib.class // 本接口的字节码我就
        );
        
        // 获取phy文件信息
        boolean getPhyBaseInfo(String phyFilePath,Memory outResult);
        
        // phy转csv
        boolean exportFileCsv(String fileName,String outPath);
    }
    
    // =========== 接口映射区 ===========
    // 获取phy文件信息
    public static Map<String,Object> getPhyBaseInfo(String phyFilePath){
        Memory memory = new Memory(4*1024*1024); // 缓冲区
        boolean b = ProcessLib.INSTANCE.getPhyBaseInfo(phyFilePath,memory);
        if(!b) return Collections.emptyMap();
        String result = memory.getString(0,StandardCharsets.UTF_8.name()).trim();
        try {
            return objectMapper.readValue(result, MAP_TYPE_REF);
        } catch (IOException e) {// 记录原始内容便于排查
            System.err.println("Invalid JSON from native call: [" + result + "]");
            return Collections.emptyMap(); // 或抛业务异常
        }
    }
    
    // phy转csv
    public static boolean exportFileCsv(String fileName,String outPath){
        boolean b = ProcessLib.INSTANCE.exportFileCsv(fileName,outPath);
        return b;
    }
}
部署目录结构(jar 同级):
your-app/ 
├── your-app.jar
├── static/
│   └── library/
│       ├── ProcessLib.dll
│       ├── Qt5Core.dll
│       ├── platforms/
│       │   └── qwindows.dll
│       └── ...
└── start.bat        ← 设置 QT_QPA_PLATFORM_PLUGIN_PATH(其实一般会给部署人员一个配置QT环境变量的脚本)
  • 注意:可以对第三方接口的返回结果进行JSON转Map的原因是因为行业内规范规定:当给第三方提供接口调用时,需遵循RESTful规范,使用资源化URL和HTTP方法,数据返回格式统一使用json格式。

参数类型对应

C 表达式Java 类型(JNA)说明
const char*(输入字符串)StringJNA 自动转换为 UTF-8(可配置)的 C 字符串
char*(输出字符串)byte[] 或 Pointer若函数写入缓冲区,用 byte[];若返回新指针,用 Pointer + getString()
intint用于模拟 C 中的 &var
bool (C99)boolean1 字节的布尔类型
  • 这里主要说明一下输出字符串:
public class ClibraryService{
    private static ObjectMapper objectMapper = new ObjectMapper();
    private static final TypeReference<Map<String, Object>> MAP_TYPE_REF = new TypeReference<Map<String, Object>>() {};
    
    ... 省略一些代码 ...

    // char*对应方式一,byte[]:
    public static Map<String,Object> getPhyBaseInfo(String phyFilePath){
        byte[] buf = new byte[4*1024*1024]; // 注意如果缓冲区给的大小不够的话,会截断返回值内容
        Arrays.fill(buf, (byte) 0); // 可选,这么做可以确保不会读到旧数据
        boolean b = ProcessLib.INSTANCE.getPhyBaseInfo(phyFilePath,buf); // 调用C代码
        if(!b) return Collections.emptyMap(); // 如果接口返回false,我们可以处理一下
        // 如果是true,那么我们要将缓冲区中返回的数据处理一下
        String result = Native.toString(buf,"UTF-8").trim(); // 遇到第一个 \0(null 字节)就停止
        try {
            return objectMapper.readValue(result, MAP_TYPE_REF); // 返回的内容是JSON字符串,我们可以转成java的map来使用,我一般喜欢用Jackson
        } catch (IOException e) {// 记录原始内容便于排查
            System.err.println("Invalid JSON from native call: [" + result + "]");
            return Collections.emptyMap(); // 或抛业务异常
        }
    }

    // char*对应方式二,Pointer:
    public static Map<String,Object> getPhyBaseInfo(String phyFilePath){
        Memory memory = new Memory(4*1024*1024); // 缓冲区(Memory是Pointer的子类)
        boolean b = ProcessLib.INSTANCE.getPhyBaseInfo(phyFilePath,memory);
        if(!b) return Collections.emptyMap();
        String result = memory.getString(0,StandardCharsets.UTF_8.name()).trim();
        try {
            return objectMapper.readValue(result, MAP_TYPE_REF); // 返回的内容是JSON字符串,我们可以转成java的map来使用,我一般喜欢用Jackson
        } catch (IOException e) {// 记录原始内容便于排查
            System.err.println("Invalid JSON from native call: [" + result + "]");
            return Collections.emptyMap(); // 或抛业务异常
        }
    }
}

注意事项

  • 64位的jdk只能运行64位的动态库文件,32位的jdk只能运行32位的动态库文件,如果jdk和dll位数没对应上,用jna调用的话就会报错
  • dll动态库要放到library文件夹直属下级中,不能再继续嵌套子文件夹(就是放在上图的文件夹中,和Qt5Gui.dll这些依赖放一起就好了)
  • 不要将动态库放在 src/main/resources下因为打包后它会被打进 JAR,我们就没法配置QT的环境变量了
  • 静态代码块里面由于jvm加载机制问题,建议不要使用Paths工具类,File创建路径,可能会导致路径出错
  • JNA 自身的库搜索路径 ≠ Qt 插件路径!所以我们既要处理JNA路径搜索(静态代码块内容),也要指定QT插件路径(我们配的环境变量)
Euro-NCAP-HWA测试流程中文版V1.1(2023发布) 在本协议中,使用了以下术语:Vehicle undertest (VUT) – 指根据本规程测试的车辆,车上有碰撞前的碰撞缓解或避免系统Global VehicleTarget (GVT) – 指本协议中使用的车辆目标,其定义见TB025—Euro-NCAP全球车辆目标规范v1.0辅助其他车辆(SOV)--指最新的 AEB VRU 测试规程中定义的 "大型障碍车辆"(而非机器人控制的平台),用于本规程中的 "切出 "测试。Time To Collision (TTC) – 指在VUT碰撞GVT之前的剩余时间 阅读详情

相关推荐

DIY音乐播放器:用VS1053模块+Arduino打造你的专属MP3播放器(附完整代码)

本文详细介绍了如何使用VS1053音频解码模块与Arduino开发板,从零开始构建一个功能完备的桌面级MP3播放器。内容涵盖硬件架构设计、SPI总线共享、底层驱动编写、双缓冲区数据流管理以及用户交互实现,并提供完整的代码示例与性能优化建议,帮助创客打造高度可定制的专属音乐播放设备。

weixin_29189003的博客 286

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 195

【scratch音乐课】天空之城:音符与节拍、消息与链表

scratch是一个非常小巧的青少年编程工具,自带程序编写语句,积木式语句非常直观、易用,可创作数学、科学、音乐、绘画等方面作品。本文用scratch“演奏”一首大家非常熟悉的《天空之城》,并向大家介绍一些基本乐理知识,以及scratch中消息、链表的使用。

★·°立芯;︶︵︶ 1万+

Spring Boot 工程通用能力建设:声明式事务定制与 Redis 缓存实践

本文探讨Spring Boot中声明式事务的进阶实践,通过解耦“切点”与“事务属性”,实现灵活控制。核心包括:基于方法名的只读规则(如get*、find*自动设为只读),组合事务属性源确保@Transactional注解优先于命名规则,以及自定义切点实现事务统一织入。该方案提升代码可维护性,避免误写,同时支持特殊场景覆盖,构建更健壮的事务治理体系。

BADAO_LIUMANG_QIZHI的博客 240

CANoe实战:手把手教你用J1939.dbc发送超8字节长帧报文(附完整CAPL代码)

本文详细解析了使用CANoe和J1939.dbc实现超8字节长帧报文的分包发送技术,提供完整的CAPL代码实现与优化策略。内容涵盖J1939.dbc配置、分包发送模块设计、接收端处理逻辑,并与ISO 15765-2协议进行深度对比,帮助开发者构建高效可靠的商用车通信解决方案。

weixin_29169899的博客 392

【PINN物理信息网络】基于PINN物理信息网络求解泊松方程(python)

【PINN物理信息网络】基于PINN物理信息网络求解泊松方程(python)

k8291121的博客 2811

Spring Boot 缓存一致性怎么保证?@Cacheable、@CacheEvict 与事务提交时机实战

Spring Boot 项目中,为什么数据库更新成功后,接口仍然返回旧数据?本文从 Spring Cache 的工作机制入手,结合 @Cacheable、@CacheEvict、Caffeine 和事务同步回调,分析缓存失效时机、事务提交顺序及并发回填等常见问题,并提供代码示例、解决方案和测试思路,帮助开发者设计更可靠的缓存更新机制。

代码不止,探索不息 248

基于STM32、HAL库的MICRF112YMM-TR 无线收发器驱动程序设计

内部集成了 VCO、PLL、PA、LNA、混频器、滤波器等射频功能单元,极大减少了外部元件数量,降低了设计和布线难度。: 具有多种工作模式(睡眠、待机、接收、发射),待机电流极低,非常适合电池供电的物联网(IoT)设备。是 Microchip 公司生产的一款高性能、低功耗的单片 UHF ASK/FSK 收发器芯片。: 支持 ASK (幅移键控) 和 FSK (频移键控) 调制,适应不同应用需求。// 可以添加其他配置参数,如中心频率、数据速率等。智能家居(安防、遥控、传感)远程钥匙进入系统 (RKE)

corlin6688的博客 415

数据库乐观锁深度解析:MySQL、PostgreSQL 实战 + Spring Boot 集成指南

Oracle 提供了ORA_ROWSCN伪列(System Change Number),记录最后修改行的 SCN,类似 PostgreSQL 的xmin。实践中通常仍使用 version 字段方案,逻辑与 MySQL 完全相同。-- Oracle:通过 ORA_ROWSCN 实现乐观锁-- 更新时比对 SCN// 自定义注解// AOP 切面@Aspect@Component@Slf4jtry {

yjc_1111的专栏 399

对CryptoDriver里密钥格式定义的探索(1)

一入网络安全才发现深似海,传统汽车MCU码农转型之路异常艰难。在CP AUTOSAR中的CryptoDriver SWS对密钥格式的需求来源于RFC,其中要求了RSA、ECC、SHE等key material的格式。再往下看,我就有些迷糊了,例如关于RSA Private Key的OCTEE STRING格式根据IETF RFC8017,Public Key又得根据RFC5280,完全不得其解。所以今天先从生成密钥对,并对其进行解析开始,一步一步拆解。

认真搞搞汽车MCU 527

手把手教你学simulink(57.2)--电励磁同步电动机场景示例:基于Simulink的矢量控制(Field-Oriented Control, FOC)电励磁同步电动机

在某电动汽车驱动系统中,采用了本项目开发的带有矢量控制功能的电励磁同步电动机驱动系统,结合三相逆变器实现了对电动机的高效控制。在某工业驱动系统中,采用了本项目开发的带有矢量控制功能的电励磁同步电动机驱动系统,结合三相逆变器实现了对电动机的精确控制。经过实际测试,该系统的应用使得生产效率提高了10%,并且在高负载条件下,电动机的温升得到了有效控制,延长了电动机的使用寿命。通过本项目的实施,我们成功配置了三相交流电源、三相逆变器和电励磁同步电动机的参数,并实现了对电动机的精确控制。

MHD0815的博客 944

Spring Boot 提速记:启动 40 秒到 8 秒,GC 停顿从 850ms 降到 60ms

面向线上 Spring Boot 服务,给出启动侧延迟初始化与 AOT 编译、运行侧 GC 与虚拟线程选型,再用分层压测验证收益,附可复制的参数与代码。

2301_78967866的博客 357

Day 01: 与Python的第一次亲密接触

环境搭建 、第一个程序 Hello, World!、以及Python之禅 (The Zen of Python) 的解读。

杨小扩的博客 220

Spring Boot 项目容器化与 Kubernetes 部署实战指南

本文档基于真实实践,总结了一套最简且可行的流程:将本地打包好的 Spring Boot JAR 包,制作成 Docker 镜像,推送到阿里云私有镜像仓库,并最终在 Kubernetes 集群中运行。

zmm0628的博客 214

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

2. 编程语言-Java-Spring Boot-JAR

本文以Spring Boot 3.5.16为例,详解Fat JAR的运行机制:通过自定义类加载器与约定目录结构(BOOT-INF/classes/lib),实现依赖内嵌与嵌套JAR动态加载;借助MANIFEST.MF中的Main-Class(JarLauncher)和Start-Class,完成启动引导;最终在内嵌Tomcat中运行Web应用。核心在于“一个JAR包自包含运行环境”,让java -jar真正实现“开箱即用”。

princemilo的博客 350

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Spring Boot 4 升级为什么卡在 Jackson 3?包名、配置与兼容迁移指南

Spring Boot 4 默认使用 Jackson 3,给 Java 项目的 JSON 序列化和反序列化带来了新的迁移挑战。本文结合 Spring 官方升级指南,详细分析 Jackson 2 与 Jackson 3 的包名、依赖、API 和默认行为差异,介绍 JsonMapper 改造、配置迁移、兼容模式及 JSON 契约测试方法,并提供生产项目的渐进式升级建议,帮助开发者降低 Spring Boot 4 升级风险。

代码不止,探索不息 396

Spring Boot 配置文件详解:从基础语法到 YAML 配置

配置文件用于将程序中易变的配置信息(如端口、数据库连接、密钥等)与代码分离,避免硬编码带来的维护困难。SpringBoot支持application.properties和application.yml两种格式,前者语法简单但冗长,后者结构清晰、可读性强,支持复杂数据类型如对象、列表、映射,且层级关系通过缩进体现。使用@Value或@ConfigurationProperties可实现配置注入。YML虽可读性高、跨语言通用,但对格式敏感,不适用于极复杂配置。

java_nnnn的博客 265

Spring Boot 和 Spring Cloud 是什么关系

Spring Boot 与 Spring Cloud 是协同关系,非替代或升级。Boot 负责单个服务的快速启动与配置,Cloud 则解决多服务间的注册发现、调用、容错等分布式问题。两者能力不重叠:Boot 管进程内,Cloud 管进程间。Cloud 组件基于 Boot 的自动配置机制实现,依赖版本严格匹配,不可随意升级。是否使用 Cloud,取决于是否有跨服务治理需求,而非项目规模。小系统或已用 K8s 可无需引入完整 Cloud。

2401_89855466的博客 210

Spring Boot SSE 流式输出变成一次性返回怎么办:大模型打字机效果失效的 5 个原因(Spring Boot 4.1.2 实测)

Spring Boot 用 SseEmitter 或 Flux 做大模型流式输出,前端却一次性收到全部内容、只收到第一个字或 30 秒后断开?本文用 Spring Boot 4.1.2 实测 5 个原因:controller 线程里 send、ContentCachingResponseWrapper 日志过滤器、默认 30 秒异步超时、Flux 没写 produces、Nginx 代理缓冲,并验证开 gzip 在 Tomcat 上不会让 SSE 变成一次性返回。

u011831527的博客 339

Spring Boot 实战:基于 Hutool + Session 实现图形验证码(从原理到完整项目)

本文实现了一个基于Hutool工具包的图形验证码系统,前后端协同完成验证码生成、校验与跳转功能。后端使用Hutool-captcha生成带干扰线的验证码,通过Session存储验证码及有效期(1分钟),并提供接口返回图片和校验结果;前端利用Ajax提交用户输入,通过jQuery动态刷新验证码图片并实时校验,错误时弹窗提示,正确则跳转至成功页面。整体设计解耦清晰,安全有效,适用于常见登录验证场景。

java_nnnn的博客 285

Spring Boot 4.1 + Spring AI 2.0:RAG 供文、图谱供关系、MCP 供工具的 Java Agent 平台

以 Spring Boot 4.1 与 Spring AI 2.0 为底座搭建 Java Agent 平台:向量库供语义、知识图谱供关系、MCP 供工具,给出装配、编排与观测的落地配置。

2301_78967866的博客 338

Java EE 进阶:7.Spring Boot 日志(第一弹)

Java EE 进阶:7.Spring Boot 日志(第一弹)

要努力去发光,而不是被照亮~ 217

Spring Boot 3.x + Spring Security 6.x 整合 JWT 实战:jjwt 与 java-jwt 两种工具库详解

在前后端分离项目中,身份认证是后端开发中非常重要的一部分。传统的 Session 认证需要服务端保存用户会话信息,而在分布式架构中,Session 的共享和管理会带来额外的复杂度。JWT(JSON Web Token)是一种常见的无状态身份认证方案。服务端在用户登录成功后生成 Token,客户端在后续请求中携带 Token,服务端通过验证 Token 的签名和有效期来识别用户身份。Spring Security 是 Spring 生态中非常成熟的安全框架,主要负责身份认证、权限控制和安全过滤器管理。

211

Spring Boot 性能优化实战:从启动加速到运行时调优

本文结合微服务扩缩容与高并发场景,梳理 Spring Boot 应用在启动速度与运行期性能上的优化路径。内容涵盖懒加载与类索引的配置细节、GraalVM AOT 编译的落地条件与边界、JVM 参数在容器环境下的正确设置、线程池与连接池的容量测算方法,以及基于 Arthas 和 JFR 的线上诊断流程。通过具体配置示例、压测对比思路和排查路径,给出可验证的调优步骤,帮助后端团队在有限资源下平衡冷启动成本与吞吐量稳定性。

2402_83344867的博客 376

英汉对照文本工坊(Spring Boot 版)

英汉对照文本工坊将 11 个 Python 脚本重构为 Spring Boot 3 本地 Web 应用,覆盖中英拆分、对照合成、引号转换、格式规范等 8 类功能。全部内存计算,不写库不落盘。修正换行符、空行语义等原缺陷,支持拖拽、复制下载与快捷键,离线可用。

howard2005的专栏 199

一个端口能被两个程序同时监听吗:SO_REUSEADDR、SO_REUSEPORT Java 实测和 Spring Boot 端口被占照样启动的坑

一个端口能不能被两个进程同时监听?用 JDK 17 在 macOS 上实测 8 种绑定组合、SO_REUSEADDR、SO_REUSEPORT、TCP 与 UDP 同端口和 TIME_WAIT 重启,并复现 Spring Boot 端口被占却照样启动、本机请求被别的进程截走的坑,附 Port 8080 was already in use 的排查步骤。

u011831527的博客 232

在 IntelliJ IDEA 中关闭 DevTools(通常指 Spring Boot DevTools 热部署功能)

在 IntelliJ IDEA 中关闭 DevTools(通常指 Spring Boot DevTools 热部署功能)需要从等多个层面协同操作,才能确保彻底关闭,避免出现“删了依赖但热部署仍在生效”的常见问题。

BillKu的博客 325
上一篇: SpringBoot 不重启!基于脚本引擎实现评分规则的动态加载与执行
下一篇: SpringBoot3 + SpringSecurity + JWT 一站式整合实战
爱探险的阿伟
博客等级 码龄4年 10粉丝 5原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值