SpringBootは@Scheduledアノテーションを使用してタイミングタスクを実装します

序文

Spring 3.0以降のバージョンには時限タスクが付属しており、時限タスク機能を実現するために@EnableSchedulingアノテーションと@Scheduledアノテーションが提供されています。

SpringBootを使用して時限タスクを作成するのは非常に簡単です。現在、タスクを作成する主な方法は3つあります。

  • 1.アノテーションに基づく(@Scheduled)
  • 2.インターフェース(SchedulingConfigurer)に基づく前者は誰もが知っていると思われますが、実際の使用では、データベースから指定した時刻を読み取って動的にタイミングタスクを実行したいことがよくあります。現時点では、インターフェースベースのタイミングタスクです。便利です。
  • 3.注釈に基づいてマルチスレッドタイミングタスクを設定します

1.アノテーションに基づく(@Scheduled)

1.1@Scheduledアノテーションと@EnableSchedulingアノテーションの使用

アノテーションに基づいて、@ Scheduledはデフォルトでシングルスレッドです。複数のタスクが開始されると、タスクの実行時間は前のタスクの実行時間の影響を受けます。

@EnableSchedulingアノテーション:構成クラスで使用して、(クラスで)スケジュールされたタスクのサポートを有効にします。

@Scheduledアノテーション:これがcron、fixDelay、fixRate、その他のタイプを含むタスクであることを宣言します(メソッドに関しては、最初にスケジュールされたタスクのサポートをオンにする必要があります)。

[例] @Scheduledアノテーションと@EnableSchedulingアノテーションは、タイミングタスクを実装するためにSpringBootプロジェクトで使用されます。

(1)タイミングタスクを開始します

SpringBootプロジェクトで、@ EnableSchedulingアノテーションをプロジェクトのスタートアップクラスに追加して、タイミングタスク管理を有効にします。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling //开启定时任务
public class ScheduledDemoApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(ScheduledDemoApplication.class, args);
    }
}

(2)時限タスクを作成する

スケジュールされたタスクを作成し、@ Scheduledアノテーションを使用します。

package com.pjb.Schedule;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 定时任务的使用
 * @author pan_junbiao
 **/
@Component
public class Task
{
    @Scheduled(cron="0/5 * *  * * ? ")   //每5秒执行一次
    public void execute(){
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //设置日期格式
        System.out.println("欢迎访问 pan_junbiao的博客 " + df.format(new Date()));
    }
}

結果:

1.2@Scheduledアノテーションの各パラメータの説明

@Scheduledアノテーションの使用についてはここでは詳しく説明せず、8つのパラメーターについて直接説明します。

@Scheduledアノテーションクラスのソースコードは次のとおりです。

package org.springframework.scheduling.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
    String CRON_DISABLED = "-";

    String cron() default "";

    String zone() default "";

    long fixedDelay() default -1L;

    String fixedDelayString() default "";

    long fixedRate() default -1L;

    String fixedRateString() default "";

    long initialDelay() default -1L;

    String initialDelayString() default "";
}

1.2.1cron

このパラメーターはcron式を受け入れます。cron式は文字列です。文字列は5つまたは6つのスペースで区切られ、合計6つまたは7つのフィールドで区切られ、各フィールドは意味を表します。

cron式の構文:

形式:[秒] [分] [時間] [日] [月] [週] [年]

シリアルナンバー 説明 必要ですか 許容値 許可されたワイルドカード
1 2番目 はい 0-59 、-* /
2 はい 0-59 、-* /
3 時間 はい 0-23 、-* /
4 はい 1-31 、-*?/ LW
5 はい 1〜12日または1月〜12月 、-* /
6 週間 はい 1-7またはSUN-SAT 、-*?/ L#
7 番号 空または1970-2099 、-* /

ワイルドカードの説明:

すべての値を意味します。例:分フィールドに「*」を設定すると、毎分トリガーされます。

 値が指定されていないことを意味します。使用されるシナリオは、現在設定されているこのフィールドの値を気にする必要がないというものです。例:毎月10日に操作をトリガーしたいが、曜日は気にしないので、週の位置のフィールドを「?」に設定する必要があります。具体的には、00に設定します。 0 10 *?

- 間隔を表します。たとえば、時間に「10-12」を設定すると、10、11、および12時にトリガーされます。

 複数の値を指定することを意味します。たとえば、週フィールドに「MON、WED、FRI」を設定すると、月曜日、水曜日、金曜日にトリガーされます。

/ インクリメンタルトリガーに使用されます。たとえば、秒に「5/15」を設定します。これは、5秒から開始し、15秒ごとにトリガーすることを意味します(5、20、35、50)。月フィールドで「1/3」を設定して、毎月1日に開始し、3日に1回トリガーします。

L は最後の意味を意味します。日フィールドの設定では、月の最終日を意味し(現在の月によると、2月の場合は、旧正月[leap]かどうかによっても異なります)、週フィールドでは土曜日を意味します。 、これは「7」または「SAT」に相当します。「L」の前に数字を追加すると、データの最後の数字を意味します。たとえば、週フィールドに「6L」の形式を設定すると、「その月の最後の金曜日」を意味します。

W は、指定した日付に最も近い営業日(月曜日から金曜日)を表します。たとえば、[日]フィールドに「15W」を設定すると、毎月15日に最も近い営業日にトリガーがトリガーされます。15日が土曜日の場合は、トリガーする最も近い金曜日(14日)を見つけ、15日が平日である場合は、トリガーする最も近い次の月曜日(16日)を見つけます。15日がたまたま営業日(月曜日から週)5)、それはその日にトリガーされます。指定された形式が「1W」の場合、毎月1日以降の最も近い営業日にトリガーされることを意味します。1日が土曜日に当たる場合、次の月曜日の3日にトリガーされます。(「W」の前に設定できるのは特定の数字のみであり、間隔「-」は許可されないことに注意してください)。

ヒント:「L」と「W」は組み合わせて使用​​できます。日フィールドに「LW」が設定されている場合は、その月の最終営業日にトリガーされることを意味します(通常は給与計算を指します)。

 シリアル番号(月の最初の数週間を表す)。たとえば、週フィールドに「6#3」を設定すると、毎月第3土曜日になります。「#5」を指定すると、第5週6の週ではない場合、この構成はトリガーされません(母の日と父の日に適しています)

ヒント:英語の文字が使用されている場合、週フィールドの設定では大文字と小文字が区別されません。MONはmonと同じです。

cron式を生成するためのオンラインツールhttp://cron.qqe2.com/ を使用して、必要な式を生成できます。

一般的な例:

0 0 12 * *? 毎日12時にトリガーします
0 15 10?* * 毎日10:15にトリガーします
0 15 10 * *? 毎日10:15にトリガーします
0 15 10 * *?* 毎日10:15にトリガーします
0 15 10 * *?2005年 2005年は毎日10:15に発砲
0 * 14 * *? 毎日午後2時から午後2時59分まで毎分トリガーします
0 0/5 14 * *? 毎日午後2時から午後2時59分まで(正時に開始し、5分ごとにトリガーします)
0 0/5 14,18 * *? 毎日午後2時から午後2時59分(正時に開始して5分ごとにトリガー)毎日18時から午後18時59分まで(正時に開始して5分ごとにトリガー)
0 0-5 14 * *? 毎日午後2時から午後2時5分まで毎分トリガーされます
0 10,44 14?3水 3月の午後2:10と2:44に毎週水曜日にトリガーされます
0 15 10?*月〜金 月曜日から金曜日まで毎日午前10時15分に発砲
0 15 10 15 *? 毎月15日の午前10時15分に発砲
0 15 10 L *? 毎月最終日の10:15に発砲
0 15 10?* 6L 毎月最終金曜日の10:15に発砲
0 15 10?* 6L 2002-2005 2002年から2005年までの毎月最終金曜日の10:15にトリガーされます
0 15 10?* 6#3 毎月第3週の金曜日にトリガーします
0 0 12 1/5 *? 毎月第1正午から5日ごとにトリガーされます
0 11 11 11 11? 毎年11月11日11時11分に発砲(光棍節)

cron式はプレースホルダーを使用します

さらに、cron属性が受け取るcron式は、プレースホルダーをサポートします。例えば:

構成ファイル:

time:
  cron: */5 * * * * *
  interval: 5

5秒ごとに実行します。

@Scheduled(cron="${time.cron}")
void testPlaceholder1() {
    System.out.println("Execute at " + System.currentTimeMillis());
}

@Scheduled(cron="*/${time.interval} * * * * *")
void testPlaceholder2() {
    System.out.println("Execute at " + System.currentTimeMillis());
}

1.2.2ゾーン

タイムゾーン、java.util.TimeZone#IDを受け取ります。cron式は、タイムゾーンに基づいて解析されます。デフォルトは空の文字列、つまりサーバーが配置されているタイムゾーンです。たとえば、私たちは通常、アジア/上海のタイムゾーンを使用します。通常、このフィールドは空白のままにします。

1.2.3fixedDelay

最後の実行時点から実行にかかる時間。といった:

@Scheduled(fixedDelay = 5000) //上一次执行完毕时间点之后5秒再执行

1.2.4fixedDelayString

1.2.3 fixedDelayと同じ意味ですが、文字列の形式を使用します。唯一の違いは、プレースホルダーがサポートされていることです。といった:

@Scheduled(fixedDelayString = "5000") //上一次执行完毕时间点之后5秒再执行

プレースホルダーの使用:

application.yml構成ファイルに次の構成を追加します。

time:
  fixedDelay: 5000

関連するコードを書く: 

/**
 * 定时任务的使用
 * @author pan_junbiao
 **/
@Component
public class Task
{
    @Scheduled(fixedDelayString = "${time.fixedDelay}")
    void testFixedDelayString()
    {
        System.out.println("欢迎访问 pan_junbiao的博客 " + System.currentTimeMillis());
    }
}

結果:

1.2.5fixedRate

最後に実行が開始されてからどれくらい経ちますか。といった:

@Scheduled(fixedRate = 5000) //上一次开始执行时间点之后5秒再执行

1.2.6fixedRateString

1.2.5 fixedRateと同じ意味ですが、文字列の形式を使用します。唯一の違いは、プレースホルダーがサポートされていることです。

1.2.7initialDelay

初回以降の実行を遅らせる時間。といった:

@Scheduled(initialDelay=1000, fixedRate=5000) //第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次

1.2.8 initialDelayString

1.2.7 initialDelayと同じ意味ですが、文字列の形式を使用します。唯一の違いは、プレースホルダーがサポートされていることです。

 

2.動的:インターフェースに基づく(SchedulingConfigurer)

インターフェイス(SchedulingConfigurer)に基づきます。

(1)データテーブルを作成する

MySQLデータベースにcronテーブルを作成し、データを追加します。

DROP TABLE IF EXISTS cron;
CREATE TABLE cron  (
  cron_id VARCHAR(30) NOT NULL PRIMARY KEY,
  cron VARCHAR(30) NOT NULL  
);

INSERT INTO cron VALUES ('1', '0/5 * * * * ?');

(2)pom.xml構成情報を追加します

MyBatisとMySQLのJDBCデータベースドライバーの依存関係をpom.xml構成ファイルに追加します。

<!-- MyBatis与SpringBoot整合依赖 -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.3</version>
</dependency>

<!-- MySQL的JDBC数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.20</version>
</dependency>

(3)構成関連情報

プロジェクトのデフォルトのapplication.propertiesファイルのサフィックスを「.yml」に変更します。つまり、構成ファイル名はapplication.ymlであり、次の情報を構成します。

spring:
  #DataSource数据源
  datasource:
    url: jdbc:mysql://localhost:3306/db_admin?useSSL=false&amp
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

#MyBatis配置
mybatis:
  type-aliases-package: com.pjb.entity #别名定义
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #指定 MyBatis 所用日志的具体实现,未指定时将自动查找
    map-underscore-to-camel-case: true #开启自动驼峰命名规则(camel case)映射
    lazy-loading-enabled: true #开启延时加载开关
    aggressive-lazy-loading: false #将积极加载改为消极加载(即按需加载),默认值就是false
    lazy-load-trigger-methods: "" #阻挡不相干的操作触发,实现懒加载
    cache-enabled: true #打开全局缓存开关(二级环境),默认值就是true

(4)タイマーを作成する

データベースがデータを準備した後、タイミングタスクを記述します。ここにTriggerTaskが追加されていることに注意してください。目的は、データベースに設定した実行サイクルを周期的に読み取り、関連するタイミングタスクの内容を実行することです。具体的なコードは次のとおりです。

package com.pjb.config;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;

/**
 * 动态定时任务配置类
 * @author pan_junbiao
 **/
@Configuration      //1.主要用于标记配置类,兼备Component的效果
@EnableScheduling   //2.开启定时任务
public class DynamicScheduleConfigurer implements SchedulingConfigurer
{
    @Mapper
    public interface CronMapper {
        @Select("select cron from cron limit 1")
        public String getCron();
    }

    //注入mapper
    @Autowired
    @SuppressWarnings("all")
    CronMapper cronMapper;

    /**
     * 执行定时任务.
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar)
    {
        taskRegistrar.addTriggerTask(
                //1.添加任务内容(Runnable)
                () -> System.out.println("欢迎访问 pan_junbiao的博客: " + LocalDateTime.now().toLocalTime()),
                //2.设置执行周期(Trigger)
                triggerContext -> {
                    //2.1 从数据库获取执行周期
                    String cron = cronMapper.getCron();
                    //2.2 合法性校验.
                    if (StringUtils.isEmpty(cron)) {
                        // Omitted Code ..
                    }
                    //2.3 返回执行周期(Date)
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                }
        );
    }
}

結果:

注: データベースの変更時にフォーマットにエラーがある場合、変更が正しい場合でも、時間指定タスクは停止します。現時点では、プロジェクトを復元するには、プロジェクトを再起動する必要があります。

 

3.注釈に基づいてマルチスレッドタイミングタスクを設定します

マルチスレッドのタイミングタスクを作成します。

package com.pjb.Task;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

/**
 * 基于注解设定多线程定时任务
 * @author pan_junbiao
 */
@Component
@EnableScheduling   // 1.开启定时任务
@EnableAsync        // 2.开启多线程
public class MultithreadScheduleTask
{
    @Async
    @Scheduled(fixedDelay = 1000)  //间隔1秒
    public void first() throws InterruptedException {
        System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
        System.out.println();
        Thread.sleep(1000 * 10);
    }

    @Async
    @Scheduled(fixedDelay = 2000)
    public void second() {
        System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
        System.out.println();
    }
}

注:アノテーション@Scheduledはデフォルトでシングルスレッドであるため、複数のタスクが開始されると、タスクの実行時間は前のタスクの実行時間の影響を受けます。したがって、ここで@Asyncアノテーションを使用することは非常に重要です。

結果:

コンソールから、最初の時限タスクと2番目の時限タスクは互いに影響を与えないことがわかります。

また、マルチスレッドが有効になっているため、最初のタスクの実行時間はそれ自体の実行時間によって制限されないため、操作を繰り返すとデータ異常が発生する可能性があることに注意する必要があります。

学習資料1:https //blog.csdn.net/MobiusStrip/article/details/85126261

学習教材2:https //www.jianshu.com/p/1defb0f22ed1

元のアドレス:https //blog.csdn.net/pan_junbiao/article/details/109399280

おすすめ

転載: blog.csdn.net/pan_junbiao/article/details/109399280