異步任務
在Java應用中,絕大多數情況下都是通過同步的方式來實作互動處理的;但是在處理與第三方系統互動的時候,容易造成響應遲緩的情況,之前大部分都是使用多線程來完成此類任務,其實,在Spring 3.x之後,就已經内置了
@Async
來完美解決這個問題。
注解使用
在SpringBoot裡,需要給啟動類添加
@EnableAsync
來開啟異步注解。
使用
@Async
異步注解:
@Service
public class AsyncService {
@Async // 該注解表明這是一個異步任務
public void hello(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("處理資料中...");
}
}
定時任務
一、@Scheduled
項目開發中經常需要執行一些
定時任務
,比如需要在每天淩晨時候,分析一次前一天的日志資訊,統計前一天的商品銷量等。Spring為我們提供了異步執行任務排程的方式,提供
TaskExecutor
、
TaskScheduler
接口。
要使用同步任務,首先需要了解SpringTask以及Cron表達式
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2csEzZE1UMnR0T3FleYhnRzwEMW1mY1RzRapnTtxkb5ckYplTeMZTTINGMShUYfRHelRHLwEzX39GZhh2css2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xyayFWbyVGdhd3LcV2Zh1Wa9M3clN2byBXLzN3btg3Pn5GcuETNzAjMykDMzIzMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
注解使用
在SpringBoot裡,需要給啟動類添加
@EnableScheduling
來開啟定時任務注解。
使用
@Scheduled
定時任務注解:
@Service
public class ScheduleService {
/**
* Cron表達式
*/
@Scheduled(cron = "0/4 * * * * MON-SAT") // 每4秒執行一次
public void hello(){
System.out.println("hello...");
}
/**
* initialDelay:首次執行任務的延遲
* fixedRate:目前任務開始執行2000ms之後開啟另一個定時任務
*/
@Scheduled(initialDelay = 1000,fixedRate = 2000)
public void world(){
System.out.println("world...");
}
/**
* fixedDelay:目前任務執行結束1000ms之後開啟另一個任務
*/
@Sheduled(fixedDelay = 1000)
public void test(){
System.out.println("text...");
}
}
定時任務+異步任務
實作定時任務不阻塞。預設定時任務是阻塞的。
設定線程池大小
# 線程池 核心大小-5,最大大小-50
spring.task.execution.pool.core-size=5
spring.task.execution.pool.max-size=50
代碼示例
@EnableAsync
@EnableScheduling
@Component
@Slf4j
public class HelloSchedule {
/**
* 1、Spring中6位組成,不允許7位的年
* 2、在周幾的位置,1-7表示周一到周日: MON-SUN
* 3、定時任務不應該阻塞:預設是阻塞的
* 解決方案:
* 1、可以讓業務運作以異步的方式,自己送出到線程池
* CompletableFuture.runAsync(() -> {
* xxxService.hello();
* },executor);
* 2、支援定時任務線程池:設定 TaskSchedulingProperties,不推薦,有時候不好使
* spring.task.scheduling.pool.size=5
* 3、讓定時任務異步執行
* 異步任務:
* 1、給類加上@EnableAsync注解
* 2、給方法加上@Async注解
* 解決:異步+定時任務來完成定時任務不阻塞的功能
*/
@Scheduled(cron = "* * * ? * 5")
@Async
public void hello() throws InterruptedException {
log.info("hello");
Thread.sleep(3000);
}
}
二、Quartz
Quartz 是一個功能豐富的開源作業排程庫,它由 Java 寫成,可以內建在任何 Java 應用程式中,
包括 Java SE 和 Java EE 等。使用 Quartz 可以建立簡單或者複雜的執行計劃,它支援資料庫、叢集、插件以及郵件,并且支援 cron 表達式,具有極高的靈活性。
1、引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2、建立兩個任務
①普通JavaBean
@Component
public class MyFirstJob {
public void firstJob(){
System.out.println("MyFirstJob:firstJob-" + new Date());
}
}
②繼承QuartzJobBean
public class MySecondJob extends QuartzJobBean{
private String name;
public void setName(String name){
this.name = name;
}
/**
* 任務被調用時使用
*/
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext){
System.out.println("SecondJob:" + name + "-" + new Date());
}
}
3、配置任務
@Configuration
public class QuartzConfig {
/**
* JobDetail配置方式一
* 該方式隻需要指定:執行個體名、方法名
* 缺點:無法傳遞參數
*/
@Bean
public MethodInvokingJobDetailFactoryBean jobDetail1(){
MethodInvokingJobDetailFactoryBean bean = new MethodInvokingJobDetailFactoryBean();
bean.setTargetBeanName("myFirstJob");
bean.setTargetMethod("firstJob");
return bean;
}
/**
* JobDetail配置方式二
* 該方式隻需要指定:JobClass - 即繼承QuartzJobBean的類
* 優點:可以通過jobDataMap傳遞參數,key與Job中的屬性名一緻即可,且Job中的屬性要提供set方法
*/
@Bean
public JobDetailFactoryBean jobDetail2(){
JobDetailFactoryBean bean = new JobDetailFactoryBean();
bean.setJobClass(MySecondJob.class);
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("name","LCY");
bean.setJobDataMap(jobDataMap);
bean.setDurability(true);
return bean;
}
/**
* Trigger常見實作方式一
*/
@Bean
public SimpleTriggerFactoryBean simpleTrigger(){
SimpleTriggerFactoryBean bean = new SimpleTriggerFactoryBean();
bean.setJobDetail(jobDetail1().getObject());
// 任務循環次數
bean.setRepeatCount(3);
// 任務啟動延遲時間
bean.setStartDelay(1000);
// 任務時間間隔
bean.setRepeatInterval(2000);
return bean;
}
/**
* Trigger常見實作方式二
*/
@Bean
public CronTriggerFactoryBean cronTrigger(){
CronTriggerFactoryBean bean = new CronTriggerFactoryBean();
bean.setJobDetail(jobDetail2().getObject());
// Corn表達式 - 每秒執行一次
bean.setCronExpression("* * * * * ?");
return bean;
}
/**
* 建立SchedulerFactory
*/
@Bean
public SchedulerFactoryBean schedulerFactory(){
SchedulerFactoryBean bean = new SchedulerFactoryBean();
// 配置Trigger
SimpleTrigger simpleTrigger = simpleTrigger().getObject();
CronTrigger cronTrigger = cronTrigger().getObject();
// Trigger多參數
bean.setTriggers(simpleTrigger,cronTrigger);
return bean;
}
}
4、測試結果
郵件任務
郵件任務在項目中也比較常見,比如使用者注冊,我們可以通過郵件的方式确認注冊資訊(驗證碼),注冊成功了,發送一些有關的資訊等等。
原理
[email protected]
要給
[email protected]
發送郵件,它們不是直接互動的。而是lcy要登入自己的郵箱伺服器,登進來以後以這個賬戶為名,給jyqc發郵件。而給jyqc發郵件,也是qq郵箱伺服器發給163郵箱伺服器,163郵箱伺服器再發給jyqc的。
一、依賴引入及自動配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
自動配置
配置檔案内容
二、開啟郵箱服務
以
QQ郵箱為例
,需要開啟這些服務:
然後我們就可以點選生成授權碼,在
yml
配置檔案中使用。
三、配置yml
主要需要配置郵箱的
賬号
、
密碼(授權碼)
、
郵箱主機位址
spring:
mail:
username: [email protected] # 郵箱
password: vzydizxcystfkwefi # 授權碼
host: smtp.qq.com # QQ郵箱主機位址
default-encoding: utf-8 # 編碼,預設UTF-8
properties:
mail:
smtp:
ssl:
enable: true # 安全的ssl連結
四、簡單郵件發送
注入
JavaMailSenderImpl
。
@Autowired
private JavaMailSenderImpl mailSender;
@Test
public void test(){
// 簡單郵件資訊對象
SimpleMailMessage message = new SimpleMailMessage();
// 郵件标題
message.setSubject("尋寶遊戲注冊驗證碼:");
// 郵件内容
message.setText("驗證碼:123456");
// 郵件接收方
message.setTo("[email protected]");
// 郵件發送方
message.setFrom("[email protected]");
// 發送
mailSender.send(message);
}
五、複雜郵件發送
@Test
public void test1(){
try {
// 複雜的消息郵件對象
MimeMessage mimeMessage = mailSender.createMimeMessage();
// 參數2:是否上傳檔案
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
// 郵件标題
helper.setSubject("尋寶遊戲注冊");
// 設定内容 - 參數2:是否使用html格式
helper.setText("<em style='color:red'>注冊成功!請閱讀附件!</em>",true);
// 郵件接收方
helper.setTo("[email protected]");
// 郵件發送方
helper.setFrom("[email protected]");
//上傳檔案
helper.addAttachment("1.jpg",new File("D:\\1.jpg"));
helper.addAttachment("2.jpg",new File("D:\\2.jpg"));
// 發送
mailSender.send(mimeMessage);
} catch (MessagingException e) {
e.printStackTrace();
}
}