Java中实现订单超时自动取消功能(最新推荐)

2025-02-25 17:50

本文主要是介绍Java中实现订单超时自动取消功能(最新推荐),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Java中实现订单超时自动取消功能(最新推荐)》本文介绍了Java中实现订单超时自动取消功能的几种方法,包括定时任务、JDK延迟队列、Redis过期监听、Redisson分布式延迟队列、Rocket...

        在开发中,我们会遇到需要延时任务的业务场景,例如:用户下单之后未在规定的时间内支付成功,该订单会自动取消; 用户注册成功15分钟后,发消息通知用户;还有比如到期自动收货,超时自动退款等都是类似的延时任务的业务问题。

这里主要介绍一下几种方法:

  • 1、定时任务
  • 2、JDK延迟队列 DelayQueue
  • 3、redis过期监听
  • 4、Redisson分布式延迟队列
  • 5、RocketMQ延迟消息
  • 6、RabbitMQ死信队列

1、定时任务

        写一个定时任务,定期扫描数据库中的订单,如果时间过期,就取消这个订单。这种实现方法成本低、实现容易。这里使用@Scheduled注解实现,也可以用Quartz框架实现定时任务。

@Scheduled(cron = "30 * * * * ?")
public void scanOrder(){
   orderService.scanOrder(); //每30秒扫描数据库 找出过期未支付的订单,取消该订单
}

优点:实现容易,成本低,不依赖其他组件。

缺点:

  • 时间不够精确。因为扫描是有间隔的,但却随时会产生过期的订单,所以可能会导致有些订单已经过期了一段时间后才被扫描到。
  • 增加了数据库的压力。频繁的访问数据库,当数据越来越多时,访问数据库的成本也会增加。

2、JDK延迟队列 DelayQueue

      DelayQueue是JDK提供的一个无界队列,它的本质是封装了一个PriorityQueue(优先队列), PriorityQueue内部使用完全二叉堆来实现队列排序,在向队列中插入元素时,需要给出这个元素的Delay时间,也就是过期时间,队列中最小的元素会被放在队首,队列中的元素只有到了Delay时间才允许从队列中取出。

   具体的实现思路就是:首先创建一个实体类实现Delay接口,然后将它放入DelayQueue队列中。

(1)定义实现Delayed接口的实体类

需要实现Delayed接口的两个方法:getDelay()和compareTo()

import com.fasterXML.jackson.annotation.jsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import Java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyDelay implements Delayed {
    private String orderNumber;  //订单编号
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Long time;   //过期时间
    @Override
    public long getDelay(TimeUnit timeUnit) {
        return time - System.currentTimeMillis();
    }
    @Override
    public int compareTo(Delayed delayed) {
        MyDelay myDelawww.chinasem.cny = (MyDelay)delayed;
        return this.time.compareTo(myDelay.getTime());
    }
}

 (2)将延时任务放入队列

package com.demo;
import com.demo.config.MyDelay;
import java.util.concurrent.DelayQueue;
public class demo {
    public static void main(String[] args) throws InterruptedException {
        MyDelay myDelay1 = new MyDelay("0001", 5L);
        MyDelay myDelay2 = new MyDelay("0002", 10L);
        MyDelay myDelay3 = new MyDelay("0003", 15L);
        DelayQueue<MyDelay> delayDelayQueue = new DelayQueue<MyDelay>();
        delayDelayQueue.add(myDelay1);
        delayDelayQueue.add(myDelay2);
        delayDelayQueue.add(myDelay3);
        while (delayDelayQueue.size()!=0) {
                /**
                 * 取队列头部元素是否过期
                 */
                //DelayQueue的put/add方法是线程安全的,因为put/add方法内部使用了ReentrantLock锁进行线程同步。
                // DelayQueue还提供了两种出队的方法 poll() 和 take() ,
                // poll() 为非阻塞获取,没有到期的元素直接返回null;
                // take() 阻塞方式获取,没有到期的元素线程将会等待。
                MyDelay order = delayDelayQueue.poll();
                if(order!=null) {
                    System.out.println("订单编号:"+order.getOrderNumber()+",超时取消!");
                }
                Thread.sleep(1000);
        }
    }
}

优点:不依赖任何第三方组件,实现方便。

缺点:因为DelayQueue是基于JVM的,如果放入的订单过多,会造成JVM溢出。如果JVM重启了,那所有的数据就丢失了。

3、redis过期监听

redis是一个高性能的key,value数据库,除了用作缓存之外,它还提供了过期监听的功能。

在redis.conf中配置

配置notify-keyspace-events "Ex" 即可开启此功能。

Java中实现订单超时自动取消功能(最新推荐)

springboot 项目集成redis配置过期监听

在pom中引入依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在yml中配置redis源

redis:
  #数据库索引
  database: 0
  host: 127.0.0.1
  port: 6379
  password: 123456
  jedis:
    pool:
      #最大连接数
      max-active: 15
      #最大阻塞等待时间(负数表示没限制)
      max-wait: -1
      #最大空闲
      max-idle: 15
      #最小空闲
      min-idle: 0
      #连接超时时间
  timeout: js10000

编写redis配置类

package com.example.study_demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
/**
 * Redis配置
 */
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer() {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        return redisMessageListenerContainer;
    }
    @Bean
    public KeyExpiredListener keyExpiredListener() {
        return new KeyExpiredListener(this.redisMessageListenerContainer());
    }
}

编写redis工具类

package com.example.study_demo.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.corpythone.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Component
public class RedisCache
{
    @Autowired
    public RedisTemplate redisTemplate;
    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }
    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }
    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }
    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }
    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }
    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }
    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }
    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }
    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);
    }
    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }
    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }
    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }
    /**
     * 删除Hash中的数据
     * 
     * @param key
     * @param hkeyjavascript
     */
    public void delCacheMapValue(final String key, final String hkey)
    {
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, hkey);
    }
    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public wYOctfZsNp<T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }
    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }
}

编写监控类

在代码中继承KeyspaceEventMessageListener ,实现onMessage就可以监听过期的数据量

package com.example.study_demo.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Slf4j
public class KeyExpiredListener extends KeyExpirationEventMessageListener {
    public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiredKey = message.toString();
        log.info("订单{}过期了", expiredKey);
    }
}

测试

package com.demo;
import com.demo.config.MyDelay;
import java.util.concurrent.DelayQueue;
public class demo {
    public static void main(String[] args) throws InterruptedException {
        long expire = 5L; //设置过期时间
        String key = "0001";
        RedisCache redisCache = new RedisCache();
        redisCache.setCacheObject(key,"订单过期了");
        redisCache.expire(key,expire);
    }
}

优点:由于redis的高性能,所以在设置以及消费key时的速度可以保证。

缺点: 由于redis的key过期策略的原因,当一个key过期时,无法立刻保证将其删除,自然我们监听事件也无法第一时间消费到这个key,所以会存在一定的延迟。 此外,在redis5.0之前,订阅发布消息并没有被持久化,自然也没有所谓的确认机制,所以一旦消费信息过程中我们的客户端发生了宕机,这条消息就彻底丢失了。

4、Redisson分布式延迟队列

       Redisson是一个基于redis实现的Java驻内存数据网络,它不仅提供了一系列的分布式Java常用对象,还提供了许多分布式服务。Redisson除了提供我们常用的分布式锁外,还提供了一个分布式延迟队列RDelayedQueue ,它是一种基于zset结构实现的延迟队列,其实现类是RedissonDelayedQueue,在springboot中整合使用Redisson分布式延迟队列的步骤如下:

引入pom依赖,yml中配置redis连接

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

创建延时队列生产者

import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * 延迟队列生产者
 */
@Service
public class RDelayQueueProducer {
    @Autowired
    private RedissonClient redissonClient;
    public void addTask(String taskId, long delayTime){
        //创建一个延迟队列
        RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(redissonClient.getQueue("my_delayQueue"));
        //将任务添加到延迟队列,指定延迟时间
        delayedQueue.offer(taskId,delayTime,java.util.concurrent.TimeUnit.SECONDS);
    }
}

创建延时队列消费者

import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * 延迟队列消费者
 */
@Service
public class RDelayQueueConsumer {
    @Autowired
    private RedissonClient redissonClient;
    public void consumeTask(){
        RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(redissonClient.getQueue("my_delayQueue"));
        while (true){
            String poll = delayedQueue.poll();
            if(poll!=null){
                //收到消息进行处理
                System.out.println("收到消息:"+poll);
            }
        }
    }
}

测试

@PostMapping("/test")
    public void test(){
        rDelayQueueProducer.addTask("0001",5);
        rDelayQueueProducer.addTask("0002",10);
        rDelayQueueProducer.addTask("0003",15);
    }

优点:使用简单,并且其实现类中大量使用Lua脚本保证其原子性,不会有并发重复问题。

缺点:需要依赖redis

5、RocketMQ延迟消息

       RocketMQ是阿里巴巴开源的一款分布式消息中间件,基于高可用分布式集群技术,提供低延迟的、可靠的消息发布与订阅服务。下面是在springboot中集成RocketMQ延迟消息的步骤:

安装并启动 RocketMQ 服务

       可参考RocketMQ 官方文档进行安装和启动

引入依赖

 <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
        <version>2.2.2</version>
  </dependency>

配置RocketMQ

spring:
  rocketmq:
    name-server: 127.0.0.1:9876 # RocketMQ NameServer地址
    producer:
      group: my-group # 生产者组名

创建消息生产者

import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RocketMQProducerService {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    public void sendMessage(String topic, String message,long delay) {
        // 发送延迟消息,延迟级别为16,对应延迟时间为delay
        rocketMQTemplate.syncSend(topic, message, delay, 16);
    }
}

创建消息消费者

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;
@Service
@RocketMQMessageListener(topic = "test-topic", consumerGroup = "my-consumer-group")
public class RocketMQConsumerService implements RocketMQListener<String> {
    @Override
    public void onMessage(String message) {
        System.out.println("接收到消息: " + message);
         //检查订单是否支付
    }
}

测试

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RocketMQTestController {
    @Autowired
    private RocketMQProducerService producerService;
    @GetMapping("/sendMessage")
    public String sendMessage() {
        String topic = "test-topic";
        String message = "0001"; //发送订单编号到rocketMQ
        long delay = 3000;
        producerService.sendMessage(topic, message, delay);
        return "消息发送成功";
    }
}

优点:系统之间完全解耦,只需要关注生产及消费即可。其吞吐量极高。

缺点:RocketMQ是重量级的组件,引入后,随之而来的消息丢失等问题都增加了系统的复杂度。

6、RabbitMQ死信队列

       当RabbitMQ中的一条正常信息,因为过了存活时间(ttl过期)、队列长度超限等原因无法被消费时,就会被当成一条死信消息,投递到死信队列。基于这样的机制,我们可以给消息设置一个ttl ,等消息过期就会进入死信队列,我们再消费死信队列即可,这样,就可以达到和RocketMQ一样的效果。springboot集成rabbitMQ的步骤如下:

安装并启动 RabbitMQ 服务

       可参考RabbitMQ官方文档进行安装和启动

引入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>

配置RabbitMQ

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

配置 RabbitMQ 队列和交换机

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class RabbitMQConfig {
    public static final String ORDER_EXCHANGE = "order.exchange";
    public static final String ORDER_QUEUE = "order.queue";
    public static final String ORDER_ROUTING_KEY = "order.routing.key";
    public static final String DEAD_LETTER_EXCHANGE = "dead.letter.exchange";
    public static final String DEAD_LETTER_QUEUE = "dead.letter.queue";
    public static final String DEAD_LETTER_ROUTING_KEY = "dead.letter.routing.key";
    // 死信交换机
    @Bean
    public DirectExchange deadLetterExchange() {
        return new DirectExchange(DEAD_LETTER_EXCHANGE);
    }
    // 死信队列
    @Bean
    public Queue deadLetterQueue() {
        return new Queue(DEAD_LETTER_QUEUE);
    }
    // 绑定死信队列和死信交换机
    @Bean
    public Binding deadLetterBinding() {
        return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()).with(DEAD_LETTER_ROUTING_KEY);
    }
    // 正常交换机
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(ORDER_EXCHANGE);
    }
    // 正常队列,设置死信交换机和路由键,以及消息TTL为30分钟(1800000毫秒)
    @Bean
    public Queue orderQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
        args.put("x-dead-letter-routing-key", DEAD_LETTER_ROUTING_KEY);
        args.put("x-message-ttl", 1800000);
        return new Queue(ORDER_QUEUE, true, false, false, args);
    }
    // 绑定正常队列和正常交换机
    @Bean
    public Binding orderBinding() {
        return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(ORDER_ROUTING_KEY);
    }
}

创建消息生产者

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderMessageProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    public void sendOrderMessage(String message) {
        rabbitTemplate.convertAndSend(RabbitMQConfig.ORDER_EXCHANGE, RabbitMQConfig.ORDER_ROUTING_KEY, message);
    }
}

创建消息消费者

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
@Service
public class OrderMessageConsumer {
    @RabbitListener(queues = RabbitMQConfig.DEAD_LETTER_QUEUE)
    public void receiveOrderMessage(String message) {
        System.out.println("收到订单: " + message);
        // 模拟检查订单支付状态
    }
}

测试

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderMessageController {
    @Autowired
    private OrderMessageProducer orderMessageProducer;
    @GetMapping("/sendOrderMessage")
    public String sendOrderMessage() {
        String message = "0001"; //订单编号
        orderMessageProducer.sendOrderMessage(message);
        return "订单消息已发送,30分钟后处理";
    }
}

优点:同RocketMQ一样可以使业务解耦。

缺点:RabbitMQ 的 TTL 是基于队列的,而不是基于单个消息的精确时间控制。当队列中有多个消息时,即使某个消息的 TTL 已经过期,也需要等待前面的消息被处理完才能进入死信队列,导致消息的实际处理时间可能会有一定的延迟,无法保证精确的延迟时间。

到此这篇关于Java中如何实现订单超时自动取消功能的文章就介绍到这了,更多相关Java订单超时自动取消内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于Java中实现订单超时自动取消功能(最新推荐)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Nginx实现动态封禁IP的步骤指南

《Nginx实现动态封禁IP的步骤指南》在日常的生产环境中,网站可能会遭遇恶意请求、DDoS攻击或其他有害的访问行为,为了应对这些情况,动态封禁IP是一项十分重要的安全策略,本篇博客将介绍如何通过NG... 目录1、简述2、实现方式3、使用 fail2ban 动态封禁3.1 安装 fail2ban3.2 配

Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)

《Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)》本文介绍了如何使用Python和Selenium结合ddddocr库实现图片验证码的识别和点击功能,感兴趣的朋友一起看... 目录1.获取图片2.目标识别3.背景坐标识别3.1 ddddocr3.2 打码平台4.坐标点击5.图

Java 8 Stream filter流式过滤器详解

《Java8Streamfilter流式过滤器详解》本文介绍了Java8的StreamAPI中的filter方法,展示了如何使用lambda表达式根据条件过滤流式数据,通过实际代码示例,展示了f... 目录引言 一.Java 8 Stream 的过滤器(filter)二.Java 8 的 filter、fi

springboot的调度服务与异步服务使用详解

《springboot的调度服务与异步服务使用详解》本文主要介绍了Java的ScheduledExecutorService接口和SpringBoot中如何使用调度线程池,包括核心参数、创建方式、自定... 目录1.调度服务1.1.JDK之ScheduledExecutorService1.2.spring

将java程序打包成可执行文件的实现方式

《将java程序打包成可执行文件的实现方式》本文介绍了将Java程序打包成可执行文件的三种方法:手动打包(将编译后的代码及JRE运行环境一起打包),使用第三方打包工具(如Launch4j)和JDK自带... 目录1.问题提出2.如何将Java程序打包成可执行文件2.1将编译后的代码及jre运行环境一起打包2

Java使用Tesseract-OCR实战教程

《Java使用Tesseract-OCR实战教程》本文介绍了如何在Java中使用Tesseract-OCR进行文本提取,包括Tesseract-OCR的安装、中文训练库的配置、依赖库的引入以及具体的代... 目录Java使用Tesseract-OCRTesseract-OCR安装配置中文训练库引入依赖代码实

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

SpringBoot整合easy-es的详细过程

《SpringBoot整合easy-es的详细过程》本文介绍了EasyES,一个基于Elasticsearch的ORM框架,旨在简化开发流程并提高效率,EasyES支持SpringBoot框架,并提供... 目录一、easy-es简介二、实现基于Spring Boot框架的应用程序代码1.添加相关依赖2.添

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程

《SpringBoot中整合RabbitMQ(测试+部署上线最新完整)的过程》本文详细介绍了如何在虚拟机和宝塔面板中安装RabbitMQ,并使用Java代码实现消息的发送和接收,通过异步通讯,可以优化... 目录一、RabbitMQ安装二、启动RabbitMQ三、javascript编写Java代码1、引入