后端低代码AI 应用人工智能AI Agent认证鉴权RAG【免费下载链接】erupt 通用数据管理框架VORM 对象视图模型注解驱动低代码开发项目地址https://gitcode.com/erupts/erupt点击查看免费下载导读erupt-toolkit是 Erupt 框架内部的共享工具模块Shared utilities它在erupt-core之上提供了三件可直接复用的基础设施基于 Spring Data Redis 的缓存抽象实现、基于JdbcTemplate的原生 SQL 选择项拉取工具、以及基于 Redis Pub/Sub 的数据变更通知代理。本文以该模块的 README 为主体骨架结合仓库源码逐层剖析其设计意图、实现原理与调用约定帮助你在erupt-upms、erupt-job等模块或其他自定义业务中正确接入这些能力。一、模块定位Erupt 生态中的共享工具层erupt-toolkit位于erupt-support目录下其 README 开宗明义地将其定位为Shared utilities for the Erupt framework即面向 Erupt 框架内部的通用工具集合而非面向终端用户的独立业务模块。其提供的能力被归纳为三类Cache abstraction backed by Spring Data Redis基于 Spring Data Redis 的缓存抽象实现JDBC helper utilities for raw SQL operations面向原生 SQL 操作的 JDBC 辅助工具Common data processing and conversion tools通用的数据处理与转换工具。同时 README 明确指出其消费方由erupt-upms、erupt-job等需要缓存或直接数据库访问的模块在内部使用。从 erupt-upms/pom.xml 可以看到该模块确实以artifactIderupt-toolkit/artifactId作为依赖被引入验证了 README 的描述。从工程结构看该模块并不复杂根目录下仅有 6 个 Java 源文件位于 erupt-support/erupt-toolkit/src/main/java/xyz/erupt/toolkit 下分别归属handler、notify、service三个包外加一个自动配置入口。这种小而精的组织方式正是工具模块的典型形态——它不追求功能全面而是把高频复用的能力收敛到一处避免各业务模块各自重复实现。1.1 自动装配入口模块的装配入口是 EruptToolkitAutoConfiguration.java这是一个非常精简的 Spring 配置类Configuration ComponentScan public class EruptToolkitAutoConfiguration { }它仅声明ComponentScan让 Spring 自动扫描同包及其子包下的Component组件即EruptCacheRedis、SqlChoiceFetchHandler、RedisNotifyDataProxy无需手工注册 Bean。因此只要在项目中引入erupt-toolkit依赖这些工具类即可被自动装配并注入使用。1.2 依赖边界erupt-toolkit/pom.xml 展示了该模块的依赖范围依赖用途xyz.erupt:erupt-core复用EruptCache缓存抽象、GsonFactory、DataProxyContext、EruptProp等核心能力spring-boot-starter-data-redis提供StringRedisTemplate支撑缓存与消息发布spring-boot-starter-jdbc提供JdbcTemplate支撑原生 SQL 查询可以看到erupt-toolkit只依赖erupt-core与 Spring Boot 的标准 starter因此它可以被任何 Erupt 业务模块甚至第三方扩展模块安全引用而不会引入额外的重型依赖。二、缓存抽象从EruptCache接口到 Redis 落地实现2.1 核心接口EruptCacheV缓存抽象接口定义在 erupt-core 中路径为 EruptCache.javapublic interface EruptCacheV { // ttl with milliseconds V put(String key, V v, long ttl); V get(String key); void expire(String key, long ttl); Long getExpire(String key); default V getAndSet(String key, long timeout, SupplierV supplier) { return Optional.ofNullable(this.get(key)).orElseGet(() - this.put(key, supplier.get(), timeout)); } void delete(String key); }该接口有四个设计要点值得注意TTL 单位为毫秒注释与实现都明确约定ttl以毫秒计// ttl with millisecondsgetAndSet是读缓存-未命中则回源的标准模板方法通过Optional判空命中直接返回未命中则调用Supplier回源并把结果写入缓存。所有实现类共享这一逻辑无需重复编写expire与getExpire成对出现前者动态刷新过期时间后者查询剩余存活时间泛型化设计V可以是任意类型由具体实现决定序列化策略。2.2 进程内实现EruptCacheLRU与erupt-toolkit同属于缓存抽象族、但位于 erupt-core 的进程内实现是 EruptCacheLRU.java。它基于LinkedHashMapaccessOrdertrue实现 LRU 淘汰并引入ReentrantReadWriteLock保证并发安全。其关键实现细节过期节点内部用ExpireNodeV包裹value与绝对过期时间戳expireput时按ttl 0 ? now ttl : Long.MAX_VALUE计算过期点ttl 0表示永不过期惰性过期get时发现expire now即移除并返回null同时每次命中会顺带把过期时间延长 100msexpireNode.setExpire(expireNode.getExpire() 100)形成滑动续期效果容量约束重写removeEldestEntry当size capacity时先执行clean()清理已过期数据再淘汰最久未使用的条目禁用裸 removeremove(Object key)直接抛出UnsupportedOperationException强制调用方走delete(String key)的加锁路径。这个类常被SqlChoiceFetchHandler、DictChoiceFetchHandler等选择项处理器用作默认的进程内缓存。2.3 Redis 落地实现EruptCacheRediserupt-toolkit的核心交付物之一正是把上述抽象落地到 Redis 的实现——EruptCacheRedis.javaComponent AllArgsConstructor public class EruptCacheRedisV implements EruptCacheV { private final StringRedisTemplate stringRedisTemplate; Override public V put(String key, V v, long ttl) { stringRedisTemplate.opsForValue().set(key, GsonFactory.getGson().toJson(v), ttl, TimeUnit.MILLISECONDS); return v; } Override public V get(String key) { return GsonFactory.getGson().fromJson(stringRedisTemplate.opsForValue().get(key), new TypeTokenV() { }.getType()); } Override public void expire(String key, long ttl) { stringRedisTemplate.expire(key, ttl, TimeUnit.MILLISECONDS); } Override public Long getExpire(String key) { return stringRedisTemplate.getExpire(key); } Override public void delete(String key) { stringRedisTemplate.delete(key); } }结合源码可以提炼出它的几个关键约定序列化方案是 Gson写入时GsonFactory.getGson().toJson(v)读取时用TypeTokenV反序列化。这意味着缓存值必须是 Gson 可序列化的 POJO/集合且由于采用泛型TypeToken保留类型信息读取时能正确还原为V的真实类型数据结构是 Redis String通过opsForValue()操作每个缓存键对应一个 Redis 字符串键值TTL 语义与接口保持一致毫秒为单位直接透传给 Redis 的原生过期机制由 Redis 服务端负责过期清理与进程内实现的惰性过期形成互补AllArgsConstructor注入Lombok 生成的构造器注入StringRedisTemplate依赖来自spring-boot-starter-data-redis。使用建议当应用为多实例部署、需要跨节点共享缓存例如字典、菜单等全局性数据时将缓存实现替换为EruptCacheRedis即可单机场景或数据量小时直接使用EruptCacheLRU更轻量。三、JDBC 辅助工具SqlChoiceFetchHandler原生 SQL 选择项3.1 设计动机在 Erupt 的注解驱动模型中Edit(type EditType.CHOICE)的下拉选择项可以通过fetchHandler动态拉取。常规做法是走EruptDao的 LambdaQuery 查询实体如 DictChoiceFetchHandler.java 所做但有些场景需要直接基于任意 SQL 拉取选项——例如跨表 join、原生 SQL 函数、复杂条件过滤等。erupt-toolkit提供的SqlChoiceFetchHandler正是为此而生。3.2 完整实现SqlChoiceFetchHandler.java 的核心代码Component public class SqlChoiceFetchHandler implements ChoiceFetchHandlerVoid { Resource private JdbcTemplate jdbcTemplate; private final EruptCacheListVLModel eruptCache new EruptCacheLRU(500); Override SuppressWarnings(SqlSourceToSinkFlow) public ListVLModel fetch(String[] params) { EruptAssert.notNull(params, SqlChoiceFetchHandler.class.getSimpleName() → params not found); return eruptCache.getAndSet(SqlChoiceFetchHandler.class.getName() : params[0], params.length 2 ? Long.parseLong(params[1]) : 3000, () - jdbcTemplate.query(params[0], (rs, i) - { if (rs.getMetaData().getColumnCount() 1) { return new VLModel(rs.getString(1), rs.getString(1)); } else { return new VLModel(rs.getString(1), rs.getString(2)); } }) ); } }3.3 参数契约fetchHandlerParams该处理器通过ChoiceType的fetchHandlerParams传递参数参数为字符串数组params契约如下参数位含义是否必填说明params[0]原生 SQL 语句必填由EruptAssert.notNull强制校验缺失直接抛异常params[1]缓存时长毫秒可选缺省默认3000ms传入时需为数字字符串结果列约定是另一个必须注意的细节查询结果只有 1 列value与label都取第一列new VLModel(rs.getString(1), rs.getString(1))查询结果有 2 列及以上第一列作为 value第二列作为 labelnew VLModel(rs.getString(1), rs.getString(2))。因此建议 SQL 写成select id, name from ...的形式让第一列承担值、第二列承担显示文本与 README 主仓库示例中select id, name from e_upms_menu的用法一致。3.4 缓存与回源处理器内部维护了一个容量为 500 的EruptCacheLRUListVLModel进程内缓存键为SqlChoiceFetchHandler.class.getName() : params[0]即类名 SQL 语句过期时间默认 3 秒或由params[1]指定。整个流程走EruptCache.getAndSet缓存命中直接返回未命中则执行jdbcTemplate.query回源并回填缓存。3 秒的短 TTL 是一种尽力而为的弱一致性策略——既避免了高频页面刷新时反复打库又不至于让下拉选项长期滞后于数据变化。3.5 实际使用示例在 erupt-core 生态中erupt-job模块的 EruptJobLog.java 就是一个现成的接入范例任务日志列表中的任务名字段通过 SQL 从e_job表拉取选项EruptField( views View(title Job Name, type ViewType.TEXT), edit Edit(title Job Name, search Search, type EditType.CHOICE, choiceType ChoiceType(fetchHandler SqlChoiceFetchHandler.class, fetchHandlerParams select id, name from e_job)) ) private Long jobId;主仓库 README-zh.md 中也有select id, name from e_upms_menu的同类用法。由此可以推断凡是选项来自任意 SQL 查询的 CHOICE 字段都可以直接复用该处理器无需为每张表编写独立的ChoiceFetchHandler。四、Redis 数据变更通知RedisNotifyDataProxy4.1 解决什么问题多实例部署时一个实例上的数据变更需要通知其他实例例如刷新各自进程内的缓存。erupt-toolkit通过 Redis Pub/Sub 机制给出了一个轻量方案在数据新增/修改/删除后把变更事件发布到指定的 Redis Channel订阅方自行消费。4.2 实现剖析RedisNotifyDataProxy.java 实现了 Erupt 注解体系中的DataProxyObject接口并挂接三个写后钩子Component public class RedisNotifyDataProxy implements DataProxyObject { Resource private StringRedisTemplate stringRedisTemplate; Resource private EruptProp eruptProp; Override public void afterAdd(Object o) { this.publish(DataAction.ADD, o); } Override public void afterUpdate(Object o) { this.publish(DataAction.UPDATE, o); } Override public void afterDelete(Object o) { this.publish(DataAction.DELETE, o); } private void publish(DataAction action, Object data) { if (eruptProp.isRedisSession()) { if (DataProxyContext.params().length 0 || null DataProxyContext.params()[0]) throw new EruptWebApiRuntimeException(DataProxy params[0] not found → redis channel); stringRedisTemplate.convertAndSend(DataProxyContext.params()[0], GsonFactory.getGson().toJson(new NotifyData(action, data))); } } }结合 DataProxyContext.java 的实现基于ThreadLocal保存当前 Erupt 模型上下文可以还原它的三条调用约定总开关是erupt.redis-session仅当EruptProp.isRedisSession()为true时才真正发布否则钩子为空操作Channel 名称来自DataProxy的 params即使用该代理的实体上Erupt(..., dataProxy ...)所配置的参数数组params[0]必须是 Redis Channel 名。参数缺失或为空会抛出EruptWebApiRuntimeException提示DataProxy params[0] not found → redis channel消息体是NotifyDataaction, data用 Gson 序列化后通过StringRedisTemplate.convertAndSend发布到指定 Channel。4.3 消息模型DataAction与NotifyData配套的两个模型类非常清晰DataAction.java 是一个枚举定义了三种动作ADD、UPDATE、DELETE分别对应三个afterXxx钩子NotifyData.java 是消息载体包含action动作类型与data泛型业务数据两个字段提供无参构造器与全参构造器并借助 Lombok 的Getter/Setter生成访问器。因此订阅方收到的 JSON 形如{action:ADD,data:{...业务实体字段...}}消费端可以使用 Redis 的SUBSCRIBE或 Spring Data Redis 的RedisMessageListenerContainer监听对应 Channel根据action决定刷新策略。4.4 配置前提Redis Session 开关上文提到发布动作受eruptProp.isRedisSession()控制。该属性定义在 erupt-core 的 EruptProp.java 中前缀为erupt相关配置项如下erupt: # 是否使用 Redis 管理会话同时也是 RedisNotifyDataProxy 发布消息的总开关 redis-session: true # 是否自动续期 Redis 会话中的 token 过期时间仅在 redis-session 为 true 时生效 redis-session-refresh: false需要说明的是redis-session的主要职责是会话token存储介质切换RedisNotifyDataProxy只是复用了这一开关从源码结构看可以推断这是一种顺带复用现有配置的设计而非独立的通知开关。若你的场景不希望会话走 Redis却需要变更通知则需要在业务侧自行扩展 DataProxy 或另行配置开关。五、数据转换与处理工具的定位README 提到的第三类能力 Common data processing and conversion tools通用数据处理与转换工具在erupt-toolkit源码中并没有独立的工具类文件从现有源码结构看这部分能力更多体现在前文三个组件内部对 Gson、VLModel、TypeToken等通用机制的复用上——例如缓存层用GsonFactoryTypeToken完成对象与 JSON 的互转见 EruptCacheRedisSQL 查询结果被转换为 Erupt 注解体系中的VLModelvalue/label 键值对模型见 SqlChoiceFetchHandler变更通知用NotifyData包装动作与数据并序列化为 JSON见 NotifyData。因此通用数据处理与转换是模块的一个能力维度而非一个具体的公开 API 集合。在写作与引用时应将其理解为模块内各组件共享的序列化/模型转换约定。六、接入与使用清单综合以上分析将erupt-toolkit的接入方式与使用要点整理如下6.1 依赖引入在目标模块的pom.xml中加入dependency groupIdxyz.erupt/groupId artifactIderupt-toolkit/artifactId version${erupt.version}/version /dependency模块版本跟随父 POM当前父版本为 2.3.0见 pom.xml。引入后由 EruptToolkitAutoConfiguration 自动扫描装配三个组件无需额外配置类。6.2 三种能力的接入方式速查能力组件接入方式Redis 缓存EruptCacheRedis注入EruptCache接口多实例共享缓存场景或直接注入实现类键值经 Gson 序列化TTL 单位为毫秒SQL 选择项SqlChoiceFetchHandler在ChoiceType(fetchHandler SqlChoiceFetchHandler.class, fetchHandlerParams select id, name from ...)中直接引用params[0]为 SQLparams[1]可选缓存毫秒数变更通知RedisNotifyDataProxy在实体的Erupt(dataProxy RedisNotifyDataProxy.class, params channelName)中挂接需开启erupt.redis-sessionChannel 名取自 params[0]6.3 前置依赖提醒缓存与通知功能都需要 Redis 连接请在应用中配置spring-boot-starter-data-redis对应的连接信息SQL 选择项功能需要数据源spring-boot-starter-jdbc已随模块传递引入配合JdbcTemplate自动装配即可若不需要 Redis 相关能力仅使用SqlChoiceFetchHandler时进程内 LRU 缓存同样可用但模块级依赖仍会引入 Redis starter请按需评估。七、小结erupt-toolkit的体量虽小却精准填补了 Erupt 框架在跨模块共享基础设施上的空白EruptCacheRedis让进程内缓存无缝升级为 Redis 分布式缓存SqlChoiceFetchHandler让任意原生 SQL 都能成为注解驱动的下拉选项来源RedisNotifyDataProxy以极小的侵入成本为多实例部署提供了数据变更广播能力。三者均以erupt-core的抽象接口EruptCache、ChoiceFetchHandler、DataProxy为锚点实现体现了 Erupt注解驱动、面向扩展的一贯设计哲学。在阅读或扩展 Erupt 生态时erupt-toolkit是理解核心抽象如何被各业务模块复用的最佳范本之一。赞分享后端低代码AI 应用人工智能AI Agent认证鉴权RAG【免费下载链接】erupt 通用数据管理框架VORM 对象视图模型注解驱动低代码开发项目地址https://gitcode.com/erupts/erupt点击查看免费下载相关推荐ERUPT缓存策略Redis集成与性能提升ERUPT缓存策略Redis集成与性能提升 ? 引言为什么需要缓存优化 在企业级应用开发中性能瓶颈往往出现在数据库访问层面。当并发用户增多、数据量增大时后端低代码AI 应用人工智能AI Agent认证鉴权RAGERUPT数据湖大数据存储方案深度解析ERUPT数据湖大数据存储方案深度解析 ? 引言数据管理的新范式 在数字化转型浪潮中企业面临着海量数据处理与管理的巨大挑战。传统的数据管理方式往往存在以下后端低代码AI 应用人工智能AI Agent认证鉴权RAGErupt erupt-ai-claw 深度解析把自然语言变成 Erupt 管理操作的内置 AgentErupt erupt ai claw 深度解析把自然语言变成 Erupt 管理操作的内置 Agent erupt ai claw 是 Erupt 框架中的一后端低代码AI 应用人工智能AI Agent认证鉴权RAG上一篇MMD Tools一个支持 MMD 四类文件双向导入导出的 Blender 插件下一篇Win11Debloat完全指南快速给Windows 11去臃肿的免费开源工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考