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

前言

我们在开发中经常会遇到一些业务,需要通过定时任务去实现,我们可以通过SpringBoot+Quartz来实现,原始的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() ;
}

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

}

  • 31
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
quartz框架是一个开源的Java调度框架,可用于编写定时任务。而Spring Boot是一个用于简化Java应用开发的框架。在将quartz框架与Spring Boot整合时,我们可以通过使用new的方式来编写定时任务。 首先,在Spring Boot的主配置类上加上@EnableScheduling注解,以启用定时任务支持。然后,我们可以创建一个新的类并实现org.springframework.scheduling.annotation.SchedulingConfigurer接口,这个接口提供了定时任务的配置信息。在该类中,我们可以使用@Bean注解来定义一个定时任务,并设置其触发时间、逻辑等。 在定义定时任务的方法上,我们可以使用@Scheduled注解来指定任务的执行方式和触发时间。例如,可以使用@Scheduled(fixedRate = 5000)来表示每隔5秒执行一次该任务,或者使用@Scheduled(cron = "0 0 12 * * ?")来表示每天中午12点执行一次任务。 需要注意的是,在使用new的方式编写定时任务时,我们需要手动将该任务注册到quartz框架中。我们可以在Spring Boot的配置类中通过创建org.springframework.scheduling.quartz.SchedulerFactoryBean对象来获取一个Quartz调度器实例。然后,再使用调度器实例的scheduleJob方法来注册定时任务。 总的来说,通过new的方式编写定时任务的步骤如下: 1. 在Spring Boot的主配置类上加上@EnableScheduling注解,启用定时任务支持。 2. 创建一个新的类并实现SchedulingConfigurer接口,用于配置定时任务。 3. 在配置类中定义定时任务的方法,并使用@Scheduled注解指定任务的执行方式和触发时间。 4. 在配置类中获取Quartz调度器实例,并使用调度器实例的scheduleJob方法来注册定时任务。 这样,我们就可以使用new的方式编写定时任务,并实现quartz框架与Spring Boot整合
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值