SpringBoot电商购物车、结算、下单、库存方案设计与落地实施步骤

SpringBoot电商购物车、结算、下单、库存方案设计与落地实施步骤

一、整体业务架构与核心流程概述

1.1 核心业务链路

用户购物全链路:浏览商品 → 加入购物车 → 购物车修改/删除 → 确认结算 → 生成订单 → 扣减库存 → 支付 → 订单完成

核心难点:并发库存超卖、下单事务一致性、购物车数据同步、结算价格准确性、订单幂等性、库存回滚机制,本方案针对电商高并发、数据一致性核心问题做生产级设计。

1.2 技术栈选型(生产标配)

  • 核心框架:SpringBoot 2.7.x / 3.2.x、Spring MVC、Spring Transaction
  • 数据库:MySQL 8.0(InnoDB,支持事务、行锁)
  • 缓存:Redis(购物车缓存、库存缓存、分布式锁、防重令牌)
  • 消息队列:RabbitMQ(订单延时关闭、库存异步回滚、削峰填谷)
  • 持久层:MyBatis-Plus
  • 并发控制:Redis分布式锁、数据库行锁、乐观锁
  • 幂等性:唯一订单号、防重Token、请求幂等校验

二、数据库表核心设计

仅展示购物车、订单、库存核心业务表,精简冗余字段,贴合实战需求。

2.1 购物车表(shop_cart)

核心说明:购物车数据优先缓存,数据库做持久化,支持用户多设备同步

|-------------|----------|-------------------------|
| 字段名 | 类型 | 说明 |
| id | bigint | 主键ID |
| user_id | bigint | 用户ID(唯一索引) |
| sku_id | bigint | 商品SKU ID(唯一索引,用户+sku唯一) |
| sku_num | int | 购买数量 |
| price | decimal | 加入购物车时单价(留存历史价格) |
| checked | tinyint | 是否选中 0-未选中 1-选中(结算依据) |
| create_time | datetime | 创建时间 |
| update_time | datetime | 更新时间 |

2.2 商品库存表(shop_sku_stock)

核心说明:采用预扣库存+可售库存双字段设计,解决超卖与库存回滚问题

|--------------|--------|----------------|
| 字段名 | 类型 | 说明 |
| sku_id | bigint | SKU主键(唯一索引) |
| stock | int | 可售库存(真实可下单库存) |
| locked_stock | int | 锁定库存(下单预扣、未支付) |
| version | int | 乐观锁版本号(解决并发超卖) |

2.3 订单主表、订单明细表

订单主表存储订单整体信息,订单明细表存储下单商品明细,一对多关联,此处省略基础字段,核心关键字段:订单号、用户ID、订单状态、实付金额、收货地址、创建时间。

三、核心模块详细方案设计

3.1 购物车模块设计(缓存+数据库双存)

3.1.1 存储方案

未登录用户:数据存储在浏览器LocalStorage,本地临时存储,不同步服务端;登录用户:数据同步至Redis+MySQL,Redis做热点查询,MySQL做持久化兜底。

Redis存储结构:CART:{userId},Hash结构,field=skuId,value=购物车商品信息(数量、价格、选中状态),设置过期时间(30天),自动清理无效数据。

3.1.2 核心功能逻辑

  • 加入购物车:先查Redis,存在则更新数量,不存在则新增,异步同步至MySQL,提升响应速度
  • 修改购物车:更新数量、选中状态,优先更新缓存,异步落库
  • 删除购物车:删除缓存数据,异步删除数据库数据
  • 购物车合并:用户登录时,合并本地LocalStorage数据与服务端缓存数据,去重求和

优势:避开数据库频繁读写,支撑高并发购物车操作,提升用户体验。

3.2 结算模块设计(价格校验+数据预校验)

3.2.1 结算前置校验

用户点击结算时,批量校验选中购物车商品:商品是否下架、SKU是否有效、库存是否充足、价格是否变动,无效商品自动过滤并提示用户。

3.2.2 价格计算规则

  • 商品原价:以实时商品价格为准,不使用购物车历史价格,避免价格滞后问题
  • 优惠计算:满减、优惠券、折扣叠加计算,统一结算总价、优惠金额、实付金额
  • 价格防篡改:服务端重新计算所有价格,拒绝前端传参价格,杜绝前端篡改价格漏洞

3.3 下单模块核心设计(幂等+事务+防重)

3.3.1 下单核心流程

获取防重Token → 校验购物车选中数据 → 校验库存 → 分布式锁锁定商品 → 生成订单号 → 开启事务 → 保存订单信息 → 预扣锁定库存 → 清除已结算购物车 → 提交事务 → 发送延时消息 → 跳转支付

3.3.2 核心解决方案

  • 幂等性设计:基于Redis防重Token,下单前获取唯一Token,下单成功后删除Token,防止重复提交订单;订单号采用雪花算法,全局唯一
  • 事务一致性:Spring声明式事务,保证订单创建、库存锁定、购物车清除原子性,任意异常全部回滚
  • 并发防重:Redis分布式锁(Lock+skuId),防止同一商品并发下单导致数据异常

3.4 库存核心方案(生产级防超卖)

3.4.1 库存状态流转

可售库存 → 锁定库存(下单预扣)→ 真实扣减(支付成功) / 解锁回滚(超时未支付/取消订单)

3.4.2 防超卖双重机制

  1. 数据库乐观锁兜底:更新库存时校验version版本号,同时判断可售库存≥扣减数量,防止并发超卖
  1. Redis库存缓存预拦截:热点商品库存缓存至Redis,下单先校验缓存库存,不足直接拦截,减少数据库压力

3.4.3 库存回滚机制

  • 超时未支付:下单后15分钟未支付,RabbitMQ延时队列监听,自动解锁锁定库存、关闭订单
  • 主动取消订单:用户手动取消,立即回滚锁定库存,恢复可售库存
  • 支付失败:支付回调异常,触发库存补偿回滚机制

四、完整落地实施步骤(可直接落地执行)

步骤1:环境搭建与基础配置

  1. 搭建SpringBoot项目,引入依赖:web、mybatis-plus、redis、rabbitmq、lombok
  1. 配置MySQL、Redis、RabbitMQ连接参数,开启事务管理、Redis序列化配置
  1. 生成数据库对应实体类、Mapper、Service基础代码
  1. 封装Redis工具类、分布式锁工具类、雪花算法订单号工具类

步骤2:购物车模块开发落地

  1. 实现未登录用户LocalStorage购物车基础交互
  1. 开发登录用户Redis购物车CRUD接口:新增、修改、删除、查询
  1. 开发用户登录购物车合并功能,解决多设备数据同步问题
  1. 实现缓存异步落库、过期清理机制
  1. 接口测试:并发加入购物车、重复添加、数量修改场景验证

步骤3:结算模块开发落地

  1. 开发购物车选中商品批量校验接口(商品状态、库存、价格)
  1. 开发服务端价格计算逻辑,封装优惠、满减、总价计算工具类
  1. 实现结算数据组装、无效商品过滤、价格防篡改校验
  1. 测试场景:商品涨价、下架、库存不足时结算异常处理

步骤4:下单核心模块开发落地

  1. 开发防重Token生成、校验、删除接口,实现订单防重提交
  1. 封装Redis分布式锁工具类,实现商品并发锁定控制
  1. 开发下单核心事务逻辑:订单生成、明细保存、库存预锁定
  1. 开发购物车结算后清空逻辑,只删除已选中结算商品
  1. 集成RabbitMQ,配置订单延时关闭队列、库存回滚队列

步骤5:库存模块开发与防超卖落地

  1. 实现Redis库存缓存预热,热点商品库存同步至缓存
  1. 开发库存预扣、真实扣减、库存解锁回滚接口
  1. 实现数据库乐观锁库存更新逻辑,兜底防超卖
  1. 开发延时订单关闭、库存自动释放消费者逻辑
  1. 库存异常补偿机制:定时任务校对缓存库存与数据库库存一致性

步骤6:支付联动与订单状态流转

  1. 对接支付回调接口,支付成功后完成真实库存扣减、订单状态更新
  1. 支付失败/退款触发库存回滚、订单关闭逻辑
  1. 完善订单全状态流转:待支付、已支付、已取消、已完成、已退款

步骤7:压力测试与BUG修复

  1. JMeter压测:高并发下单场景,验证无超卖、无重复订单
  1. 异常测试:网络中断、事务回滚、MQ消息丢失、缓存失效场景
  1. 数据一致性校验:库存、订单、购物车数据联动准确性

五、核心问题解决方案与优化策略

5.1 超卖问题终极解决

Redis缓存拦截(前置)+ 分布式锁(并发控制)+ 数据库乐观锁(最终兜底),三层防护,彻底杜绝超卖问题,适配秒杀、普通下单场景。

5.2 订单重复提交问题

前端按钮置灰 + Redis防重Token + 订单幂等校验,三层拦截,完全避免重复下单。

5.3 库存数据不一致问题

  • 实时校验:下单、支付、回滚全流程数据校验
  • 定时补偿:每日定时任务校对Redis缓存库存与数据库库存,修复偏差数据
  • MQ保证可靠消息:消息确认机制、重试机制、死信队列处理异常消息

5.4 性能优化

  • 购物车、热点库存全部缓存化,减少DB查询压力
  • 下单核心逻辑异步化、MQ削峰,应对流量峰值
  • 数据库加索引:用户ID、SKU ID、订单号、状态字段,提升查询效率
  • 读写分离:查询走缓存/从库,写入走主库

六、生产落地注意事项

  1. 禁止前端传任何价格、库存相关参数,所有核心数据服务端自主计算校验
  1. 分布式锁必须设置过期时间,防止死锁,同时适配锁续期逻辑
  1. 延时队列必须配置死信队列,处理异常未消费消息,避免库存卡死
  1. 所有库存、订单操作日志留存,方便线上问题排查
  1. 缓存穿透、击穿、雪崩问题做兜底:空值缓存、互斥锁、过期时间随机化

SpringBoot 电商购物车‑结算‑下单‑库存 核心代码实现

依赖:SpringBoot2.7、MyBatis‑Plus、RedisTemplate、RabbitMQ、Lombok 业务前提:

  1. sku_stock:sku_id,stock (可售),locked_stock (锁定),version (乐观锁)
  2. 防重 token:下单前获取 token 放入 redis,下单消费删除;防止重复提交
  3. 库存模式:下单预扣锁定库存;支付成功真实扣可售;超时 / 取消解锁回滚
  4. Redis 分布式锁;RabbitMQ 延时队列关闭订单 + 释放锁定库存

1. Redis 工具类、分布式锁工具

复制代码
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

@Component
public class RedisLockUtil {

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 获取分布式锁
     * @param key 锁key
     * @param value 线程标识
     * @param expire 过期时间秒
     */
    public boolean tryLock(String key,String value,long expire){
        String script = "if redis.call('set',KEYS[1],ARGV[1],'NX','EX',ARGV[2]) then return 1 else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long res = redisTemplate.execute(redisScript, Collections.singletonList(key),value,expire);
        return res != null && res ==1;
    }

    /**
     * 释放锁 lua脚本保证原子性:只能释放自己持有的锁
     */
    public boolean unLock(String key,String value){
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long res = redisTemplate.execute(redisScript, Collections.singletonList(key),value);
        return res != null && res ==1;
    }

    /**
     * 生成下单防重token
     */
    public String generateOrderToken(Long userId){
        String token = java.util.UUID.randomUUID().toString().replace("-","");
        redisTemplate.opsForValue().set("order:token:"+userId,token,15, TimeUnit.MINUTES);
        return token;
    }

    /**
     * 校验并消费token(一次性,校验通过立即删除)
     */
    public boolean checkAndConsumeToken(Long userId,String token){
        String key = "order:token:"+userId;
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long res = redisTemplate.execute(redisScript, Collections.singletonList(key),token);
        return res != null && res ==1;
    }
}

2. SKU 库存 Mapper(乐观锁预扣锁定库存、真实扣减、解锁回滚)

复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

public interface SkuStockMapper extends BaseMapper<SkuStock> {

    /**
     * 下单:预扣锁定库存
     * stock 可售减少 num,locked_stock增加num,乐观锁version
     * 返回更新行数,0代表扣减失败(库存不足/版本冲突)
     */
    int lockStock(@Param("skuId") Long skuId, @Param("num") Integer num, @Param("version") Integer version);

    /**
     * 支付成功:真实扣减锁定库存
     * locked_stock - num
     */
    int realDeductLockedStock(@Param("skuId") Long skuId, @Param("num") Integer num);

    /**
     * 取消/超时未支付:解锁回滚库存
     * locked_stock - num ; stock + num
     */
    int unlockStock(@Param("skuId") Long skuId, @Param("num") Integer num);
}

<!-- SkuStockMapper.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxx.mapper.SkuStockMapper">

    <update id="lockStock">
        update sku_stock
        set stock = stock - #{num},
            locked_stock = locked_stock + #{num},
            version = version +1
        where sku_id = #{skuId}
        and version = #{version}
        and stock >= #{num}
    </update>

    <update id="realDeductLockedStock">
        update sku_stock
        set locked_stock = locked_stock - #{num}
        where sku_id = #{skuId}
        and locked_stock >= #{num}
    </update>

    <update id="unlockStock">
        update sku_stock
        set locked_stock = locked_stock - #{num},
            stock = stock + #{num}
        where sku_id = #{skuId}
        and locked_stock >= #{num}
    </update>
</mapper>

3. 购物车 Service 核心片段(Redis Hash 为主,异步落库 MySQL)

复制代码
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;

@Service
public class CartService {

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    private static final String CART_PREFIX = "CART:";

    /**
     * 添加购物车
     */
    public void addCart(Long userId,Long skuId,Integer num){
        String cartKey = CART_PREFIX + userId;
        // hash field:skuId ; value json:{num,price,checked}
        Map<String,Object> cartItem = redisTemplate.opsForHash().entries(cartKey);
        if(cartItem.containsKey(skuId.toString())){
            //累加数量
            redisTemplate.opsForHash().increment(cartKey,skuId.toString(),num);
        }else{
            redisTemplate.opsForHash().put(cartKey,skuId.toString(),num);
        }
        // 异步线程池同步mysql,不阻塞主流程
    }

    /**
     * 获取用户选中购物车商品(用于结算)
     */
    public Map<Object,Object> getCheckedCartItems(Long userId){
        String cartKey = CART_PREFIX + userId;
        return redisTemplate.opsForHash().entries(cartKey);
    }

    /**
     * 下单成功:清除已结算购物车商品
     */
    public void clearCheckedCart(Long userId,java.util.List<Long> skuIdList){
        String cartKey = CART_PREFIX + userId;
        for(Long skuId : skuIdList){
            redisTemplate.opsForHash().delete(cartKey,skuId.toString());
        }
    }
}

4. 下单核心 Service(事务、防重 Token、分布式锁、预扣库存、发送延时消息)

业务入参:userId、token、结算选中 sku 列表 (skuId,num) 说明:

  • 先消费防重 token;
  • 循环商品加分布式锁;
  • 查询 sku_stock 拿到 version;乐观锁 lockStock;
  • @Transactional 保证订单主表、订单明细表、库存锁定、购物车清理原子;
  • 事务提交后发送 RabbitMQ 延时消息(15 分钟)关闭订单
复制代码
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
import java.util.UUID;

@Service
public class OrderCreateService {

    @Resource
    private RedisLockUtil redisLockUtil;
    @Resource
    private SkuStockMapper skuStockMapper;
    @Resource
    private CartService cartService;
    @Resource
    private OrderMapper orderMapper;
    @Resource
    private OrderItemMapper orderItemMapper;
    @Resource
    private RabbitMqProducer rabbitMqProducer;
    @Resource
    private SkuStockService skuStockService;

    /**
     * 创建订单入口
     * @param userId 用户id
     * @param token 前端从接口获取的下单防重token
     * @param orderItemDTOList 结算商品 skuId,num
     * @return orderNo 订单号
     */
    @Transactional(rollbackFor = Exception.class)
    public String createOrder(Long userId, String token, List<OrderItemDTO> orderItemDTOList){
        //1.校验并消费防重Token,失败拒绝下单
        boolean tokenOk = redisLockUtil.checkAndConsumeToken(userId, token);
        if(!tokenOk){
            throw new RuntimeException("请勿重复提交订单");
        }

        // 生成雪花订单号
        String orderNo = generateOrderNo();

        for (OrderItemDTO dto : orderItemDTOList) {
            Long skuId = dto.getSkuId();
            Integer num = dto.getNum();
            String lockKey = "lock:sku:" + skuId;
            String lockValue = UUID.randomUUID().toString();
            boolean lock = redisLockUtil.tryLock(lockKey, lockValue, 30);
            if(!lock){
                throw new RuntimeException("商品繁忙,请稍后重试");
            }
            try {
                //查询库存+version
                SkuStock skuStock = skuStockMapper.selectById(skuId);
                if(skuStock == null || skuStock.getStock() < num){
                    throw new RuntimeException("商品库存不足");
                }
                //乐观锁预扣锁定库存
                int affect = skuStockMapper.lockStock(skuId, num, skuStock.getVersion());
                if(affect <=0){
                    throw new RuntimeException("并发下单,库存扣减失败");
                }
            }finally {
                redisLockUtil.unLock(lockKey,lockValue);
            }
        }

        // 构建订单主表、订单明细表入库
        Order order = buildOrder(userId,orderNo);
        orderMapper.insert(order);
        List<OrderItem> itemList = buildOrderItem(order.getId(),orderNo,orderItemDTOList);
        orderItemMapper.insertBatchSomeColumn(itemList);

        // 清除购物车已结算商品
        List<Long> skuIds = orderItemDTOList.stream().map(OrderItemDTO::getSkuId).collect(java.util.stream.Collectors.toList());
        cartService.clearCheckedCart(userId,skuIds);

        // 事务提交之后发送延时消息(注意:不要写在事务内部,用TransactionSynchronization)
        org.springframework.transaction.support.TransactionSynchronizationManager.registerSynchronization(
                new org.springframework.transaction.support.TransactionSynchronization() {
                    @Override
                    public void afterCommit() {
                        //15分钟延时消息:关闭订单、回滚锁定库存
                        rabbitMqProducer.sendOrderCloseDelayMsg(orderNo,15*60*1000);
                    }
                }
        );
        return orderNo;
    }

    private String generateOrderNo(){
        //雪花算法,这里简易演示
        return System.currentTimeMillis()+"";
    }
}

5. MQ 消费者:延时消息,关闭订单,解锁库存

复制代码
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

@Component
public class OrderCloseConsumer {

    @Resource
    private OrderService orderService;

    @RabbitListener(queues = "order.delay.close.queue")
    public void consume(String orderNo, Message message, Channel channel) throws Exception{
        try {
            orderService.closeExpireOrder(orderNo);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        }catch (Exception e){
            //异常,nack重回队列/死信
            channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
        }
    }
}

// OrderService 关闭过期订单逻辑
@Service
public class OrderService {
    @Resource
    private OrderMapper orderMapper;
    @Resource
    private OrderItemMapper orderItemMapper;
    @Resource
    private SkuStockMapper skuStockMapper;

    @Transactional(rollbackFor = Exception.class)
    public void closeExpireOrder(String orderNo){
        //查询订单状态,只有待支付才执行关闭
        Order order = orderMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNo, orderNo));
        if(!order.getStatus().equals(OrderStatusEnum.WAIT_PAY.getCode())){
            return;
        }
        //修改订单状态为已取消
        order.setStatus(OrderStatusEnum.CANCEL.getCode());
        orderMapper.updateById(order);

        //查询订单明细,循环解锁库存
        List<OrderItem> itemList = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderNo, orderNo));
        for(OrderItem item : itemList){
            skuStockMapper.unlockStock(item.getSkuId(),item.getNum());
        }
    }
}

6. 支付回调成功:真实扣减锁定库存

复制代码
@Service
public class PayCallbackService {

    @Resource
    private OrderMapper orderMapper;
    @Resource
    private OrderItemMapper orderItemMapper;
    @Resource
    private SkuStockMapper skuStockMapper;

    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(String orderNo){
        Order order = orderMapper.selectOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNo, orderNo));
        if(!OrderStatusEnum.WAIT_PAY.getCode().equals(order.getStatus())){
            return;
        }
        //更新订单状态为已支付
        order.setStatus(OrderStatusEnum.PAID.getCode());
        orderMapper.updateById(order);

        //真实扣减锁定库存
        List<OrderItem> items = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderNo, orderNo));
        for(OrderItem item : items){
            skuStockMapper.realDeductLockedStock(item.getSkuId(),item.getNum());
        }
    }
}

7. Controller 接口层

复制代码
@RestController
@RequestMapping("/order")
public class OrderController {

    @Resource
    private RedisLockUtil redisLockUtil;
    @Resource
    private OrderCreateService orderCreateService;

    /**
     * 获取下单防重token,结算页调用
     */
    @GetMapping("/token")
    public String getOrderToken(@RequestParam Long userId){
        return redisLockUtil.generateOrderToken(userId);
    }

    /**
     * 提交下单
     */
    @PostMapping("/create")
    public String createOrder(@RequestParam Long userId,
                              @RequestParam String token,
                              @RequestBody List<OrderItemDTO> itemList){
        return orderCreateService.createOrder(userId,token,itemList);
    }
}

关键坑点说明(生产必须注意)

  1. 延时消息不要放在事务内发送 ,必须用TransactionSynchronization在事务提交后发 MQ,防止事务回滚消息已经发出。
  2. 分布式锁不能用try‑finally直接 unlock;必须 lua 脚本判断持有者,避免释放别人锁。
  3. 乐观锁lockStock返回行数判断;返回 0 代表:库存不足 /version 冲突并发抢占,直接抛异常。
  4. Redis 库存缓存:热点商品把库存放入 Redis 做前置拦截,减少 DB 压力;Redis 库存仅做拦截,扣减以 mysql 为准,Redis 库存做异步同步,不要直接拿 Redis 做库存扣减源。
  5. MQ 一定要配置死信队列,消息丢失、消费失败走死信,定时任务扫描异常订单做补偿兜底。
  6. 价格全部服务端计算,禁止前端传 price;结算时实时查商品最新价格。
相关推荐
凤山老林1 小时前
高可用分布式任务调度架构:Spring Boot 集成 PowerJob 实战指南
spring boot·分布式·架构
Cache技术分享1 小时前
517. Java 方法句柄 - 方法句柄 vs 反射 API
前端·后端
哪 吒1 小时前
华为OD机试 - 云服务安全策略最优选择 - 深度优先搜索DFS(Java 新系统 200分)
java·华为od·深度优先
拒绝内耗。1 小时前
程序员学架构(一):一张图看懂 Java 后端架构:一个请求怎样从手机到数据库?
java·智能手机·架构
棣廷1 小时前
初识Python面向对象
java·开发语言
用户3126874877201 小时前
ThreadLocal 到底会不会内存泄漏?源码级拆解 + 线程池陷阱
java
珍珠先生1 小时前
P11 · MySQL 驱动版本过旧:Client does not support authentication protocol
java
~木雨2 小时前
Java 并发编程架构全景:并发层的五域设计 —— 线程模型、线程池隔离、并发安全到问题治理(全体系汇总)
java·安全·架构·线程池·并发编程·高并发架构
极小狐2 小时前
从复制粘贴到可复用组件:极狐GitLab CI/CD 组件目录实战
java·ci/cd·gitlab·devops·组件化