Spring Boot + Redis 操作多种实现

点击上方“Java基基”,选择“设为星标”

做积极的人,而不是积极废人!

每天 14:00 更新文章,每天掉亿点点头发...

源码精品专栏

 
  • 原创 | Java 2021 超神之路,很肝~

  • 中文详细注释的开源项目

  • RPC 框架 Dubbo 源码解析

  • 网络应用框架 Netty 源码解析

  • 消息中间件 RocketMQ 源码解析

  • 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析

  • 作业调度中间件 Elastic-Job 源码解析

  • 分布式事务中间件 TCC-Transaction 源码解析

  • Eureka 和 Hystrix 源码解析

  • Java 并发源码

来源:blog.csdn.net/qq_42105629/
article/details/102589319/

  • 前言

  • 第一步、配置@Async

    • 一、springBoot启动类的配置:

    • 二、Spring XML的配置方式:

  • 第二步:创建两个异步方法的类,如下所示:

  • 使用Java代码结合@Configuration注解的配置方式(推荐使用)

    • 1. 新建一个配置类

    • 2.创建两个异步方法的类(和之前的类类似仅仅是方法上注解不一样),如下所示:

  • 注意事项

Spring Boot + Redis 操作多种实现_第1张图片


前言

最近在做订单模块,用户购买服务类产品之后,需要进行预约,预约成功之后分别给商家和用户发送提醒短信。考虑发短信耗时的情况所以我想用异步的方法去执行,于是就在网上看见了Spring的@Async了。

但是遇到了许多问题,使得@Async无效,也一直没有找到很好的文章去详细的说明@Async的正确及错误的使用方法及需要注意的地方,这里简单整理了一下遇见的问题,Sring是以配置文件的形式来开启@Async,而SpringBoot则是以注解的方式开启。

我们可以使用springBoot默认的线程池,不过一般我们会自定义线程池(因为比较灵活),配置方式有:

  1. 使用 xml 文件配置的方式

  2. 使用Java代码结合@Configuration进行配置(推荐使用)

下面分别实现两种配置方式

推荐下自己做的 Spring Boot 的实战项目:

https://github.com/YunaiV/ruoyi-vue-pro

第一步、配置@Async

一、springBoot启动类的配置:

在Spring Boot的主程序中配置@EnableAsync,如下所示:

@ServletComponentScan  
@SpringBootApplication  
@EnableAsync  
public class ClubApiApplication {  
    public static void main(String[] args) {  
        SpringApplication.run(ClubApiApplication.class, args);  
    }  
}

二、Spring XML的配置方式:

1.applicationContext.xml同目录下创建文件threadPool.xml文件:

  
  
  
      
      
  
      
      
          
          
  
          
          
  
          
          
  
          
          
  
          
          
  
          
          
  
          
          
              
              
              
              
              
          
      

2.然后在applicationContext.xml中引入threadPool.xml:

  
  
  

推荐下自己做的 Spring Cloud 的实战项目:

https://github.com/YunaiV/onemall

第二步:创建两个异步方法的类,如下所示:

第一个类(这里模拟取消订单后发短信,有两个发送短信的方法):

@Service  
public class TranTest2Service {  
  
  
    // 发送提醒短信 1  
    @Async  
    public void sendMessage1() throws InterruptedException {  
  
        System.out.println("发送短信方法---- 1   执行开始");  
        Thread.sleep(5000); // 模拟耗时  
        System.out.println("发送短信方法---- 1   执行结束");  
    }  
  
    // 发送提醒短信 2  
    @Async  
    public void sendMessage2() throws InterruptedException {  
  
        System.out.println("发送短信方法---- 2   执行开始");  
        Thread.sleep(2000); // 模拟耗时  
        System.out.println("发送短信方法---- 2   执行结束");  
    }  
}

第二个类。调用发短信的方法 (异步方法不能与被调用的异步方法在同一个类中,否则无效):

@Service  
public class OrderTaskServic {  
    @Autowired  
    private TranTest2Service tranTest2Service;  
  
    // 订单处理任务  
    public void orderTask() throws InterruptedException {  
  
        this.cancelOrder(); // 取消订单  
        tranTest2Service.sendMessage1(); // 发短信的方法   1  
        tranTest2Service.sendMessage2(); // 发短信的方法  2  
  
    }  
  
    // 取消订单  
    public void cancelOrder() throws InterruptedException {  
        System.out.println("取消订单的方法执行------开始");  
        System.out.println("取消订单的方法执行------结束 ");  
    }  
  
}

经过测试得到如下结果:

1.没有使用@Async

Spring Boot + Redis 操作多种实现_第2张图片

2.使用了@Async

Spring Boot + Redis 操作多种实现_第3张图片

可以看出,没有使用@Async方式实现的发送短信是同步执行的,意思就是说第一条发送之后再发送第二条,第二条发送成功之后再给用户提示,这样显然会影响用户体验,再看使用了@Async实现的,在执行第一个发送短信方法之后马上开启另一个线程执行第二个方法,显然这样我们的处理速度回快很多。

使用Java代码结合@Configuration注解的配置方式(推荐使用)

1. 新建一个配置类

package com.boot.common.conf;  
  
import java.util.concurrent.ThreadPoolExecutor;  
  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.scheduling.annotation.EnableAsync;  
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;  
  
/**  
 * 线程池配置  
 * @author zhh  
 *  
 */  
@Configuration  
@EnableAsync  
public class ThreadPoolTaskConfig {  
  
/**   
 *   默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,  
 *    当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;  
 *  当队列满了,就继续创建线程,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝   
 */  
  
    /** 核心线程数(默认线程数) */  
    private static final int corePoolSize = 20;  
    /** 最大线程数 */  
    private static final int maxPoolSize = 100;  
    /** 允许线程空闲时间(单位:默认为秒) */  
    private static final int keepAliveTime = 10;  
    /** 缓冲队列大小 */  
    private static final int queueCapacity = 200;  
    /** 线程池名前缀 */  
    private static final String threadNamePrefix = "Async-Service-";  
  
    @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名  
    public ThreadPoolTaskExecutor taskExecutor(){  
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
        executor.setCorePoolSize(corePoolSize);     
        executor.setMaxPoolSize(maxPoolSize);  
        executor.setQueueCapacity(queueCapacity);  
        executor.setKeepAliveSeconds(keepAliveTime);  
        executor.setThreadNamePrefix(threadNamePrefix);  
  
        // 线程池对拒绝任务的处理策略  
        // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务  
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());  
        // 初始化  
        executor.initialize();  
        return executor;  
    }  
}

2.创建两个异步方法的类(和之前的类类似仅仅是方法上注解不一样),如下所示:

第一个类(这里模拟取消订单后发短信,有两个发送短信的方法):

package com.boot.test1.service;  
  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.scheduling.annotation.Async;  
import org.springframework.stereotype.Service;  
  
@Service  
public class TranTest2Service {  
    Logger log = LoggerFactory.getLogger(TranTest2Service.class);  
  
    // 发送提醒短信 1  
        @PostConstruct // 加上该注解项目启动时就执行一次该方法  
    @Async("taskExecutor")  
    public void sendMessage1() throws InterruptedException {  
        log.info("发送短信方法---- 1   执行开始");  
        Thread.sleep(5000); // 模拟耗时  
        log.info("发送短信方法---- 1   执行结束");  
    }  
  
    // 发送提醒短信 2  
        @PostConstruct // 加上该注解项目启动时就执行一次该方法  
    @Async("taskExecutor")  
    public void sendMessage2() throws InterruptedException {  
  
        log.info("发送短信方法---- 2   执行开始");  
        Thread.sleep(2000); // 模拟耗时  
        log.info("发送短信方法---- 2   执行结束");  
    }  
}

代码中的 @Async("taskExecutor") 对应我们自定义线程池中的 @Bean("taskExecutor") ,表示使用我们自定义的线程池。

第二个类。调用发短信的方法 (异步方法不能与被调用的异步方法在同一个类中,否则无效):

@Service  
public class OrderTaskServic {  
    @Autowired  
    private TranTest2Service tranTest2Service;  
  
    // 订单处理任务  
    public void orderTask() throws InterruptedException {  
  
        this.cancelOrder(); // 取消订单  
        tranTest2Service.sendMessage1(); // 发短信的方法   1  
        tranTest2Service.sendMessage2(); // 发短信的方法  2  
  
    }  
  
    // 取消订单  
    public void cancelOrder() throws InterruptedException {  
        System.out.println("取消订单的方法执行------开始");  
        System.out.println("取消订单的方法执行------结束 ");  
    }  
  
}

运行截图:

Spring Boot + Redis 操作多种实现_第4张图片

注意看,截图中的 [nio-8090-exec-1] 是Tomcat的线程名称

[Async-Service-1]、[Async-Service-2]表示线程1和线程2 ,是我们自定义的线程池里面的线程名称,我们在配置类里面定义的线程池前缀:

private static final String threadNamePrefix = "Async-Service-"; // 线程池名前缀,说明我们自定义的线程池被使用了。

注意事项

如下方式会使@Async失效

  • 异步方法使用static修饰

  • 异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类

  • 异步方法不能与被调用的异步方法在同一个类中

  • 类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象

  • 如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解

- END -

欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

Spring Boot + Redis 操作多种实现_第5张图片

已在知识星球更新源码解析如下:

Spring Boot + Redis 操作多种实现_第6张图片

Spring Boot + Redis 操作多种实现_第7张图片

Spring Boot + Redis 操作多种实现_第8张图片

Spring Boot + Redis 操作多种实现_第9张图片

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 6W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)

你可能感兴趣的:(spring,java,spring,boot,mybatis,分布式)