SpringBoot uses @Scheduled annotation to implement timing tasks

Preface

After Spring 3.0 version comes with timed tasks, and @EnableScheduling annotations and @Scheduled annotations are provided to realize the timed task functions.

It is very simple to use SpringBoot to create a timed task. There are currently three main ways to create it:

  • 1. Based on annotations (@Scheduled)
  • 2. Based on the interface (SchedulingConfigurer) The former is believed to be familiar to everyone, but in actual use, we often want to read the specified time from the database to dynamically execute timing tasks. At this time, interface-based timing tasks come in handy.
  • 3. Set multi-threaded timing tasks based on annotations

1. Based on annotations (@Scheduled)

1.1 Use of @Scheduled annotation and @EnableScheduling annotation

Based on the annotation @Scheduled is single-threaded by default. When multiple tasks are started, the execution time of the task is affected by the execution time of the previous task.

@EnableScheduling Annotation: Use on the configuration class to enable the support of scheduled tasks (on the class).

@Scheduled annotation: to declare that this is a task, including cron, fixDelay, fixRate and other types (in terms of method, you need to turn on the support of scheduled tasks first).

[Example] The @Scheduled annotation and @EnableScheduling annotation are used in the SpringBoot project to implement timing tasks.

(1) Start timing tasks

In the SpringBoot project, add the @EnableScheduling annotation to the project startup class to enable timing task management.

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) Create timed tasks

Create a scheduled task and use the @Scheduled annotation.

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()));
    }
}

Results of the:

1.2 Explanation of each parameter of @Scheduled annotation

The use of @Scheduled annotation is not explained in detail here, and the 8 parameters are explained directly.

The source code of the @Scheduled annotation class is as follows:

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.1 cron

This parameter accepts a cron expression. The cron expression is a string. The string is separated by 5 or 6 spaces, separated by a total of 6 or 7 fields, and each field represents a meaning.

Cron expression syntax:

Format: [second] [minute] [hour] [day] [month] [week] [year]

Serial number Description Is it required Allowed values Allowed wildcards
1 second Yes 0-59 , - * /
2 Minute Yes 0-59 , - * /
3 hour Yes 0-23 , - * /
4 day Yes 1-31 , - * ? / L W
5 month Yes 1-12 or JAN-DEC , - * /
6 week Yes 1-7 or SUN-SAT , - * ? / L #
7 year no empty or 1970-2099 , - * /

Wildcard description:

Means all values. For example: setting "*" on the minute field means it will be triggered every minute.

?  Means no value is specified. The scenario used is that you don't need to care about the value of this field currently set. For example: you want to trigger an operation on the 10th of each month, but don’t care about the day of the week, so you need to set the field of the week position to "?" Specifically, set it to 0 0 0 10 *?

-  representing the interval. For example, setting "10-12" on the hour means that it will trigger at 10, 11, and 12 o'clock.

,  Means to specify multiple values, for example, setting "MON,WED,FRI" on the week field means triggering on Monday, Wednesday and Friday

/ Is  used for incremental triggering. For example, set "5/15" on the second, it means starting from 5 seconds and triggering every 15 seconds (5, 20, 35, 50). Set "1/3" in the month field to start on the 1st of each month and trigger once every three days.

L  means the last meaning. In the day field setting, it means the last day of the month (according to the current month, if it is February, it will also depend on whether it is a lunar year [leap]), and in the week field it means Saturday, which is equivalent to "7" or "SAT". If you add a number before "L", it means the last one of the data. For example, setting the format "6L" on the week field means "the last Friday of the month"

W  means the closest working day to the specified date (Monday to Friday). For example, setting "15W" in the day field means that it will be triggered on the working day closest to the 15th of each month. If the 15th happens to be a Saturday, find the nearest Friday (14th) to trigger, if the 15th is a weekday, find the nearest next Monday (16th) to trigger. If the 15th happens to be a working day (Monday to Week 5), it will be triggered on that day. If the specified format is "1W", it means that it will be triggered on the nearest working day after the 1st of each month. If the 1st falls on Saturday, it will be triggered on the 3rd next Monday. (Note, only specific numbers can be set before "W", and the interval "-" is not allowed).

Tips: 'L' and'W' can be used in combination. If "LW" is set in the day field, it means that it will be triggered on the last working day of the month (generally referring to payroll).

#Serial  number (representing the first few weeks of the month), for example, setting "6#3" in the week field means it is on the third Saturday of each month. Note that if you specify "#5", there is no week in the fifth week Six, this configuration will not be triggered (it is suitable for Mother's Day and Father's Day)

Tips: The setting of the week field is case-insensitive if English letters are used. MON is the same as mon.

You can use the online tool for generating cron expressions: http://cron.qqe2.com/  to generate the expressions you want.

Common examples:

0 0 12 * * ? Trigger at 12 o'clock every day
0 15 10 ? * * Trigger at 10:15 every day
0 15 10 * * ? Trigger at 10:15 every day
0 15 10 * * ? * Trigger at 10:15 every day
0 15 10 * * ? 2005 Fire at 10:15 every day in 2005
0 * 14 * * ? Trigger every minute from 2pm to 2:59pm every day
0 0/5 14 * * ? Every afternoon from 2 pm to 2:59 pm (starts on the hour and triggers every 5 minutes)
0 0/5 14,18 * * ? Every afternoon from 2 o’clock to 2:59 pm (starts on the hour and triggers every 5 minutes) every day from 18 o’clock to 18:59 pm (starts on the hour and triggers every 5 minutes)
0 0-5 14 * * ? Triggered every minute from 2pm to 2:05pm every day
0 10,44 14 ? 3 WED Triggered every Wednesday at 2:10 and 2:44 in the afternoon in March
0 15 10? * MON-FRI Fire at 10:15 am every day from Monday to Friday
0 15 10 15 * ? Fire at 10:15 am on the 15th of every month
0 15 10 L * ? Fire at 10:15 on the last day of each month
0 15 10 ? * 6L Fire at 10:15 on the last Friday of each month
0 15 10 ? * 6L 2002-2005 Triggered at 10:15 on the last Friday of each month from 2002 to 2005
0 15 10 ? * 6#3 Trigger on Friday of the third week of each month
0 0 12 1/5 * ? Triggered every 5 days starting at the first noon of each month
0 11 11 11 11 ? Fire at 11:11 on November 11th every year (Singles' Day)

cron expressions use placeholders

In addition, the cron expression received by the cron attribute supports placeholders. eg:

Configuration file:

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

Execute every 5 seconds:

@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 zone

Time zone, receive a java.util.TimeZone#ID. The cron expression will be parsed based on the time zone. The default is an empty string, that is, the time zone where the server is located. For example, we generally use the time zone Asia/Shanghai. We generally leave this field blank.

1.2.3 fixedDelay

How long will it take to execute after the last execution time point. Such as:

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

1.2.4 fixedDelayString

It has the same meaning as 1.2.3 fixedDelay, but uses the form of a string. The only difference is that placeholders are supported. Such as:

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

Use of placeholders:

Add the following configuration in the application.yml configuration file:

time:
  fixedDelay: 5000

Write relevant code: 

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

Results of the:

1.2.5 fixedRate

How long after the last time the execution started. Such as:

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

1.2.6 fixedRateString

It has the same meaning as 1.2.5 fixedRate, but uses the form of a string. The only difference is that placeholders are supported.

1.2.7 initialDelay

How long to delay the execution after the first time. Such as:

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

1.2.8 initialDelayString

It has the same meaning as 1.2.7 initialDelay, but uses the form of a string. The only difference is that placeholders are supported.

 

2. Dynamic: Based on the interface (SchedulingConfigurer)

Based on the interface (SchedulingConfigurer).

(1) Create a data table

Create a cron table in the MySQL database and add data.

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) Add pom.xml configuration information

Add the JDBC database driver dependencies of MyBatis and MySQL in the pom.xml configuration file.

<!-- 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) Configuration related information

Modify the suffix of the project's default application.properties file to ".yml", that is, the configuration file name is: application.yml, and configure the following information:

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) Create a timer

After the database has prepared the data, we write a timing task. Note that TriggerTask is added here. The purpose is to cyclically read the execution cycle that we set in the database and execute the content of related timing tasks. The specific code is as follows:

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);
                }
        );
    }
}

Results of the:

Note:  If there is an error in the format when the database is modified, the timed task will stop, even if the modification is correct; at this time, the project can only be restored by restarting the project.

 

3. Set multi-threaded timing tasks based on annotations

Create multi-threaded timing tasks.

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();
    }
}

Note: Due to the annotation @Scheduled is single-threaded by default, when multiple tasks are started, the execution time of the task will be affected by the execution time of the previous task. So it is very important to use @Async annotation here.

Results of the:

It can be seen from the console that the first timed task and the second timed task do not affect each other;

In addition, since multithreading is enabled, the execution time of the first task is not limited by its own execution time, so it is necessary to be aware that repeated operations may cause data abnormalities.

Learning material 1: https://blog.csdn.net/MobiusStrip/article/details/85126261

Learning materials 2: https://www.jianshu.com/p/1defb0f22ed1

Original address: https://blog.csdn.net/pan_junbiao/article/details/109399280

Guess you like

Origin blog.csdn.net/pan_junbiao/article/details/109399280