前面介绍的内容我们都是在普通Java工程中使用Quartz框架。在Spring工程中,Spring框架为Quartz提供了一些封装,方便我们使用。这里我们介绍如何将Quartz集成到Spring中。
我们知道Spring本身提供了一种基于JDK的@Scheduled
计划任务功能,和Quartz相比,@Scheduled
的优点是使用非常方便,直接使用注解标注SpringBean的方法即可实现计划任务。当然缺点也很明显,它的功能太过简略,它不支持分布式,而且调度信息也不能持久化。而Quartz支持分布式锁和调度信息存储,任务、触发器都可以动态注册、删除以及进行启停操作,显然其功能比@Scheduled
强大很多,不过缺点则是用起来比较麻烦。
Spring框架对Quartz进行了封装,默认集成到了spring-context
包中,除了Quartz框架本身,不需要再额外引入什么依赖。
Spring提供了JobDetailFactoryBean
,它用于封装JobDetail
并自动注册到Scheduler
,下面是一个例子。
package com.gacfox.cron;
import org.quartz.*;
import org.springframework.scheduling.quartz.QuartzJobBean;
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class DemoJob extends QuartzJobBean {
private Integer count;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
count++;
context.getJobDetail().getJobDataMap().put("count", count);
System.out.println("Job参数:已执行次数 count [" + count + "]");
}
public void setCount(Integer count) {
this.count = count;
}
}
上面代码没有什么特别的,就是我们之前章节编写的一个有状态Job。但要注意,这里我们继承的是Spring提供的QuartzJobBean
基类,这一点和之前不同,其余方法参数等都与之前一致。
<!-- 配置Job -->
<bean id="demoJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.gacfox.cron.DemoJob"/>
<property name="jobDataAsMap">
<map>
<entry key="count" value="0"/>
</map>
</property>
</bean>
Spring配置文件中,我们使用JobDetailFactoryBean
装配了demoJob
这个Bean,其API和之前我们使用Quartz中的JobDetail
是类似的。这里我们指定了Job的类名以及参数。
Spring和Quartz原版的封装层次有些不同,Spring是将Job封装到Trigger里,其他参数均一致。
<!-- 为demoJob配置Trigger -->
<bean id="demoJobCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="demoJob"/>
<property name="cronExpression" value="0/1 * * * * ?"/>
</bean>
下面代码我们通过Spring提供的SchedulerFactoryBean
来创建Scheduler,参数中我们指定了Quartz配置文件位置,以及之前我们创建的所有Trigger。quartz.properties
配置文件的内容可以参考之前的例子。
<!-- 配置Scheduler -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="configLocation" value="classpath:quartz.properties" />
<property name="triggers">
<list>
<ref bean="demoJobCronTrigger"/>
</list>
</property>
</bean>
对于JDBC使用的数据源,我们可以单独使用Quartz默认引入的c3p0,这样便于将Quartz和业务数据划分到两个独立的数据库中互不影响;也可以将其和工程的数据源配置成一个。
如果使用SpringBoot搭建工程,在配置上有一些区别,SpringBoot工程中我们直接引入Quartz的起步依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
在application.properties
配置文件中,我们先配置了SpringBoot工程默认使用的数据源,在之后的Quartz配置中我们没有再额外指定数据源了,此时Quartz会自动使用工程默认的数据源。
application.properties
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=1
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.idle-timeout=60000
spring.datasource.url=jdbc:mysql://127.0.0.1/demoquartz?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.quartz.job-store-type=jdbc
spring.quartz.jdbc.initialize-schema=never
spring.quartz.properties.org.quartz.scheduler.instanceName=QuartzScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
spring.quartz.properties.org.quartz.threadPool.threadCount=10
spring.quartz.properties.org.quartz.threadPool.threadPriority=5
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties
前缀的配置中可编写的配置内容和之前quartz.properties
配置文件中的内容一致,我们在这些配置中指定Quartz的线程池配置、使用集群模式等信息。
QuartzConfig.java
package com.gacfox.bootquartz.config;
import com.gacfox.bootquartz.crontab.DemoJob;
import org.quartz.JobDetail;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
public class QuartzConfig {
@Bean
public JobDetailFactoryBean demoJobDetail() {
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
jobDetailFactoryBean.setJobClass(DemoJob.class);
Map<String, Integer> dataMap = new ConcurrentHashMap<>();
dataMap.put("count", 0);
jobDetailFactoryBean.setJobDataAsMap(dataMap);
jobDetailFactoryBean.setDurability(true);
return jobDetailFactoryBean;
}
@Bean
public CronTriggerFactoryBean demoJobTrigger(JobDetail demoJobDetail) {
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
cronTriggerFactoryBean.setJobDetail(demoJobDetail);
cronTriggerFactoryBean.setCronExpression("0/1 * * * * ?");
return cronTriggerFactoryBean;
}
}
QuartzConfig
配置类的作用和前面XML配置中的一致,其中我们配置了JobDetail
和JobTrigger
这两个Bean,JobDetail
中指定了Job的实现类。DemoJob
类的内容和前面完全相同,这里就不重复粘贴了。
如果希望Quartz使用单独的数据库,可以在SpringBoot中使用@QuartzDataSource
注解标注数据源Bean的配置类,这样就可以实现为Quartz单独指定数据源了。
@Bean
@QuartzDataSource
@ConfigurationProperties(prefix = "spring.datasource.qrtzDataSource")
public DataSource quartzDataSource(){
// ... 创建数据源
}