SpringBoot usa la anotación @Scheduled para implementar tareas de sincronización

Prefacio

Después de Spring 3.0, la versión viene con tareas cronometradas, y se proporcionan anotaciones @EnableScheduling y anotaciones @Scheduled para realizar las funciones de tareas cronometradas.

Es muy simple usar SpringBoot para crear una tarea cronometrada. Actualmente hay tres formas principales de crearla:

  • 1. Basado en anotaciones (@Scheduled)
  • 2. Basado en la interfaz (SchedulingConfigurer) Se cree que el primero es familiar para todos, pero en el uso real, a menudo queremos leer el tiempo especificado de la base de datos para ejecutar dinámicamente las tareas de sincronización. En este momento, las tareas de sincronización basadas en la interfaz Ser util.
  • 3. Establecer tareas de tiempo de subprocesos múltiples basadas en anotaciones

1. Basado en anotaciones (@Scheduled)

1.1 Uso de la anotación @Scheduled y la anotación @EnableScheduling

Según la anotación, @Scheduled tiene un solo subproceso de forma predeterminada. Cuando se inician varias tareas, el tiempo de ejecución de la tarea se ve afectado por el tiempo de ejecución de la tarea anterior.

@EnableScheduling Annotation: Úselo en la clase de configuración para habilitar el soporte de tareas programadas (en la clase).

@Anotación programada: para declarar que se trata de una tarea, incluidos cron, fixDelay, fixRate y otros tipos (en términos de método, primero debe activar el soporte de tareas programadas).

[Ejemplo] La anotación @Scheduled y la anotación @EnableScheduling se utilizan en el proyecto SpringBoot para implementar tareas de sincronización.

(1) Iniciar tareas de cronometraje

En el proyecto SpringBoot, agregue la anotación @EnableScheduling a la clase de inicio del proyecto para habilitar la administración de tareas de tiempo.

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) Cree tareas cronometradas

Cree una tarea programada y use la anotación @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()));
    }
}

Resultados del:

1.2 Explicación de cada parámetro de la anotación @Scheduled

El uso de la anotación @Scheduled no se explica en detalle aquí, y los 8 parámetros se explican directamente.

El código fuente de la clase de anotación @Scheduled es el siguiente:

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

Este parámetro acepta una expresión cron. La expresión cron es una cadena. La cadena está separada por 5 o 6 espacios, separados por un total de 6 o 7 campos, y cada campo representa un significado.

Sintaxis de la expresión cron:

Formato: [segundo] [minuto] [hora] [día] [mes] [semana] [año]

Número de serie Descripción Es requerido Valores permitidos Comodines permitidos
1 segundo 0-59 , - * /
2 Minuto 0-59 , - * /
3 hora 0-23 , - * /
4 día 1-31 , - *? / LW
5 mes 1-12 o ENE-DIC , - * /
6 semana 1-7 o SUN-SAT , - *? / L #
7 año No vacío o 1970-2099 , - * /

Descripción del comodín:

Significa todos los valores. Por ejemplo: establecer "*" en el campo de los minutos significa que se activará cada minuto.

?  Significa que no se especifica ningún valor. El escenario utilizado es que no necesita preocuparse por el valor de este campo configurado actualmente. Por ejemplo: desea activar una operación el día 10 de cada mes, pero no le importa el día de la semana, por lo que debe establecer el campo de la posición de la semana en "?" Específicamente, configúrelo en 0 0 0 10 *?

- que  representa el intervalo. Por ejemplo, configurar "10-12" en la hora significa que se activará a las 10, 11 y 12 en punto.

,  Significa especificar varios valores, por ejemplo, establecer "MON, WED, FRI" en el campo de la semana significa activar el lunes, miércoles y viernes

/ Se  utiliza para disparos incrementales. Por ejemplo, establezca "5/15" en el segundo, significa comenzar desde 5 segundos y disparar cada 15 segundos (5, 20, 35, 50). Configure '1/3' en el campo del mes para que comience el día 1 de cada mes y se active una vez cada tres días.

L  significa el último significado. En la configuración del campo del día, significa el último día del mes (según el mes actual, si es febrero, también dependerá de si es un año lunar [bisiesto]), y en el campo de la semana significa sábado , que es equivalente a "7" o "SAT". Si agrega un número antes de "L", significa el último de los datos. Por ejemplo, configurar el formato "6L" en el campo de la semana significa "el último viernes del mes"

W  representa el día laborable más cercano (de lunes a viernes) a la fecha especificada. Por ejemplo, configurar "15W" en el campo del día significa que el disparador se activa el día laborable más cercano al día 15 de cada mes. Si el día 15 es sábado, busque el viernes más cercano (14) para activar, si el día 15 es un día laborable, busque el próximo lunes (16) más cercano para activar. Si el día 15 es un día laborable (lunes a Semana) 5), se activará ese día. Si el formato especificado es "1W", significa que se activará el día laborable más cercano después del día 1 de cada mes. Si el 1º cae en sábado, se activará el 3º próximo lunes. (Tenga en cuenta que solo se pueden establecer números específicos antes de "W" y el intervalo "-" no está permitido).

Consejos: 'L' y 'W' se pueden usar en combinación. Si se establece "LW" en el campo del día, significa que se activará el último día hábil del mes (generalmente se refiere a la nómina).

#  Número de serie (que representa las primeras semanas del mes), por ejemplo, establecer "6 # 3" en el campo de la semana significa que es el tercer sábado de cada mes. Tenga en cuenta que si se especifica "# 5", no no es una semana en la quinta semana Seis, esta configuración no se activará (es adecuada para el Día de la Madre y el Día del Padre)

Sugerencias: La configuración del campo de la semana no distingue entre mayúsculas y minúsculas si se utilizan letras en inglés. MON es lo mismo que mon.

Puede utilizar la herramienta en línea para generar expresiones cron: http://cron.qqe2.com/  para generar las expresiones que desee.

Ejemplos comunes:

0 0 12 * *? Disparo a las 12 en punto todos los días
0 15 10? * * Activar a las 10:15 todos los días
0 15 10 * *? Activar a las 10:15 todos los días
0 15 10 * *? * Activar a las 10:15 todos los días
0 15 10 * *? 2005 Fuego a las 10:15 todos los días en 2005
0 * 14 * *? Activar cada minuto de 2 p.m. a 2:59 p.m. todos los días
0 0/5 14 * *? Todas las tardes de 2 p.m. a 2:59 p.m. (comienza a la hora y se activa cada 5 minutos)
0 0/5 14,18 * *? Todas las tardes desde las 2 en punto hasta las 2:59 p. M. (Comienza en la hora y se activa cada 5 minutos) todos los días desde las 18 en punto hasta las 18:59 p. M. (Comienza en la hora y se activa cada 5 minutos)
0 0-5 14 * *? Se activa cada minuto de 2 p.m. a 2:05 p.m. todos los días
0 10,44 14? 3 MIÉ Se activa todos los miércoles a las 2:10 y a las 2:44 de la tarde de marzo
0 15 10? * LUN-VIE Fuego a las 10:15 am todos los días de lunes a viernes
0 15 10 15 *? Fuego a las 10:15 am el día 15 de cada mes
0 15 10 L *? Disparar a las 10:15 del último día de cada mes
0 15 10? * 6L Disparar a las 10:15 el último viernes de cada mes
0 15 10? * 6L 2002-2005 Se activa a las 10:15 del último viernes de cada mes de 2002 a 2005
0 15 10? * 6 # 3 Disparo el viernes de la tercera semana de cada mes
0 0 12 1/5 *? Se activa cada 5 días a partir del primer mediodía de cada mes
0 11 11 11 11? Disparo a las 11:11 del 11 de noviembre de cada año (Día del Soltero)

las expresiones cron usan marcadores de posición

Además, la expresión cron recibida por el atributo cron admite marcadores de posición. p.ej:

Archivo de configuración:

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

Ejecutar cada 5 segundos:

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

Zona horaria, reciba un java.util.TimeZone # ID. La expresión cron se analizará según la zona horaria. El valor predeterminado es una cadena vacía, es decir, la zona horaria en la que se encuentra el servidor. Por ejemplo, generalmente usamos la zona horaria Asia / Shanghai. Generalmente dejamos este campo en blanco.

1.2.3 FixedDelay

¿Cuánto tiempo llevará la ejecución después del último punto de tiempo de ejecución? Tal como:

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

1.2.4 fixedDelayString

Tiene el mismo significado que 1.2.3 FixedDelay, pero usa la forma de una cadena. La única diferencia es que se admiten marcadores de posición. Tal como:

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

Uso de marcadores de posición:

Agregue la siguiente configuración en el archivo de configuración application.yml:

time:
  fixedDelay: 5000

Escriba el código relevante: 

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

Resultados del:

1.2.5 tarifa fija

Cuánto tiempo después de la última vez que comenzó la ejecución. Tal como:

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

1.2.6 fixedRateString

Tiene el mismo significado que 1.2.5 FixedRate, pero usa la forma de una cadena. La única diferencia es que se admiten marcadores de posición.

1.2.7 retraso inicial

Cuánto tiempo demorar la ejecución después de la primera vez. Tal como:

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

1.2.8 initialDelayString

Tiene el mismo significado que 1.2.7 initialDelay, pero usa la forma de una cadena. La única diferencia es que se admiten marcadores de posición.

 

2. Dinámico: basado en la interfaz (SchedulingConfigurer)

Basado en la interfaz (SchedulingConfigurer).

(1) Cree una tabla de datos

Cree una tabla cron en la base de datos MySQL y agregue datos.

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) Agregar información de configuración pom.xml

Agregue las dependencias del controlador de la base de datos JDBC de MyBatis y MySQL en el archivo de configuración 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) Información relacionada con la configuración

Modifique el sufijo del archivo application.properties predeterminado del proyecto a ".yml", es decir, el nombre del archivo de configuración es: application.yml, y configure la siguiente información:

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) Crea un temporizador

Una vez que la base de datos ha preparado los datos, escribimos una tarea de sincronización. Tenga en cuenta que aquí se agrega TriggerTask. El propósito es leer cíclicamente el ciclo de ejecución que establecemos en la base de datos y ejecutar el contenido de las tareas de sincronización relacionadas. El código específico es el siguiente:

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

Resultados del:

Nota:  Si hay un error en el formato cuando se modifica la base de datos, la tarea cronometrada se detendrá, incluso si la modificación es correcta; en este momento, el proyecto solo se puede restaurar reiniciando el proyecto.

 

3. Establecer tareas de tiempo de subprocesos múltiples basadas en anotaciones

Cree tareas de temporización de subprocesos múltiples.

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

Nota: Debido a que la anotación @Scheduled es de un solo subproceso de forma predeterminada, cuando se inician varias tareas, el tiempo de ejecución de la tarea se verá afectado por el tiempo de ejecución de la tarea anterior. Por lo tanto, es muy importante usar la anotación @Async aquí.

Resultados del:

Se puede ver en la consola que la primera tarea cronometrada y la segunda tarea cronometrada no se afectan entre sí;

Además, dado que el subproceso múltiple está habilitado, el tiempo de ejecución de la primera tarea no está limitado por su propio tiempo de ejecución, por lo que es necesario tener en cuenta que las operaciones repetidas pueden causar anomalías en los datos.

Material de aprendizaje 1: https://blog.csdn.net/MobiusStrip/article/details/85126261

Materiales de aprendizaje 2: https://www.jianshu.com/p/1defb0f22ed1

Dirección original: https://blog.csdn.net/pan_junbiao/article/details/109399280

Supongo que te gusta

Origin blog.csdn.net/pan_junbiao/article/details/109399280
Recomendado
Clasificación