Rumah  >  Artikel  >  Java  >  Bagaimana untuk melaksanakan tugas berjadual dinamik SpringBoot

Bagaimana untuk melaksanakan tugas berjadual dinamik SpringBoot

王林
王林ke hadapan
2023-05-21 14:05:301200semak imbas

Kelas konfigurasi kumpulan benang untuk melaksanakan tugas berjadual

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 
@Configuration
public class SchedulingConfig {  
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        // 定时任务执行线程池核心线程数  
        taskScheduler.setPoolSize(6);  
        taskScheduler.setRemoveOnCancelPolicy(true);  
        taskScheduler.setThreadNamePrefix("TaskSchedulerThreadPool-");  
        return taskScheduler;  
    }  
}

Kelas kertas ScheduledFuture

ScheduledFuture ialah hasil pelaksanaan kumpulan utas tugas berjadual ScheduledExecutorService.

import java.util.concurrent.ScheduledFuture;
 
public final class ScheduledTask {
  
    volatile ScheduledFuture<?> future;
    /**  
     * 取消定时任务  
     */  
    public void cancel() {  
        ScheduledFuture<?> future = this.future;  
        if (future != null) {  
            future.cancel(true);  
        }  
    }  
}

Kelas pelaksanaan antara muka boleh jalan

dipanggil oleh kumpulan benang tugas yang dijadualkan untuk melaksanakan kaedah dalam kacang yang ditentukan.

import com.ying.demo.utils.springContextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.Objects;
 
public class SchedulingRunnable implements Runnable {
 
   private static final Logger logger = LoggerFactory.getLogger(SchedulingRunnable.class);
 
   private String beanName;
 
   private String methodName;
 
   private String params;
 
   public SchedulingRunnable(String beanName, String methodName) {
      this(beanName, methodName, null);
   }
 
   public SchedulingRunnable(String beanName, String methodName, String params) {
      this.beanName = beanName;
      this.methodName = methodName;
      this.params = params;
   }
 
   @Override
   public void run() {
      logger.info("定时任务开始执行 - bean:{},方法:{},参数:{}", beanName, methodName, params);
      long startTime = System.currentTimeMillis();
 
      try {
         Object target = springContextUtils.getBean(beanName);
 
         Method method = null;
         if (!StringUtils.isEmpty(params)) {
            method = target.getClass().getDeclaredMethod(methodName, String.class);
         } else {
            method = target.getClass().getDeclaredMethod(methodName);
         }
 
         ReflectionUtils.makeAccessible(method);
         if (!StringUtils.isEmpty(params)) {
            method.invoke(target, params);
         } else {
            method.invoke(target);
         }
      } catch (Exception ex) {
         logger.error(String.format("定时任务执行异常 - bean:%s,方法:%s,参数:%s ", beanName, methodName, params), ex);
      }
 
      long times = System.currentTimeMillis() - startTime;
      logger.info("定时任务执行结束 - bean:{},方法:{},参数:{},耗时:{} 毫秒", beanName, methodName, params, times);
   }
 
   @Override
   public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      SchedulingRunnable that = (SchedulingRunnable) o;
      if (params == null) {
         return beanName.equals(that.beanName) &&
               methodName.equals(that.methodName) &&
               that.params == null;
      }
 
      return beanName.equals(that.beanName) &&
            methodName.equals(that.methodName) &&
            params.equals(that.params);
   }
 
   @Override
   public int hashCode() {
      if (params == null) {
         return Objects.hash(beanName, methodName);
      }
 
      return Objects.hash(beanName, methodName, params);
   }
}

Kelas pendaftaran tugas bermasa

Digunakan untuk menambah dan memadam tugas bermasa

@Component
public class CronTaskRegistrar implements DisposableBean {
 
   private final Map<Runnable, ScheduledTask> scheduledTasks = new ConcurrentHashMap<>(16);
 
   @Autowired
   private TaskScheduler taskScheduler;
 
   public TaskScheduler getScheduler() {
      return this.taskScheduler;
   }
 
   public void addCronTask(Runnable task, String cronExpression) {
      addCronTask(new CronTask(task, cronExpression));
   }
 
   public void addCronTask(CronTask cronTask) {
      if (cronTask != null) {
         Runnable task = cronTask.getRunnable();
         if (this.scheduledTasks.containsKey(task)) {
            removeCronTask(task);
         }
 
         this.scheduledTasks.put(task, scheduleCronTask(cronTask));
      }
   }
 
   public void removeCronTask(Runnable task) {
      ScheduledTask scheduledTask = this.scheduledTasks.remove(task);
      if (scheduledTask != null)
         scheduledTask.cancel();
   }
 
   public ScheduledTask scheduleCronTask(CronTask cronTask) {
      ScheduledTask scheduledTask = new ScheduledTask();
      scheduledTask.future = this.taskScheduler.schedule(cronTask.getRunnable(), cronTask.getTrigger());
 
      return scheduledTask;
   }
 
 
   @Override
   public void destroy() {
      for (ScheduledTask task : this.scheduledTasks.values()) {
         task.cancel();
      }
 
      this.scheduledTasks.clear();
   }
}

Kelas contoh tugas bermasa

@Slf4j
@Component("taskDemo")
public class Task1 {  
    public void taskByParams(String params) {
        log.info("taskByParams执行时间:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        log.info("taskByParams执行有参示例任务:{}",params);
    }  
  
    public void taskNoParams() {
        log.info("taskByParams执行时间:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        log.info("taskNoParams执行无参示例任务");
    }
 
    public void test(String params) {
        log.info("test执行时间:{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        log.info("test执行有参示例任务:{}",params);
    }
}

Reka bentuk jadual pangkalan data

rreee

Kelas entiti

CREATE TABLE `schedule_setting` (
  `job_id` int NOT NULL AUTO_INCREMENT COMMENT &#39;任务ID&#39;,
  `bean_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT &#39;bean名称&#39;,
  `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT &#39;方法名称&#39;,
  `method_params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT &#39;方法参数&#39;,
  `cron_expression` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT &#39;cron表达式&#39;,
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT &#39;备注&#39;,
  `job_status` int DEFAULT NULL COMMENT &#39;状态(1正常 0暂停)&#39;,
  `create_time` datetime DEFAULT NULL COMMENT &#39;创建时间&#39;,
  `update_time` datetime DEFAULT NULL COMMENT &#39;修改时间&#39;,
  PRIMARY KEY (`job_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

Memanaskan badan tugas berjadual

Selepas projek but spring dimulakan, muatkan tugas yang dijadualkan dengan status normal dalam pangkalan data

@Data
public class ScheduleSetting extends Model<ScheduleSetting> {
   /**
    * 任务ID
    */
   @Id
   private Integer jobId;
   /**
    * bean名称
    */
   private String beanName;
   /**
    * 方法名称
    */
   private String methodName;
   /**
    * 方法参数
    */
   private String methodParams;
   /**
    * cron表达式
    */
   private String cronExpression;
   /**
    * 状态(1正常 0暂停)
    */
   private Integer jobStatus;
   /**
    * 备注
    */
   private String remark;
   /**
    * 创建时间
    */
   private Date createTime;
   /**
    * 更新时间
    */
   private Date updateTime;
}

Kelas alat

Digunakan untuk mendapatkan kacang dari bekas spring

@Service  
public class SysJobRunner implements CommandLineRunner {  
  
    private static final Logger logger = LoggerFactory.getLogger(SysJobRunner.class);  
  
    @Autowired  
    private CronTaskRegistrar cronTaskRegistrar;  
  
    @Override  
    public void run(String... args) {  
        // 初始加载数据库里状态为正常的定时任务  
        ScheduleSetting existedSysJob = new ScheduleSetting();
        List<ScheduleSetting> jobList = existedSysJob.selectList(new QueryWrapper<ScheduleSetting>().eq("job_status", 1));
        if (CollectionUtils.isNotEmpty(jobList)) {  
            for (ScheduleSetting job : jobList) {  
                SchedulingRunnable task = new SchedulingRunnable(job.getBeanName(), job.getMethodName(), job.getMethodParams());  
                cronTaskRegistrar.addCronTask(task, job.getCronExpression());  
            }  
            logger.info("定时任务已加载完毕...");  
        }  
    }  
}

Tugas berjadual: tambah/padam/ubah suai/mulakan/jeda

@Component  
public class SpringContextUtils implements ApplicationContextAware {  
  
    private static ApplicationContext applicationContext;  
  
    @Override  
    public void setApplicationContext(ApplicationContext applicationContext)  
            throws BeansException {  
        SpringContextUtils.applicationContext = applicationContext;  
    }  
  
    public static Object getBean(String name) {  
        return applicationContext.getBean(name);  
    }  
  
    public static <T> T getBean(Class<T> requiredType) {  
        return applicationContext.getBean(requiredType);  
    }  
  
    public static <T> T getBean(String name, Class<T> requiredType) {  
        return applicationContext.getBean(name, requiredType);  
    }  
  
    public static boolean containsBean(String name) {  
        return applicationContext.containsBean(name);  
    }  
  
    public static boolean isSingleton(String name) {  
        return applicationContext.isSingleton(name);  
    }  
  
    public static Class<? extends Object> getType(String name) {  
        return applicationContext.getType(name);  
    }  
}

Bagaimana untuk melaksanakan tugas berjadual dinamik SpringBoot

cron

sintaks ungkapan cron:

[saat] [minit] [jam] [hari] [bulan] [minggu] [tahun]

Nota: [ Tahun] bukan medan yang diperlukan, anda boleh meninggalkan [Tahun], kemudian terdapat 6 medan kesemuanya

Bagaimana untuk melaksanakan tugas berjadual dinamik SpringBoot

Penerangan kad liar:

  • * mewakili semua nilai. Contohnya: tetapan * pada medan minit bermakna ia akan mencetuskan setiap minit.

  • bermakna tiada nilai dinyatakan. Senario penggunaan ialah anda tidak perlu mengambil berat tentang nilai semasa medan ini. Sebagai contoh: anda ingin mencetuskan operasi pada 10 haribulan setiap bulan, tetapi anda tidak kisah hari dalam minggu itu, jadi anda perlu menetapkan kedudukan medan minggu kepada "?" 0 0 10 * ?

  • - mewakili selang. Sebagai contoh, menetapkan "10-12" pada jam bermakna ia akan dicetuskan pada pukul 10, 11 dan 12.

  • , bermaksud menyatakan berbilang nilai, sebagai contoh, menetapkan "ISNIN, RABU, JUM" pada medan minggu bermakna mencetuskan pada hari Isnin, Rabu dan Jumaat

  • / digunakan untuk mencetuskan tambahan. Tetapkan bilangan saat kepada "5/15", yang bermaksud bahawa bermula dari saat ke-5, ia akan mencetuskan setiap 15 saat (iaitu, saat ke-5, saat ke-20, saat ke-35 dan saat ke-50). Tetapkan "1/3" dalam medan hari untuk bermula pada 1 haribulan setiap bulan dan cetuskan setiap tiga hari.

  • L bermaksud perkataan terakhir. Dalam tetapan medan hari, ia mewakili hari terakhir bulan tersebut (mengikut bulan semasa, jika bulan Februari, ia juga akan bergantung pada sama ada ia adalah tahun lompat) dan dalam medan minggu ia mewakili hari Sabtu, iaitu bersamaan dengan "7" atau "SAT". Jika anda menambah nombor sebelum "L", ini bermakna yang terakhir daripada data. Contohnya, menetapkan format seperti "6L" pada medan minggu bermaksud "Jumaat terakhir bulan ini"

  • W mewakili hari bekerja terdekat dengan tarikh yang ditentukan (Isnin hingga Jumaat) . Contohnya, menetapkan "15W" dalam medan hari bermakna pencetus dicetuskan pada hari bekerja paling hampir dengan 15hb setiap bulan. Jika 15hb kebetulan hari Sabtu, picu akan ditemui pada hari Jumaat terdekat (14hb Jika 15hb adalah hujung minggu, picu akan ditemui pada hari Isnin yang terdekat (16hb). pada hari bekerja (Isnin hingga Ahad) 5), ia akan dicetuskan pada hari tersebut. Apabila format yang ditentukan ialah "1W", ini bermakna ia dicetuskan pada hari bekerja terdekat selepas 1hb setiap bulan. Jika 1hb jatuh pada hari Sabtu, ia akan dicetuskan pada hari Isnin 3hb. (Nota, hanya nombor tertentu boleh ditetapkan sebelum "W", dan selang "-" tidak dibenarkan).

  • # Nombor siri (menunjukkan hari dalam minggu setiap bulan), contohnya, tetapan "6#3" pada medan minggu menunjukkan Sabtu ketiga setiap bulan jika Nyatakan "#5", jika tiada hari Sabtu dalam minggu kelima, konfigurasi ini tidak akan dicetuskan (ia sesuai untuk Hari Ibu dan Hari Bapa: "L" dan "W" boleh digunakan dalam kombinasi . Jika "LW" ditetapkan pada medan hari, ini bermakna ia dicetuskan pada hari bekerja terakhir bulan ini; medan tetapan minggu tidak peka huruf besar kecil jika huruf Inggeris digunakan, iaitu MON adalah sama sebagai mon.

Contoh:

Dilaksanakan setiap 5 saat: */5 * * * * ?

Dilaksanakan setiap 1 minit: 0 */1 * * * ?

Dilaksanakan sekali setiap hari pada 23:00: 0 0 23 * * ?

Dilaksanakan sekali setiap hari pada 1 pagi: 0 0 1 * * ?

Laksanakan sekali pada 1 pagi pada 1hb setiap bulan: 0 0 1 1 * ?

Laksanakan sekali pada 23:00 pada hari terakhir setiap bulan: 0 0 23 L * ?

Dilaksanakan sekali setiap hari Sabtu jam 1 pagi: 0 0 1 ? * L

Dilaksanakan sekali pada 26 minit, 29 minit dan 33 minit: 0 26, 29, 33 * * * ?

Laksanakan sekali setiap hari pada 0:00, 13:00, 18:00 dan 21:00: 0 0 0,13,18,21 * * ?

penjana ungkapan dalam talian cron: http://tools.jb51.net/code/Quartz_Cron_create

Atas ialah kandungan terperinci Bagaimana untuk melaksanakan tugas berjadual dinamik SpringBoot. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam