springboot整合quartz

简介

Quartz是一款功能强大的任务调度器,可以实现较为复杂的调度功能,如每月一号执行、每天凌晨执行、每周五执行等等,还支持分布式调度。本文使用Springboot+Mybatis+Quartz实现对定时任务的增、删、改、查、启用、停用等功能。并把定时任务持久化到数据库以及支持集群。

Quartz的3个基本要素

1.Scheduler:调度器。所有的调度都是由它控制。

2.Trigger: 触发器。决定什么时候来执行任务。

3.JobDetail & Job: JobDetail定义的是任务数据,而真正的执行逻辑是在Job中。使用JobDetail + Job而不是Job,这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。

如何使用Quartz

1.首先需要到quartz官网下载quartz包,http://www.quartz-scheduler.org/downloads/,我这边下载的是2.2.3的版本,解压后执行\quartz-2.2.3\docs\dbTables下的数据库建表语句,我这边用的是mysql,因此执行tables_mysql_innodb.sql文件

2.添加maven依赖:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.3</version>
    <exclusions>
        <exclusion>
            <artifactId>slf4j-api</artifactId>
            <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
    <version>2.2.3</version>
    <exclusions>
        <exclusion>
             <artifactId>slf4j-api</artifactId>
             <groupId>org.slf4j</groupId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
</dependency>

3.创建配置文件
在maven项目的resource目录下创建quartz.properties

org.quartz.scheduler.instanceName=quartzScheduler
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.rmi.export=false
org.quartz.scheduler.rmi.proxy=false
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false
org.quartz.jobStore.misfireThreshold=50000
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.useProperties=false
org.quartz.jobStore.clusterCheckinInterval=20000
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=10
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true

4.数据原配置

@Configuration
public class DataSourceConfig
{
    @Value("${spring.datasource.url}")
    private String url;

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Value("${spring.datasource.username}")
    private String userName;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.initialSize}")
    private int initialSize;

    @Value("${spring.datasource.minIdle}")
    private int minIdle;

    @Value("${spring.datasource.maxActive}")
    private int maxActive;

    @Value("${spring.datasource.maxWait}")
    private int maxWait;

    @Value("${spring.datasource.timeBetweenEvictionRunsMilli}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.validationQuery}")
    private String validationQuery;

    @Value("${spring.datasource.testWhileIdle}")
    private boolean testWhileIdle;

    @Value("${spring.datasource.testOnBorrow}")
    private boolean testOnBorrow;

    @Value("${spring.datasource.testOnReturn}")
    private boolean testOnReturn;

    @Value("${spring.datasource.poolPreparedStatements}")
    private boolean poolPreparedStatements;

    @Value("${spring.datasource.connectionProperties}")
    private String properties;

    @Bean
    public DataSource dataSource()
    {
        DruidDataSource configDataSource = new DruidDataSource();
        configDataSource.setUrl(url);
        configDataSource.setDriverClassName(driverClassName);
        configDataSource.setUsername(userName);
        configDataSource.setPassword(password);
        configDataSource.setInitialSize(initialSize);
        configDataSource.setDefaultAutoCommit(true);
        configDataSource.setMinIdle(minIdle);
        configDataSource.setMaxActive(maxActive);
        configDataSource.setMaxWait(maxWait);
        configDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        configDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        configDataSource.setValidationQuery(validationQuery);
        configDataSource.setTestWhileIdle(testWhileIdle);
        configDataSource.setTestOnBorrow(testOnBorrow);
        configDataSource.setTestOnReturn(testOnReturn);
        configDataSource.setConnectionProperties(properties);

        return configDataSource;
    }
}

5.quartz配置

首先定义一个jobfactory,继承org.springframework.scheduling.quartz.SpringBeanJobFactory,实现任务实例化方式。

public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory
    implements ApplicationContextAware
{
    private transient AutowireCapableBeanFactory beanFactory;

    @Override
    public void setApplicationContext(final ApplicationContext context)
    {
        beanFactory = context.getAutowireCapableBeanFactory();
    }

    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle)
        throws Exception
    {
        final Object job = super.createJobInstance(bundle);

        beanFactory.autowireBean(job);

        return job;
    }
}

然后对调度器做一些配置,将之前的数据源配置,jobfactory都注入spring ioc。

@EnableScheduling
@Configuration
public class SchedulerConfig
{
    @Autowired
    private DataSource dataSource;

    @Bean
    public JobFactory jobFactory(ApplicationContext applicationContext)
    {
        AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();

        jobFactory.setApplicationContext(applicationContext);

        return jobFactory;
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory,
                                                     Trigger simpleJobTrigger)
        throws IOException
    {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();

        factory.setJobFactory(jobFactory);
        factory.setQuartzProperties(quartzProperties());
        factory.setTriggers(simpleJobTrigger);
        factory.setDataSource(dataSource);
        factory.setWaitForJobsToCompleteOnShutdown(
            true);//这样当spring关闭时,会等待所有已经启动的quartz job结束后spring才能完全shutdown。
        factory.setOverwriteExistingJobs(false);
        factory.setStartupDelay(1);
        //设置调度器自动运行
        factory.setAutoStartup(true);
        //设置上下文spring bean name
        factory.setApplicationContextSchedulerContextKey("applicationContext");

        return factory;
    }

    /**
     * 静态方式配置定时任务
     *
     * @param jobDetail
     * @return
     */
    @Bean
    public CronTriggerFactoryBean simpleJobTrigger(
        @Qualifier("simpleJobDetail") JobDetail jobDetail)
    {
        CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();

        factoryBean.setJobDetail(jobDetail);
        factoryBean.setStartDelay(1000L);
        factoryBean.setName("trigger1");
        factoryBean.setGroup("group1");
        //周1至周5,每天上午8点至下午18点,每分钟执行一次
        factoryBean.setCronExpression("0 0/1 8-18 ? * 2-6");

        return factoryBean;
    }

    @Bean
    public JobDetailFactoryBean simpleJobDetail()
    {
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();

        factoryBean.setJobClass(ScheduledJob.class);
        factoryBean.setGroup("group1");
        factoryBean.setName("job1");

        return factoryBean;
    }

    @Bean
    public Properties quartzProperties()
        throws IOException
    {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();

        propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));

        propertiesFactoryBean.afterPropertiesSet();

        return propertiesFactoryBean.getObject();
    }
}

定义一个监听器,用于在任务执行前后做一些操作:
 

public class SchedulerListener implements JobListener
{
    public static final String LISTENER_NAME = "QuartSchedulerListener";

    @Override
    public String getName()
    {
        return LISTENER_NAME; //must return a name
    }

    //任务被调度前
    @Override
    public void jobToBeExecuted(JobExecutionContext context)
    {

        String jobName = context.getJobDetail().getKey().toString();
        System.out.println("jobToBeExecuted");
        System.out.println("Job : " + jobName + " is going to start...");

    }

    //任务调度被拒了
    @Override
    public void jobExecutionVetoed(JobExecutionContext context)
    {
        System.out.println("jobExecutionVetoed");
        //可以做一些日志记录原因

    }

    //任务被调度后
    @Override
    public void jobWasExecuted(JobExecutionContext context,
                               JobExecutionException jobException)
    {
        System.out.println("jobWasExecuted");

        String jobName = context.getJobDetail().getKey().toString();
        System.out.println("Job : " + jobName + " is finished...");

        if (jobException != null && !jobException.getMessage().equals(""))
        {
            System.out.println("Exception thrown by: " + jobName
                               + " Exception: " + jobException.getMessage());
        }
    }
}

定义一个具体的job,用于实现具体的业务逻辑:

public class ScheduledJob implements Job
{

    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledJob.class);

    @Override
    public void execute(JobExecutionContext jobExecutionContext)
        throws JobExecutionException
    {

        //执行任务逻辑....
        LOGGER.info("执行自定义定时任务, time is {}.", new Date());
    }
}

定义一个管理类,用于实现任务的创建,删除等操作:

@Component
public class SchedulerManager
{
    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;

    private JobListener scheduleListener;

    /**
     * 开始定时任务
     *
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     */
    public void startJob(String cron, String jobName, String jobGroup,
                         Class<? extends Job> jobClass)
        throws SchedulerException
    {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        if (scheduleListener == null)
        {
            scheduleListener = new SchedulerListener();
            scheduler.getListenerManager().addJobListener(scheduleListener);
        }
        JobKey jobKey = new JobKey(jobName, jobGroup);
        if (!scheduler.checkExists(jobKey))
        {
            scheduleJob(cron, scheduler, jobName, jobGroup, jobClass);
        }
    }

    /**
     * 移除定时任务
     *
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     */
    public void deleteJob(String jobName, String jobGroup)
        throws SchedulerException
    {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = new JobKey(jobName, jobGroup);
        scheduler.deleteJob(jobKey);
    }

    /**
     * 暂停定时任务
     *
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     */
    public void pauseJob(String jobName, String jobGroup)
        throws SchedulerException
    {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = new JobKey(jobName, jobGroup);
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复定时任务
     *
     * @param jobName
     * @param jobGroup
     * @throws SchedulerException
     */
    public void resumeJob(String jobName, String jobGroup)
        throws SchedulerException
    {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey triggerKey = new JobKey(jobName, jobGroup);
        scheduler.resumeJob(triggerKey);
    }

    /**
     * 清空所有当前scheduler对象下的定时任务【目前只有全局一个scheduler对象】
     *
     * @throws SchedulerException
     */
    public void clearAll()
        throws SchedulerException
    {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        scheduler.clear();
    }

    /**
     * 动态创建Job
     * 此处的任务可以配置可以放到properties或者是放到数据库中
     * Trigger:name和group 目前和job的name、group一致,之后可以扩展归类
     *
     * @param scheduler
     * @throws SchedulerException
     */
    private void scheduleJob(String cron, Scheduler scheduler, String jobName, String jobGroup,
                             Class<? extends Job> jobClass)
        throws SchedulerException
    {
        /*
         *  此处可以先通过任务名查询数据库,如果数据库中存在该任务,更新任务的配置以及触发器
         *  如果此时数据库中没有查询到该任务,则按照下面的步骤新建一个任务,并配置初始化的参数,并将配置存到数据库中
         */
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).build();
        // 每5s执行一次
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName,
            jobGroup).withSchedule(scheduleBuilder).build();

        scheduler.scheduleJob(jobDetail, cronTrigger);
    }
}

最后定义一个controller,用于出发定时任务:

@RestController
@RequestMapping("/quartz")
public class QuartzController
{
    @Autowired
    public SchedulerManager myScheduler;

    @RequestMapping(value = "/job2", method = RequestMethod.GET)
    public String scheduleJob2()
    {
        try
        {
            myScheduler.startJob("0/5 * * * * ?", "job2", "group2", ScheduledJob.class);//每五秒执行一次
            return "启动定时器成功";
        }
        catch (SchedulerException e)
        {
            e.printStackTrace();
        }
        return "启动定时器失败";
    }

    @RequestMapping(value = "/del_job2", method = RequestMethod.GET)
    public String deleteScheduleJob2()
    {
        try
        {
            myScheduler.deleteJob("job2", "group2");
            return "删除定时器成功";
        }
        catch (SchedulerException e)
        {
            e.printStackTrace();
        }
        return "删除定时器失败";
    }
}

这样通过页面的调用就能出发定时任务完成特定的业务逻辑了。

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值