SpringBoot整合Quartz,通过注解方式实现定时任务

一、依赖引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.0.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
    <version>2.3.0</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.6.26</version>
</dependency>
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.10.2</version>
</dependency>

二、添加配置

application.properties

spring.quartz.properties.org.quartz.scheduler.instanceName=testScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
spring.quartz.properties.org.quartz.jobStore.class= org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=10000
spring.quartz.properties.org.quartz.jobStore.useProperties=false
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount=10
spring.quartz.properties.org.quartz.threadPool.threadPriority=5
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
spring.quartz.job-store-type=jdbc

三、建表

在数据库创建tables_mysql.sql里面的表

四、编码

涉及六个类如下

(1)JobFactory.java

@Component
public class JobFactory extends AdaptableJobFactory {
    
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        //进行注入
        capableBeanFactory.autowireBean(jobInstance);

        return jobInstance;
    }
}

(2)ClassUtils.java

public class ClassUtils {

    public static Set<Class<?>> getClasses(String packageName, Class<? extends Annotation> annotationCls) {
        Reflections reflections = new Reflections(packageName);
        return reflections.getTypesAnnotatedWith(annotationCls);
    }

    public static boolean isSubclass(Class<?> clazz, Class<?> superClass) {
        if (clazz == null || superClass == null) {
            return false;
        }
        if (clazz.equals(superClass) || clazz.isAssignableFrom(superClass)) {
            return true;
        }
        return isSubclass(clazz.getSuperclass(), superClass);
    }
}

(3)QuartzConfig.java

@Configuration
public class QuartzConfig implements SchedulerFactoryBeanCustomizer {

    @Autowired
    private JobFactory jobFactory;

    @Autowired
    private DefaultListableBeanFactory beanFactory;

    @Override
    public void customize(SchedulerFactoryBean schedulerFactoryBean) {
        //项目启动完成后,等待10秒后开始执行调度器初始化
        schedulerFactoryBean.setStartupDelay(10);
        //设置调度器自动运行
        schedulerFactoryBean.setAutoStartup(true);
        //QuartzScheduler启动时更新已存在的job
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        //将spring管理job自定义工厂交由调度器维护
        schedulerFactoryBean.setJobFactory(jobFactory);
        //这样当spring关闭时,会等待所有已经启动的quartz job结束后spring才能完全shutdown。
        schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
    }

    /**
     * 初始化定时任务监听器
     **/
     @Bean
    public QuartzInitializerListener executorListener() {
        return new QuartzInitializerListener();
    }

    @PostConstruct
    public void initMethod() {

        //创建注解定时任务
        createAnnotationJob();

    }

    /**
     * 找出所有定时任务,并创建
     */
    private void createAnnotationJob() {
        //TODO 此处改成定时任务TestJob.java所在包即可
        String packageName = this.getClass().getPackage().getName();
        Class<QuartzSchedule> annotationCls = QuartzSchedule.class;
        Set<Class<?>> annotationClsSet = ClassUtils.getClasses(packageName, annotationCls);

        annotationClsSet.forEach(cls -> {
            QuartzSchedule annotation = cls.getAnnotation(annotationCls);
            String cronSchedule = annotation.cronSchedule();
            if(!StringUtils.isEmpty(cronSchedule) && ClassUtils.isSubclass(cls, Job.class)) {
                Class<? extends Job> jobSubClass = cls.asSubclass(Job.class);
                createJob(jobSubClass, cronSchedule);
            }
        });
    }

    /**
     * 创建定时任务 
     * @param jobCls  定时任务执行类
     * @param cronSchedule 执行计划
     */
    private void createJob(Class<? extends Job> jobCls, String cronSchedule) {
        //任务详情
        JobDetail jobDetail = jobDetail(jobCls);
        //手动注入定时任务详情jobDetail
        registerSingletonBean(jobDetail);

        //任务触发器
        Trigger trigger = jobTrigger(jobCls, cronSchedule);
        //手动注入触发器trigger
        registerSingletonBean(trigger);
    }

    /**
     * 注入bean
     */
    private void registerSingletonBean(Object bean) {
        String beanName = bean.getClass().getSimpleName().substring(0,1).toLowerCase() + bean.getClass().getSimpleName().substring(1) + UUID.randomUUID();
        // 手动注入bean
        beanFactory.registerSingleton(beanName, bean);
    }

    /**
     * 定时任务详情
     */
    private JobDetail jobDetail(Class<? extends Job> jobCls){
        String name = jobCls.getSimpleName();
        String group = jobCls.getSimpleName() + "Group";
        return JobBuilder.newJob(jobCls)
                .withIdentity(name,group)
                .storeDurably()// 即使没有Trigger关联时,也不需要删除该JobDetail
                .build();
    }
    /**
     * 定时任务触发器
     */
    private Trigger jobTrigger(Class<? extends Job> jobCls, String cronSchedule){
        String name = jobCls.getSimpleName();
        String group = jobCls.getSimpleName() + "Group";
        return TriggerBuilder.newTrigger()
                .forJob(jobDetail(jobCls))
                .withIdentity(name,group)
                .withSchedule(CronScheduleBuilder.cronSchedule(cronSchedule))
                .build();
    }

}

(4)QuartzSchedule.java

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface QuartzSchedule {
    /**
     * 任务调度表达式
     */
    String cronSchedule() default "";
    /**
     * 任务名称
     */
    String name() default "";
}

(5)QuartzJobLock.java

集群状态需要使用分布式锁

@Slf4j
public abstract class QuartzJobLock extends QuartzJobBean {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 执行定时任务
     */
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        String className = this.getClass().getSimpleName();
        QuartzSchedule annotation = this.getClass().getAnnotation(QuartzSchedule.class);
        if(annotation == null) {
            log.error("该类 {} 未配置QuartzSchedule注解,不执行任务", className);
            return;
        }

        String lockKey = className + "Key";
        boolean lock = tryLock(lockKey);
        if (!lock) {
            log.error(lockKey + " 正在执行");
            return;
        }
        try {
            executeJob();
        } catch (Exception e) {
            log.error(lockKey + " error{}", e);
        } finally {
            unlock(lockKey);
        }

    }

    /**
     * 加锁
     */
    private boolean tryLock(String key) {
        return redisTemplate.opsForValue().setIfAbsent(key, "1", 100, TimeUnit.SECONDS);
    }
​
    /**
     * 解锁
     */
    private void unlock(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 调用具体方法实现业务逻辑
     */
    public abstract void executeJob() ;
}

(5)TestJob.java

每新增一个定时任务,只需要创建一个如下的类

定时任务注解 @QuartzSchedule  name:定时任务名称  cronSchedule:执行计划

@DisallowConcurrentExecution
@Slf4j
@Component
@QuartzSchedule(name = "测试定时任务", cronSchedule = "0 */1 * * * ?")
public class TestJob extends QuartzJobLock {

    @Autowired
    private TestService testService;

    /**
     * 业务逻辑
     */
    @Override
    public void executeJob() {
        testService.doSomething();
    }

}

相关推荐

  1. Quartz定时任务Spring FrameWork整合

    2024-04-11 17:10:01       33 阅读
  2. SpringBoot整合定时任务

    2024-04-11 17:10:01       11 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-11 17:10:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-11 17:10:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-11 17:10:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-11 17:10:01       18 阅读

热门阅读

  1. c#编程基础学习之数据类型

    2024-04-11 17:10:01       13 阅读
  2. [html]网页结构以及常见标签用法

    2024-04-11 17:10:01       13 阅读
  3. C语言程序设计每日一练(1)

    2024-04-11 17:10:01       12 阅读
  4. 在前端开发中用到了哪些设计模式?

    2024-04-11 17:10:01       17 阅读
  5. python - argparse、configparser的用法

    2024-04-11 17:10:01       14 阅读
  6. Python零基础从小白打怪升级中~~~~~~~Python中的函数

    2024-04-11 17:10:01       12 阅读
  7. 深入了解路由交换技术:原理,实践与挑战

    2024-04-11 17:10:01       15 阅读
  8. SSH和telnet的了解

    2024-04-11 17:10:01       12 阅读
  9. FreeRTOS Day4

    2024-04-11 17:10:01       10 阅读