Spring Boot 3 高并发事务与分布式事务企业级完整解决方案

Spring Boot 3 高并发事务与分布式事务企业级完整解决方案

写这个因为,是有个接口没做事务兜底机制。

从单体高并发 → 分布式高并发 · 全链路异常兜底 · 企业级生产就绪


文档说明

  • 适用版本:Spring Boot 3.2+,Java 17+,JPA / MyBatis / R2DBC 均支持
  • 目标读者:后端架构师、高级开发工程师、SRE
  • 核心价值 :覆盖 单体高并发优化 → 分布式事务选型 → 异常兜底 → 可观测性 → 容灾对账 全生命周期
  • 企业级标准:满足金融、电商、支付等强一致性与高可用场景

一、整体演进路径与选型原则

架构阶段 并发规模 数据一致性要求 推荐方案 是否需分布式事务
单体应用(高并发) 1k~10k TPS 强一致(本地 ACID) 本地事务 + 乐观锁/分段锁 + 异步解耦 ❌ 否
微服务初期(最终一致) 10k+ TPS 最终一致(秒级容忍) Outbox 模式 + MQ + 幂等消费 ✅ 轻量级
强一致场景(金融/支付) 高并发 + 强一致 强一致(毫秒级) Seata TCC / Saga ✅ 强一致性
超大规模(跨境/多云) 100k+ TPS 最终一致 + 对账兜底 Event Sourcing + CDC + 补偿平台 ✅ 最终一致

企业共识

  • 90% 场景用"最终一致性 + 对账"
  • 仅关键资金链路用 TCC
  • 绝不使用 XA(性能差、运维难)

二、单体高并发事务优化方案(企业级)

2.1 核心问题

  • 数据库行锁/间隙锁竞争
  • 事务持有时间过长导致连接池耗尽
  • 死锁频发(尤其 MySQL InnoDB)
  • CPU 飙升、TPS 下降

2.2 企业级解决方案

✅ 方案 A:细粒度事务 + 异步解耦(推荐)
java 复制代码
@Service
public class OrderService {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    // 主流程:极简事务(<50ms)
    @Transactional(timeout = 10)
    public Order createOrder(OrderRequest request) {
        Order order = new Order(request);
        order.setStatus("CREATED");
        order = orderRepo.save(); // 快速提交

        // 发布领域事件(异步处理非核心逻辑)
        eventPublisher.publishEvent(new OrderCreatedEvent(order.getId(), request.getItems()));
        return order;
    }
}

// 异步监听器(独立事务)
@Component
@RequiredArgsConstructor
public class InventoryEventListener {
    private final InventoryService inventoryService;

    @Transactional
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        try {
            inventoryService.reduceStock(event.orderId(), event.items());
        } catch (Exception e) {
            // 记录失败,由补偿任务重试
            compensationService.recordFailure("INVENTORY_REDUCE", event.orderId(), e);
            throw e; // 触发重试机制
        }
    }
}

优势 :主流程快、锁持有时间短、系统吞吐提升 3~5 倍
注意@EventListener 默认同步,需配置 TaskExecutor 实现异步

✅ 方案 B:乐观锁(高并发更新)
java 复制代码
@Entity
public class Account {
    @Version
    private Long version; // JPA 乐观锁字段
    private BigDecimal balance;
}

@Service
@Transactional
public class AccountService {
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        int retry = 0;
        while (retry < 3) {
            try {
                Account from = accountRepo.findById(fromId).orElseThrow();
                Account to = accountRepo.findById(toId).orElseThrow();

                from.setBalance(from.getBalance().subtract(amount));
                to.setBalance(to.getBalance().add(amount));

                accountRepo.save(from);
                accountRepo.save(to);
                return; // 成功退出
            } catch (OptimisticLockException e) {
                retry++;
                if (retry >= 3) throw new ServiceException("并发冲突,请重试", e);
                Thread.sleep(10 * retry); // 指数退避
            }
        }
    }
}

适用 :余额变动、积分增减等低冲突场景
不适用:秒杀库存(高频冲突)

✅ 方案 C:分段锁 + 库存预热(秒杀场景)
java 复制代码
@Component
public class SeckillInventoryService {
    private static final int SEGMENT_COUNT = 16;
    private final Object[] locks = new Object[SEGMENT_COUNT];
    private final Map<String, AtomicInteger[]> inventorySegments = new ConcurrentHashMap<>();

    public SeckillInventoryService() {
        for (int i = 0; i < SEGMENT_COUNT; i++) {
            locks[i] = new Object();
        }
    }

    public boolean trySeckill(String skuId, Long userId) {
        int segment = Math.abs(userId.hashCode()) % SEGMENT_COUNT;
        synchronized (locks[segment]) {
            AtomicInteger[] segments = inventorySegments.computeIfAbsent(skuId, k -> 
                IntStream.range(0, SEGMENT_COUNT).mapToObj(i -> new AtomicInteger(getTotalStock(k) / SEGMENT_COUNT)).toArray(AtomicInteger[]::new)
            );

            if (segments[segment].get() > 0) {
                segments[segment].decrementAndGet();
                return true;
            }
        }
        return false;
    }

    // 定时同步到 DB(每秒一次)
    @Scheduled(fixedRate = 1000)
    public void syncToDatabase() {
        // 批量更新 DB 库存
    }
}

效果:将全局锁 → 16 个局部锁,并发能力提升 10 倍+


三、分布式高并发事务方案(企业级)

3.1 方案选型矩阵

方案 一致性 性能 开发成本 运维成本 适用场景
Outbox + MQ(最终一致) 最终一致 ⭐⭐⭐⭐⭐ ⭐⭐ 电商下单、社交互动
Seata TCC 强一致 ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ 支付转账、积分兑换
Saga(编排式) 最终一致 ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐ 订单履约、审批流
XA 强一致 ⭐⭐ ⭐⭐⭐⭐ 遗留系统(禁用)

企业推荐

  • 默认选择 Outbox + MQ
  • 仅当业务无法容忍任何不一致时用 TCC

3.2 方案一:Outbox 模式 + MQ(最终一致性 · 企业首选)

架构图
  1. 本地事务 2. 定时任务 3. 异步投递 4. 幂等消费 5. 成功 6. 失败 7. 人工补偿 8. 每日跑批 订单服务
    B
    RabbitMQ/Kafka
    库存服务
    扣库存
    ACK
    死信队列
    补偿管理平台
    对账系统
    核对订单 vs 库存
关键实现
(1)Outbox 消息表(与业务同库)
sql 复制代码
CREATE TABLE outbox_message (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    event_type VARCHAR(100) NOT NULL,
    payload JSON NOT NULL,
    business_key VARCHAR(100) NOT NULL, -- 如 order_id
    status ENUM('PENDING', 'SENT') DEFAULT 'PENDING',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    sent_at DATETIME NULL,
    INDEX idx_status_created (status, created_at),
    UNIQUE KEY uk_biz_event (business_key, event_type)
);
(2)可靠消息发送(带重试)
java 复制代码
@Service
@RequiredArgsConstructor
public class OutboxMessageService {
    private final OutboxMessageRepository outboxRepo;
    private final MqProducer mqProducer;

    @Scheduled(fixedDelay = 2000)
    @Transactional
    public void sendPendingMessages() {
        List<OutboxMessage> messages = outboxRepo.findPendingBefore(Instant.now().minusSeconds(10));
        for (OutboxMessage msg : messages) {
            try {
                mqProducer.send(msg.getEventType(), msg.getPayload());
                msg.markAsSent();
                outboxRepo.save(msg);
            } catch (Exception e) {
                log.warn("消息发送失败,稍后重试: {}", msg.getId(), e);
                // 不抛异常,避免事务回滚
            }
        }
    }

    // 业务方法中保存消息
    public void saveMessage(String eventType, String payload, String businessKey) {
        outboxRepo.save(new OutboxMessage(eventType, payload, businessKey));
    }
}
(3)幂等消费(双保险)
java 复制代码
@RabbitListener(queues = "inventory.queue")
public void consume(InventoryEvent event) {
    String idempotentKey = event.getOrderId() + ":DECREASE";

    // 1. Redis 快速去重
    if (redis.hasKey("IDEMPOTENT:" + idempotentKey)) {
        return;
    }

    // 2. DB 唯一索引兜底
    try {
        idempotentLogService.log(idempotentKey); // 唯一索引 INSERT
        inventoryService.decrease(event.getItems());
    } catch (DuplicateKeyException e) {
        log.info("重复消费,跳过: {}", idempotentKey);
        return;
    } catch (Exception e) {
        // 系统异常:MQ 自动重试(最多5次)
        throw new AmqpRejectAndDontRequeueException(e);
    }
}
(4)兜底:补偿平台 + 对账
  • 补偿平台:提供 UI 查看失败事件、手动重试、跳过、数据修正

  • 对账任务

    java 复制代码
    @Scheduled(cron = "0 0 2 * * ?") // 每日凌晨2点
    public void dailyReconciliation() {
        List<Order> paidOrders = orderRepo.findPaidYesterday();
        for (Order order : paidOrders) {
            if (!inventoryService.isAllocated(order.getId())) {
                alarmService.send("库存未分配", order.getId());
                // 自动触发补偿 or 人工介入
            }
        }
    }

3.3 方案二:Seata TCC(强一致性 · 金融级)

架构图
复制代码
[Client] 
   │
   ▼
[Order Service] → @GlobalTransactional
   │
   ├──→ [Inventory Service] → @LocalTCC (Try/Confirm/Cancel)
   └──→ [Account Service] → @LocalTCC (Try/Confirm/Cancel)
   │
   ▼
[Seata Server (TC)] ← 协调全局事务
关键代码
(1)Seata 配置(application.yml)
yaml 复制代码
seata:
  enabled: true
  application-id: ${spring.application.name}
  tx-service-group: my_tx_group
  service:
    vgroup-mapping:
      my_tx_group: default
  registry:
    type: nacos
    nacos:
      server-addr: ${NACOS_SERVER}
  config:
    type: nacos
(2)TCC 接口实现
java 复制代码
@LocalTCC
public interface InventoryTccService {
    @TwoPhaseBusinessAction(name = "decreaseInventory", commitMethod = "confirm", rollbackMethod = "cancel")
    boolean prepareDecrease(BusinessActionContext ctx, Long orderId, List<Item> items);

    boolean confirm(BusinessActionContext ctx);
    boolean cancel(BusinessActionContext ctx);
}

@Component
public class InventoryTccServiceImpl implements InventoryTccService {

    @Override
    public boolean prepareDecrease(BusinessActionContext ctx, Long orderId, List<Item> items) {
        // Try: 冻结库存(必须幂等)
        return inventoryRepo.freezeIfNotExists(orderId, items);
    }

    @Override
    public boolean confirm(BusinessActionContext ctx) {
        try {
            inventoryRepo.confirmFrozen(ctx.getActionContext("orderId"));
            return true;
        } catch (Exception e) {
            log.error("Confirm 失败,需人工介入!", e);
            alarmService.sendCritical("TCC Confirm 失败", ctx.getXid());
            return false; // 不会自动重试!
        }
    }

    @Override
    public boolean cancel(BusinessActionContext ctx) {
        try {
            inventoryRepo.releaseFrozen(ctx.getActionContext("orderId"));
            return true;
        } catch (Exception e) {
            // Cancel 失败是严重事故!
            alarmService.sendCritical("TCC Cancel 失败", ctx.getXid());
            return false;
        }
    }
}
(3)全局事务调用
java 复制代码
@Service
public class OrderService {
    @GlobalTransactional(timeoutMills = 30000)
    public void createOrderWithTcc(OrderRequest request) {
        orderRepo.create(request); // 本地 RM
        inventoryService.prepareDecrease(request.getItems()); // 远程 TCC
        accountService.prepareFreeze(request.getAmount());     // 远程 TCC
    }
}

TCC 注意事项

  • prepare 必须幂等
  • confirm/cancel 失败需告警 + 人工处理
  • 全局事务日志(undo_log)需定期归档

第四章:事务异常兜底与企业级容灾方案(完整重写)

目标 :确保在任何异常场景下,系统具备自动恢复能力人工干预通道数据最终一致性保障
适用范围 :Spring Boot 3 单体高并发 & 分布式微服务架构
核心理念"事务可能失败,但业务不能中断"


4.1 异常分类与响应策略(企业级标准)

4.1.1 异常类型定义

异常大类 具体场景 是否可自动恢复 事务影响
业务异常 余额不足、库存不足、参数校验失败 ❌ 不可恢复(需用户修正) 立即回滚,不触发重试
系统异常 NPE、数据库连接失败、序列化错误 ✅ 可恢复(重试后可能成功) 回滚 + 自动重试
基础设施异常 网络超时、MQ 不可用、DB 主从切换 ✅ 可恢复(依赖外部恢复) 本地持久化 + 异步重试
服务不可用 下游服务宕机、 ✅ 可恢复(服务恢复后重试) 消息队列 + 死信机制
数据不一致 跨服务状态不同步(如订单已付但库存未扣) ⚠️ 部分可自动修复 对账 + 补偿

📌 企业原则

  • 业务异常 ≠ 系统异常:前者快速失败,后者必须重试
  • 绝不静默吞异常:所有异常必须记录 traceId 并分级告警

4.2 单体应用异常兜底方案

4.2.1 核心问题

  • 事务内异常导致回滚,但调用方不知情
  • 异步任务失败无追踪
  • 重试无上限导致雪崩

4.2.2 企业级解决方案

✅ 方案:事务边界清晰 + 异步任务可靠执行
java 复制代码
@Service
@RequiredArgsConstructor
public class OrderService {
    private final TaskReliableExecutor taskExecutor; // 可靠异步执行器

    @Transactional(rollbackFor = Exception.class)
    public Order createOrder(OrderRequest request) {
        // 1. 核心事务(快速提交)
        Order order = new Order(request);
        order.setStatus("CREATED");
        order = orderRepo.save();

        // 2. 提交后立即触发异步任务(不在事务内!)
        taskExecutor.submitReliableTask(
            "ORDER_PROCESS", 
            order.getId(), 
            () -> processOrderAsync(order.getId())
        );
        return order;
    }

    // 独立事务方法(由可靠执行器调用)
    @Transactional(rollbackFor = Exception.class)
    public void processOrderAsync(Long orderId) {
        try {
            inventoryService.reduceStock(orderId);
            paymentService.charge(orderId);
            orderRepo.updateStatus(orderId, "PAID");
        } catch (InsufficientStockException e) {
            // 业务异常:标记订单失败,不重试
            orderRepo.updateStatus(orderId, "FAILED", "库存不足");
            throw new BusinessException(e.getMessage());
        } catch (Exception e) {
            // 系统异常:抛出,由可靠执行器重试
            log.error("[orderId={}] 处理失败,将重试", orderId, e);
            throw e;
        }
    }
}
🔧 可靠异步执行器实现(带重试+持久化)
java 复制代码
@Component
public class TaskReliableExecutor {
    private final AsyncTaskRepository taskRepo;
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);

    public void submitReliableTask(String taskType, String businessKey, Runnable task) {
        // 1. 持久化任务到 DB(与主事务同库)
        AsyncTask asyncTask = new AsyncTask(taskType, businessKey, 0);
        taskRepo.save(asyncTask);

        // 2. 立即执行(或延迟执行)
        executeTask(asyncTask);
    }

    private void executeTask(AsyncTask task) {
        try {
            // 执行具体业务(通过反射或策略模式)
            taskStrategy.execute(task.getTaskType(), task.getBusinessKey());
            
            // 成功:删除任务
            taskRepo.deleteById(task.getId());
        } catch (BusinessException e) {
            // 业务异常:标记失败,不再重试
            taskRepo.markFailed(task.getId(), e.getMessage());
        } catch (Exception e) {
            // 系统异常:重试(最多5次,指数退避)
            int retryCount = task.getRetryCount() + 1;
            if (retryCount <= 5) {
                long delay = (long) Math.pow(2, retryCount) * 1000; // 2s, 4s, 8s...
                taskRepo.incrementRetry(task.getId());
                scheduler.schedule(() -> executeTask(task), delay, TimeUnit.MILLISECONDS);
            } else {
                // 超过重试次数:进入死信表,告警
                taskRepo.moveToDeadLetter(task.getId(), e.toString());
                alarmService.send("异步任务失败", task.toString());
            }
        }
    }
}

优势

  • 主流程无阻塞
  • 异常可追踪、可重试、可人工干预
  • 与主业务同库,避免分布式事务

4.3 分布式事务异常兜底方案

4.3.1 最终一致性(Outbox + MQ)兜底体系

四层防护机制:
防护层 组件 功能 企业级要求
第一层:发送可靠 Outbox 表 + 定时任务 确保消息不丢失 消息与业务同事务
第二层:消费幂等 Redis + DB 唯一索引 防重复消费 双保险去重
第三层:失败重试 MQ 死信队列(DLQ) 自动重试 + 隔离失败消息 重试策略可配置
第四层:人工兜底 补偿管理平台 查看/重试/跳过/修正 提供 Web UI
关键代码增强:死信队列处理
java 复制代码
// RabbitMQ 死信队列配置
@Bean
public Queue deadLetterQueue() {
    return QueueBuilder.durable("inventory.dlq").build();
}

@RabbitListener(queues = "inventory.dlq")
public void handleDeadLetter(Message message) {
    String payload = new String(message.getBody());
    String orderId = extractOrderId(payload);
    
    // 记录到补偿平台
    compensationService.recordFailure(
        "INVENTORY_REDUCE", 
        orderId, 
        new String(message.getBody()), 
        message.getMessageProperties().getRedelivered() ? "重试失败" : "首次失败"
    );
    
    // 发送企业微信/钉钉告警
    alarmService.sendCritical("库存扣减死信", "订单: " + orderId);
}

4.3.2 TCC 模式兜底体系

TCC 异常处理黄金法则:

"Try 可重试,Confirm/CANCEL 必须尽最大努力成功"

兜底措施:
  1. Cancel 失败告警

    java 复制代码
    @Override
    public boolean cancel(BusinessActionContext ctx) {
        try {
            inventoryRepo.releaseFrozen(ctx.getActionContext("orderId"));
            return true;
        } catch (Exception e) {
            // 立即触发 P0 级告警
            alarmService.sendP0("TCC Cancel 失败", ctx.getXid(), e);
            // 尝试备用方案(如调用 Admin API 手动释放)
            adminApi.releaseFrozenInventory(ctx.getActionContext("orderId"));
            return false; // Seata 会记录日志
        }
    }
  2. Confirm 失败人工介入

    • Seata 控制台提供 "悬挂事务" 列表
    • 运维可手动 Commit / Rollback
  3. 全局事务日志归档

    • undo_log 表每日归档至冷存储
    • 保留 180 天用于审计

4.4 企业级容灾四件套

4.4.1 幂等性(所有入口统一治理)

java 复制代码
// 通用幂等注解(支持 SpEL)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    String key() default "#requestId"; // 默认取 requestId
    long expireSeconds() default 3600;
}

// AOP 实现(Redis + 本地缓存二级)
@Aspect
public class IdempotentAspect {
    @Around("@annotation(idempotent)")
    public Object handle(ProceedingJoinPoint jp, Idempotent idempotent) {
        String key = parseSpel(idempotent.key(), jp.getArgs());
        String redisKey = "IDEMPOTENT:" + key;
        
        // 1. 本地缓存快速判断(Caffeine)
        if (localCache.getIfPresent(redisKey) != null) {
            throw new DuplicateRequestException();
        }
        
        // 2. Redis 分布式锁(SET NX EX)
        Boolean acquired = redis.setIfAbsent(redisKey, "1", Duration.ofSeconds(idempotent.expireSeconds()));
        if (Boolean.FALSE.equals(acquired)) {
            throw new DuplicateRequestException();
        }
        
        try {
            Object result = jp.proceed();
            localCache.put(redisKey, result); // 缓存结果(可选)
            return result;
        } finally {
            // 不主动删除,靠 TTL 自动过期(防重试期间误删)
        }
    }
}

4.4.2 补偿管理平台(人工兜底)

  • 功能清单

    • 查看失败事件列表(按服务/时间/类型过滤)
    • 一键重试 / 批量重试
    • 跳过(标记为成功)
    • 数据修正(直接调用 Admin API)
    • 导出 CSV 用于对账
  • 技术实现

    sql 复制代码
    CREATE TABLE compensation_task (
        id BIGINT PRIMARY KEY,
        service_name VARCHAR(100),
        event_type VARCHAR(100),
        business_key VARCHAR(100),
        payload JSON,
        status ENUM('FAILED', 'RETRIED', 'SKIPPED'),
        error_message TEXT,
        created_at DATETIME,
        updated_at DATETIME
    );

4.4.3 对账系统(数据一致性最后防线)

  • 对账类型

    对账类型 频率 说明
    实时对账 事件驱动 关键操作后立即核对(如支付成功后查库存)
    准实时对账 5分钟一次 扫描最近变更数据
    离线对账 每日凌晨 全量比对(T+1)
  • 对签示例(离线)

    java 复制代码
    @Scheduled(cron = "0 0 2 * * ?")
    public void dailyReconciliation() {
        List<ReconcileItem> mismatches = reconciliationService.findMismatches(
            LocalDate.now().minusDays(1)
        );
        
        for (ReconcileItem item : mismatches) {
            if (item.canAutoFix()) {
                autoFixService.fix(item);
            } else {
                // 生成工单,通知运营
                ticketService.create("数据不一致", item.toString());
                alarmService.send("对账失败", item.getBusinessKey());
            }
        }
    }

4.4.4 可观测性(三位一体)

维度 工具 关键指标
Trace SkyWalking / Zipkin 事务链路耗时、异常节点
Metrics Prometheus + Grafana Outbox 积压数、MQ 消费延迟、TCC 异常率
Log ELK / Loki 按 traceId 聚合日志,标记 [TXN]

Grafana 告警规则示例

  • outbox_pending_count > 100 → P2 告警
  • tcc_cancel_failure_rate > 0 → P0 告警

4.5 企业级上线 Checklist(异常兜底专项)

检查项 验证方式 负责人
所有对外 API 支持 X-Request-ID Postman 测试 开发
MQ 消费者实现幂等 模拟重复消息 开发
Outbox 定时任务覆盖所有事件类型 代码审查 架构师
补偿平台可查看最近 7 天失败事件 UI 验收 测试
对账任务每日运行且有报告 检查日志 SRE
TCC 的 Cancel 方法有 P0 告警 Chaos 演练 SRE
所有异常包含 traceId 日志采样检查 SRE

本章核心思想
"没有完美的事务,只有完备的兜底"

企业级系统不追求 100% 不出错,而是确保 "错可发现、错可恢复、错可追溯"


五、企业级 Checklist

类别 检查项 是否完成
事务设计 所有 @Transactional 明确 rollbackFor
避免 this 调用、非 public 方法
幂等性 API 层:X-Request-ID 去重
MQ 消费:Redis + DB 双幂等
异常处理 不吞异常,系统异常可重试
业务异常快速失败,不重试
可观测性 全链路 TraceID 透传
关键指标告警(积压 > 100)
容灾 对账任务每日运行
补偿平台支持人工干预
压测 模拟 2 倍峰值流量
混沌演练(网络分区、DB 故障)

六、总结:企业级事务架构建议

场景 推荐方案 关键保障
单体高并发 乐观锁 + 异步解耦 细粒度事务、分段锁
普通微服务 Outbox + MQ 幂等消费、补偿平台、对账
金融强一致 Seata TCC Cancel 幂等、人工兜底
超长流程 Saga 编排 补偿可重试、状态机持久化

终极原则

  • 简单优于复杂:能用最终一致就不用 TCC
  • 可观测性 > 事务本身:没有监控的事务等于没有事务
  • 对账是最后一道防线:再完美的事务也需要对账兜底
相关推荐
giaz14n9X17 小时前
Redis 分布式锁进阶第五十七篇
数据库·redis·分布式
指令集梦境18 小时前
Cursor + Spring Boot实战:从零写一个RESTful API
spring boot·后端·restful
WyCAGy8ij18 小时前
Redis 分布式锁进阶第二篇讲解
数据库·redis·分布式
冰西瓜60021 小时前
深度学习的数学原理(四十二)—— 分布式训练
人工智能·分布式·深度学习
普通网友21 小时前
springboot之集成Elasticsearch
spring boot·后端·elasticsearch
invicinble21 小时前
关于flowable流程引擎技术栈相关
spring boot
倒流时光三十年1 天前
第十八章 搜索历史保存功能实现记录
spring boot·微信小程序
倒流时光三十年1 天前
第十七章 投票页面增加搜索功能
spring boot·微信小程序
郑洁文1 天前
基于Springboot的足球青训俱乐部管理系统的设计与实现
java·spring boot·后端·足球青训俱乐部管理系统
我登哥MVP1 天前
Spring Boot 从“会用”到“精通”:自定义参数绑定原理
java·spring boot·后端·spring·servlet·maven·intellij-idea