springboot+redis实现订单过期(超时取消)功能的方法详解

本文主要是介绍springboot+redis实现订单过期(超时取消)功能的方法详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代...

在Spring Boot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案。以下是完整的实现方案:

一、Redis键过期回调方案(推荐)

1. 配置Redis监听器

@Configuration
public class RedisKeyExpirationConfig {
    
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(
            RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}

2. 监听键过期事件

@Component
@Slf4j
public class OrderExpirationListener {
    
    private static final String ORDER_KEY_PREFIX = "order:";
    private static final String ORDER_EXPIRE_KEY_PREFIX = "order:expire:";
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private OrderService orderService;
    
    /**
     * 监听所有键过期事件
     */
    @EventListener
    public void handleKeyExpiredEvent(KeyExpiredEvent<String> event) {
        String expiredKey = new String(event.getSource());
        
        if (expiredKey.startsWith(ORDER_EXPIRE_KEY_PREFIX)) {
            String orderId = expiredKey.substring(ORDER_EXPIRE_KEY_PREFIX.length());
            handleOrderExpired(orderId);
        }
    }
    
    private void handleOrderExpired(String orderId) {
        log.info("检测到订单过期: {}", orderId);
        
        try {
            // 异步处理,避免阻塞Redis监听线程
            CompletableFuture.runAsync(() -> {
                boolean result = orderService.cancelExpiredOrder(orderId);
                if (result) {
                    log.info("订单 {} 已成功取消", orderId);
                } else {
                    log.warn("订单 {} 取消失败或已处理", orderId);
                }
            });
        } catch (Exception e) {
            log.error("处理订单过期异常: {}", orderId, e);
        }
    }
}

3. Redis配置(开启键空间通知)

在Redis配置文件redis.conf中开启键空间通知:

# 开启键空间通知
notify-keyspace-events Ex

# 或者开启所有事件
# notify-keyspace-events AKE

Spring Boot配置:

spring:
  redis:
    host: localhost
    port: 6379
    database: 0
    # 监听键过期事件
    listen-patterns: "__keyevent@*__:expired"

二、延时队列方案(Redisson实现)

1. 添加Redisson依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.5</version>
</dependency>

2. 使用Redisson延时队列

@Service
@Slf4j
public class OrderDelayQueueService {
    
    @Autowired
    private RedissoChina编程nClient redissonClient;
    
    @Autowired
    private OrderService orderService;
    
    private static final String DELAY_QUEUE_NAME = "order:delay:queue";
    private static final String PROCESSING_SET = "order:delay:processing";
    
    /**
     * 添加订单到延时队列
     * @param orderId 订单ID
     * @param delayTime 延时时间(分钟)
     */
    public void addToDelayQueue(String orderId, long delayTime) {
        RblockingQueue<String> blockingQueue = redissonClient
            .getBlockingQueue(DELAY_QUEUE_NAME);
        RDelayedQueue<String> delayedQueue = redissonClient
            .getDelayedQueue(blockingQueue);
        
        // 添加到延时队列
        delayedQueue.offer(orderId, delayTime, TimeUnit.MINUTES);
        
        log.info("订单 {} 已添加到延时队列,将在 {} 分钟后过期", orderId, delayTime);
    }
    
    /**
     * 启动延时队列消费者
     */
    @PostConstruct
    public void startDelayQueueConsumer() {
        new Thread(this::consumeDelayQueue, "delay-queue-consumer").start();
    }
    
    private void consumeDelayQueue() {
        RBlockingQueue<String> blockingQueue = redissonClient
            .getBlockingQueue(DELAY_QUEUE_NAME);
        
        while (true) {
            try {
                // 从队列中取出过期的订单
                String orderId = blockingQueue.take();
                
                // 处理订单过期
                processExpiredOrder(orderId);
                
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("延时队列消费线程被中断", e);
                break;
            } catch (Exception e) {
                log.error("处理延时队列消息异常", e);
            }
        }
    }
    
    /**
     * 处理过期订单
     */
    private void processExpiredOrder(String orderId) {
        RSet<String> processingSet = redissonClient.getSet(PROCESSING_SET);
        
        // 使用set防止重复处理
        if (processingSet.add(orderId)) {
            try {
                boolean result = orderService.cancelExpiredOrder(orderId);
                if (result) {
                    log.info("延时队列:订单 {} 已过期取消", orderId);
                } else {
                    log.info("延时队列:订单 {} 无需处理", orderId);
                }
            } finally {
                processingSet.remove(orderId);
            }
        }
    }
}

三、ZSet有序集合方案

@Service
@Slf4j
public class OrderExpireZSetService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String ORDER_EXPIRE_ZSET = "order:expire:zset";
    private static final String ORDER_PROCESSING_SET = "ophprder:processing:set";
    
    /**
     * 添加订单到过期集合
     * @param orderId 订单ID
     * @param expireTime 过期时间戳
     */
    public void addOrderToExpireSet(String orderId, long expireTime) {
        redisTemplate.opsForZSet().add(ORDER_EXPIRE_ZSET, orderId, expireTime);
        log.info("订单 {} 已添加到过期集合,过期时间: {}", orderId, 
                 new Date(expireTime));
    }
    
    /**
     * 批量扫描过期订单
     */
    public void scanExpiredOrders() {
        long now = System.currentTimeMillis();
        
        // 获取已过期的订单
        Set<String> expiredOrders = redisTemplate.opsForZSet()
            .rangeByScore(ORDER_EXPIRE_ZSET, 0, now);
        
        if (expiredOrders != null && !expiredOrders.isEmpty()) {
            for (String orderId : expiredOrders) {
                processExpiredOrder(orderId);
            }
            
            // 移除已处理的订单
            redisTemplate.opsForZSet().removeRangeByScore(
                ORDER_EXPIRE_ZSET, 0, now);
        }
    }
    
    /**
     * 定时扫描任务
     */
    @Scheduled(fixedDelay = 30000) // 每30秒执行一次
    public void scheduledScan() {
        log.debug("开始扫描过期订单");
        scanExpiredOrders();
    }
    
    private void processExpiredOrder(String orderId) {
        // 使用setnx防止重复处理
        Boolean success = redisTemplate.opsForValue()
            .setIfAbsent(ORDER_PROCESSING_SET + ":" + orderId, "1", 5, TimeUnit.MINUTES);
        
        if (Boolean.TRUE.equals(success)) {
            try {
                // 处理订单过期逻辑
                handleOrderExpired(orderId);
            } finally {
                // 清理处理标记
                redisTemplate.delete(ORDER_PROCESSING_SET + ":" + orderId);
            }
        }
    }
    
    private void handleOrderExpired(String orderId) {
        // TODO: 实现订单过期处理逻辑
        log.info("处理过期订单: {}", orderId);
    }
}

四、完整订单服务实现

1. 订单状态枚举

public enum OrderStatus {
    
    PENDING_PAYMENT(0, "待支付"),
    PAID(1, "已支付"),
    COMPLETED(2, "已完成"),
    CANCELLED(3, "已取消"),
    EXPIRED(4, "已过期");
    
    private final int code;
    private final String description;
    
    OrderStatus(int code, String description) {
        this.code = code;
        this.description = description;
    }
    
    // getter方法省略
}

2. 订单实体

@Data
@Entity
@Table(name = "t_order")
public class Order {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(unique = true)
    private String orderNo;
    
    private Long userId;
    private BigDecimal amount;
    
    @Enumerated(EnumType.ORDINAL)
    private OrderStatus status = OrderStatus.PENDING_PAYMENT;
    
    @Column(name = "expire_time")
    private LocalDateTime expireTime;
    
    @Column(name = "create_time")
    private LocalDateTime createTime = LocalDateTime.now();
    
    @Column(name = "update_time")
    private LocalDateTime updateTime = LocalDateTime.now();
    
    /**
     * 判断订单是否已过期
     */
    public boolean isExpired() {
        return LocalDateTime.now().isAfter(expireTime) 
            && status == OrderStatus.PENDING_PAYMENT;
    }
}

3. 订单服务实现

@Service
@Slf4j
@Transactional
public class OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Autowired
    private OrderDelayQueueService delayQueueService;
    
    @Autowired
    private OrderExpireZSetService zSetService;
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    private static final String ORDER_LOCK_PREFIX = "order:lock:";
    private static final String ORDER_EXPIRE_KEY_PREFIX = "order:expire:";
    private static final int ORDER_EXPIRE_MINUTES = 30; // 30分钟未支付过期
    
    /**
     * 创建订单
     */
    public Order createOrder(Long userId, BigDecimal amount) {
        Order order = new Order();
        order.setOrderNo(generateOrderNo());
        order.setUserId(userId);
        order.setAmount(amount);
        order.setStatus(OrderStatus.PENDING_PAYMENT);
        order.setExpireTime(LocalDateTime.now().plusMinutes(ORDER_EXPIRE_MINUTES));
        
        order = orderRepository.save(order);
        
        // 设置Redis过期
        setOrderExpire(order.getOrderNo());
        
        // 添加到延时队列
        delayQueueService.addToDelayQueue(order.getOrderNo(), ORDER_EXPIRE_MINUTES);
        
        // 添加到ZSet
        zSetService.addOrderToExpireSet(
            order.getOrderNo(), 
            order.getExpireTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
        );
        
        log.info("创建订单成功: {}, 过期时间: {}", order.getOrderNo(), order.getExpireTime());
        return order;
    }
    
    /**
     * 设置Redis键过期
     */
    private void setOrderExpire(String orderNo) {
        String key = ORDER_EXPIRE_KEY_PREFIX + orderNo;
        String value = String.valueOf(System.currentTimeMillis());
        
        // 设置30分钟后过期
        redisTemplate.opsForValue().set(
            key, 
            value, 
            ORDER_EXPIRE_MINUTES, 
            TimeUnit.MINUTES
        );
        
        // 同时存储订单信息,用于过期时处理
        Map<String, String> orderInfo = new HashMap<>();
        orderInfo.put("orderNo", orderNo);
        orderInfo.put("userId", "1"); // 实际从订单获取
        orderInfo.put("amount", "100.00");
        
        redisTemplate.opsForHash().putAll(ORDER_KEY_PREFIX + orderNo, orderInfo);
    }
    
    /**
     * 支付成功处理
     */
    public boolean processPayment(String orderNo) {
        // 获取分布式锁
        String lockKey = ORDER_LOCK_PREFIX + orderNo;
        Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        
        if (Boolean.FALSE.equals(locked)) {
            throw new RuntimeException("订单处理中,请稍后");
        }
        
        try {
            Order order = orderRepository.findByOrderNo(orderNo)
                .orElseThrow(() -> new RuntimeException("订单不存在"));
            
            // 检查订单状态
            if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
                throw new RuntimeException("订单状态异常: " + order.getStatus());
            }
            
            // 检查是否过期
            if (order.isExpired()) {
                order.setStatus(OrderStatus.EXPIRED);
                orderRepository.save(order);
                throw new RuntimeException("订单已过期");
            }
            
            // 更新订单状态
            order.setStatus(OrderStatus.PAID);
            order.setUpdateTime(LocalDateTime.now());
            orderRepository.save(order);
            
            // 移除过期设置
            removeOrderExpire(orderNo);
            
            // 发布支付成功事件
            eventPublisher.publishEvent(new OrderPaidEvent(this, order));
            
            log.info("订单支付成功: {}", orderNo);
            return true;
            
        } finally {
            // 释放锁
            redisTemplate.delete(lockKey);
        }
    }
    
    /**
     * 处理过期订单
     */
    public boolean cancelExpiredOrder(String orderNo) {
        String lockKey = ORDER_LOCK_PREFIX + orderNo;
        Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        
        if (Boolean.FALSE.equals(locked)) {
            return false;
        }
        
        try {
            Order order = orderRepository.findByOrderNo(orderNo)
                .orElseThrow(() -> new RuntimeException("订单不存在"));
            
            // 双重检查:订单是否仍为待支付状态
            if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
                log.info("订单 {} 状态已变更为 {},跳过取消", orderNo, order.getStatus());
                return false;
            }
            
            // 检查是否真的过期
            if (!order.isExpired()) {
                log.info("订单 {} 未过期,跳过取消", orderNo);
                return false;
            }
            
            // 更新订单状态
            order.setStatus(OrderStatus.EXPIRED);
            order.setUpdateTime(LocalDateTime.now());
            orderRepository.save(order);
            
            // 释放库存等业务逻辑
            releaseStock(order);
            
            // 发送通知
            sendExpireNotification(order);
            
            log.info("订单 {} 已过期取消", orderNo);
            return true;
            
        } finally {
            redisTemplate.delete(lockKey);
        }
    }
    
    /**
     * 移除订单过期设置
     */
    private void removeOrderExpire(String orderNo) {
        // 删除过期key
        redisTemplajavascriptte.delete(ORDER_EXPIRE_KEY_PREFIX + orderNo);
        
        // 从延时队列移除
        // 注意:Redisson延时队列不支持直接移除,需要其他方式
        
        // 从ZSet移除
        redisTemplate.opsForZSet().remove(ORDER_EXPIRE_ZSET, orderNo);
        
        // 删除订单缓存
        redisTemplate.delete(ORDER_KEY_PREFIX + orderNo);
    }
    
    /**
     * 生成订单号
     */
    private String generateOrderNo() {
        // 时间戳 + 随机数
        return "ORD" + 
             China编程  System.currentTimeMillis() + 
               String.format("%06d", ThreadLocalRandom.current().nextInt(1000000));
    }
    
    private void releaseStock(Order order) {
        // 释放库存逻辑
        log.info("释放订单 {} 的库存", order.getOrderNo());
    }
    
    private void sendExpireNotification(Order order) {
        // 发送通知逻辑
        log.info("发送订单 {} 过期通知", order.getOrderNo());
    }
}

4. 订单支付事件

public class OrderPaidEvent extends ApplicationEvent {
    
    private final Order order;
    
    public OrderPaidEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
    
    public Order getOrder() {
        return order;
    }
}

五、多级过期策略(增强版)

@Component
@Slf4j
public class OrderExpireManager {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String ORDER_EXPIRE_ZSET = "order:expire:zset";
    private static final String ORDER_EXPIRE_DELAY_QUEUE = "order:expire:delay:queue";
    
    /**
     * 三级过期检测策略
     */
    public void startExpireMonitor() {
        // 1. Redis键过期事件(实时)
        // 2. 定时任务扫描(兜底)
        // 3. 延时队列(精确控制)
        
        new Thread(this::monitorDelayQueue, "order-expire-monitor").start();
        new Thread(this::scheduledScan, "order-expire-scanner").start();
    }
    
    /**
     * 监控延时队列
     */
    private void monitorDelayQueue() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                // 从延时队列获取订单
                String orderId = redisTemplate.opsForList()
                    .rightPop(ORDER_EXPIRE_DELAY_QUEUE, 1, TimeUnit.SECONDS);
                
                if (orderId != null) {
                    processExpiredOrder(orderId);
                }
            } catch (Exception e) {
                log.error("监控延时队列异常", e);
            }
        }
    }
    
    /**
     * 定时扫描
     */
    private void scheduledScan() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                scanExpiredOrders();
                Thread.sleep(30000); // 30秒扫描一次
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            } catch (Exception e) {
                log.error("定时扫描异常", e);
            }
        }
    }
    
    /**
     * 扫描过期订单
     */
    private void scanExpiredOrders() {
        long now = System.currentTimeMillis();
        Set<String> expiredOrders = redisTemplate.opsForZSet()
            .rangeByScore(ORDER_EXPIRE_ZSET, 0, now);
        
        if (expiredOrders != null) {
            for (String orderId : expiredOrders) {
                processExpiredOrder(orderId);
            }
            
            // 移除已处理的订单
            redisTemplate.opsForZSet().removeRangeByScore(
                ORDER_EXPIRE_ZSET, 0, now);
        }
    }
    
    /**
     * 处理过期订单
     */
    private void processExpiredOrder(String orderId) {
        // 防重处理
        String lockKey = "order:expire:process:" + orderId;
        Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 5, TimeUnit.MINUTES);
        
        if (Boolean.TRUE.equals(locked)) {
            try {
                boolean result = orderService.cancelExpiredOrder(orderId);
                if (result) {
                    log.info("成功处理过期订单: {}", orderId);
                }
            } finally {
                redisTemplate.delete(lockKey);
            }
        }
    }
}

六、配置类

@Configuration
@EnableScheduling
public class OrderExpireConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 设置key和value的序列化方式
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2jsonRedisSerializer());
        
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        
        template.afterPropertiesSet();
        return template;
    }
    
    @Bean
    public OrderExpireManager orderExpireManager() {
        return new OrderExpireManager();
    }
    
    @PostConstruct
    public void init() {
        // 启动过期监控
        orderExpireManager().startExpireMonitor();
    }
}

七、API接口

@RestController
@RequestMapping("/api/orders")
@Slf4j
public class OrderController {
    
    @Autowired
    private OrderService orderService;
    
    @PostMapping("/create")
    public ApiResponse<Order> createOrder(@RequestBody CreateOrderRequest request) {
        Order order = orderService.createOrder(
            request.getUserId(), 
            request.getAmount()
        );
        return ApiResponse.success(order);
    }
    
    @PostMapping("/{orderNo}/pay")
    public ApiResponse<Void> payOrder(@PathVariable String orderNo) {
        boolean success = orderService.processPayment(orderNo);
        if (success) {
            return ApiResponse.success("支付成功");
        } else {
            return ApiResponse.error("支付失败");
        }
    }
    
    @GetMapping("/{orderNo}/status")
    public ApiResponse<OrderStatus> getOrderStatus(@PathVariable String orderNo) {
        // 从Redis或数据库获取订单状态
        return ApiResponse.success(OrderStatus.PENDING_PAYMENT);
    }
}

八、测试类

@SpringBootTest
@Slf4j
class OrderExpireTest {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Test
    void testOrderExpire() throws InterruptedException {
        // 创建订单
        Order order = orderService.createOrder(1L, new BigDecimal("100.00"));
        
        // 验证Redis中已设置过期
        String expireKey = "order:expire:" + order.getOrderNo();
        String value = redisTemplate.opsForValue().get(expireKey);
        assertNotNull(value);
        
        // 验证订单状态
        assertEquals(OrderStatus.PENDING_PAYMENT, order.getStatus());
        
        // 等待订单过期
        Thread.sleep(2000); // 实际应该等30分钟
        
        // 测试支付
        boolean paid = orderService.processPayment(order.getOrderNo());
        assertTrue(paid);
    }
    
    @Test
    void testConcurrentPay() throws InterruptedException {
        Order order = orderService.createOrder(2L, new BigDecimal("200.00"));
        
        ExecutorService executor = Executors.newFixedThreadPool(5);
        CountDownLatch latch = new CountDownLatch(5);
        
        AtomicInteger successCount = new AtomicInteger(0);
        
        for (int i = 0; i < 5; i++) {
            executor.submit(() -> {
                try {
                    boolean result = orderService.processPayment(order.getOrderNo());
                    if (result) {
                        successCount.incrementAndGet();
                    }
                } catch (Exception e) {
                    log.error("支付异常", e);
                } finally {
                    latch.countDown();
                }
            });
        }
        
        latch.await();
        executor.shutdown();
        
        // 只有一个支付成功
        assertEquals(1, successCount.get());
    }
}

九、监控和告警

@Component
@Slf4j
public class OrderExpireMonitor {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Scheduled(fixedRate = 60000) // 每分钟执行一次
    public void monitorExpireOrders() {
        String expireKeyPattern = "order:expire:*";
        
        // 统计待过期订单数量
        Set<String> keys = redisTemplate.keys(expireKeyPattern);
        long expireCount = keys != null ? keys.size() : 0;
        
        // 统计即将在5分钟内过期的订单
        long soonExpireCount = keys.stream()
            .map(key -> redisTemplate.getExpire(key, TimeUnit.SECONDS))
            .filter(ttl -> ttl != null && ttl > 0 && ttl <= 300)
            .count();
        
        // 记录监控日志
        log.info("订单过期监控 - 待过期订单: {}, 即将过期订单: {}", 
                 expireCount, soonExpireCount);
        
        // 发送告警
        if (soonExpireCount > 100) {
            sendAlert("大量订单即将过期: " + soonExpireCount + " 个");
        }
    }
    
    private void sendAlert(String message) {
        // 发送告警到监控系统
        log.warn("订单过期告警: {}", message);
    }
}

总结建议

推荐方案

生产环境推荐组合方案

  • 主方案:Redisson延时队列​ + Redis键过期回调
  • 兜底方案:定时任务扫描ZSet
  • 防重处理:Redis分布式锁

方案对比

  • Redis键过期回调:实时性最好,但可靠性依赖Redis配置
  • Redisson延时队列:功能强大,支持分布式,推荐使用
  • ZSet定时扫描:实现简单,但实时性较差
  • 多级策略:最可靠,但实现复杂

注意事项

  • 一定要配置Redis的notify-keyspace-events Ex
  • 考虑网络分区和Redis故障的情况
  • 实现幂等性处理,防止重复取消
  • 添加监控和告警
  • 考虑持久化,防止重启后数据丢失

性能优化

  • 使用批量处理过期订单
  • 异步处理过期逻辑
  • 合理设置扫描频率
  • 使用连接池

这种实现可以确保订单过期功能的可靠性和实时性,适合电商等高并发场景。

到此这篇关于springboot+redis实现订单过期(超时取消)功能的方法详解的文章就介绍到这了,更多相关springboot订单超时取消内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于springboot+redis实现订单过期(超时取消)功能的方法详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156438

相关文章

Spring Boot 处理带文件表单的方式汇总

《SpringBoot处理带文件表单的方式汇总》本文详细介绍了六种处理文件上传的方式,包括@RequestParam、@RequestPart、@ModelAttribute、@ModelAttr... 目录方式 1:@RequestParam接收文件后端代码前端代码特点方式 2:@RequestPart接

SpringBoot整合Zuul全过程

《SpringBoot整合Zuul全过程》Zuul网关是微服务架构中的重要组件,具备统一入口、鉴权校验、动态路由等功能,它通过配置文件进行灵活的路由和过滤器设置,支持Hystrix进行容错处理,还提供... 目录Zuul网关的作用Zuul网关的应用1、网关访问方式2、网关依赖注入3、网关启动器4、网关全局变

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

基于SpringBoot实现分布式锁的三种方法

《基于SpringBoot实现分布式锁的三种方法》这篇文章主要为大家详细介绍了基于SpringBoot实现分布式锁的三种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、基于Redis原生命令实现分布式锁1. 基础版Redis分布式锁2. 可重入锁实现二、使用Redisso

SpringBoo WebFlux+MongoDB实现非阻塞API过程

《SpringBooWebFlux+MongoDB实现非阻塞API过程》本文介绍了如何使用SpringBootWebFlux和MongoDB实现非阻塞API,通过响应式编程提高系统的吞吐量和响应性能... 目录一、引言二、响应式编程基础2.1 响应式编程概念2.2 响应式编程的优势2.3 响应式编程相关技术

SpringBoot的全局异常拦截实践过程

《SpringBoot的全局异常拦截实践过程》SpringBoot中使用@ControllerAdvice和@ExceptionHandler实现全局异常拦截,@RestControllerAdvic... 目录@RestControllerAdvice@ResponseStatus(...)@Except

Springboot配置文件相关语法及读取方式详解

《Springboot配置文件相关语法及读取方式详解》本文主要介绍了SpringBoot中的两种配置文件形式,即.properties文件和.yml/.yaml文件,详细讲解了这两种文件的语法和读取方... 目录配置文件的形式语法1、key-value形式2、数组形式读取方式1、通过@value注解2、通过

Java 接口定义变量的示例代码

《Java接口定义变量的示例代码》文章介绍了Java接口中的变量和方法,接口中的变量必须是publicstaticfinal的,用于定义常量,而方法默认是publicabstract的,必须由实现类... 在 Java 中,接口是一种抽象类型,用于定义类必须实现的方法。接口可以包含常量和方法,但不能包含实例

JAVA Calendar设置上个月时,日期不存在或错误提示问题及解决

《JAVACalendar设置上个月时,日期不存在或错误提示问题及解决》在使用Java的Calendar类设置上个月的日期时,如果遇到不存在的日期(如4月31日),默认会自动调整到下个月的相应日期(... 目录Java Calendar设置上个月时,日期不存在或错误提示java进行日期计算时如果出现不存在的

C#实现将XML数据自动化地写入Excel文件

《C#实现将XML数据自动化地写入Excel文件》在现代企业级应用中,数据处理与报表生成是核心环节,本文将深入探讨如何利用C#和一款优秀的库,将XML数据自动化地写入Excel文件,有需要的小伙伴可以... 目录理解XML数据结构与Excel的对应关系引入高效工具:使用Spire.XLS for .NETC