当我们需要实现一些动态的、可配置的任务调度,比如定时发送邮件、定时生成报表,我们可以借助Quartz框架来实现。Spring框架本身对Quartz的支持也非常友好,本文旨在介绍如何使用Spring整合Quartz实现动态定时任务的详细攻略。
1. 引入依赖
我们需要在项目中引入Spring和Quartz框架的相关依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.4</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
2. 配置Quartz
我们需要在Spring配置文件中添加Quartz的相关配置:
<!-- Quartz调度器 -->
<bean name="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobFactory">
<bean class="org.springframework.scheduling.quartz.SpringBeanJobFactory"/>
</property>
<property name="autoStartup" value="true"/>
<property name="dataSource" ref="dataSource"/>
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">MyScheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<prop key="org.quartz.threadPool.threadCount">10</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.MySQLDelegate</prop>
<prop key="org.quartz.jobStore.dataSource">dataSource</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.plugin.triggHistory.class">org.quartz.plugins.history.LoggingTriggerHistoryPlugin</prop>
</props>
</property>
</bean>
<!-- 定义job和trigger -->
<bean id="myJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.example.MyJob"/>
<property name="durability" value="true"/>
<property name="requestsRecovery" value="true"/>
</bean>
<bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="myJobDetail"/>
<property name="cronExpression" value="0/5 * * * * ?"/>
<property name="startDelay" value="1000"/>
</bean>
<!-- 注册job和trigger到调度器中 -->
<bean id="scheduler" factory-bean="schedulerFactory" factory-method="getScheduler">
<lookup-method name="getScheduler" bean="schedulerFactory"/>
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="myJobDetail"/>
</list>
</property>
<property name="triggers">
<list>
<ref bean="myTrigger"/>
</list>
</property>
</bean>
在这个配置文件中,我们定义了Quartz的调度器,然后定义了一个job和trigger。其中job是实现了Quartz框架中的Job接口的Java类,而trigger则由CronTriggerFactoryBean来创建。最后,我们将job和trigger注册到调度器中。
3. 实现Job接口
我们需要实现Job接口来定义我们的定时任务。以发送邮件为例,我们可以创建一个名为SendMailJob
的类,实现Job
接口,然后在execute
方法中编写发送邮件的逻辑:
public class SendMailJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
// 实现发送邮件的逻辑
}
}
在execute
方法中,我们可以编写发送邮件的逻辑,这里涉及到JavaMail和SpringMail的相关知识,具体可以参考官方文档。
4. 动态创建Job和Trigger
在上面的例子中,我们是静态地在Spring配置文件中定义了一个job和一个trigger,我们可以通过代码动态地创建更多的job和trigger。例如,我们可以通过用户前端页面填写定时发送邮件的时间和收件人等信息,然后将这些信息存到数据库中。接着,在程序启动时,我们可以读取数据库中已经保存的信息,动态地创建对应的job和trigger。
下面是一个示例代码,通过读取数据库中保存的信息,实现动态创建job和trigger的逻辑:
public class JobManager {
private static Scheduler scheduler;
@Autowired
private DataSource dataSource;
@PostConstruct
public void init() {
try {
StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
schedulerFactory.initialize(getQuartzProperties());
scheduler = schedulerFactory.getScheduler();
scheduler.setJobFactory(new SpringBeanJobFactory());
scheduler.start();
// 从数据库中读取已经保存的定时任务信息
List<JobInfo> jobInfos = readJobInfosFromDB();
for (JobInfo jobInfo : jobInfos) {
JobKey jobKey = new JobKey(jobInfo.getName(), jobInfo.getGroup());
if (!scheduler.checkExists(jobKey)) {
// 创建job
JobDetail jobDetail = JobBuilder.newJob(SendMailJob.class)
.withIdentity(jobKey)
.usingJobData(new JobDataMap(jobInfo.getJobData()))
.storeDurably()
.build();
scheduler.addJob(jobDetail, true);
// 创建trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobInfo.getName(), jobInfo.getGroup())
.startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND)) // 延迟1秒执行
.withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression()))
.build();
scheduler.scheduleJob(trigger);
}
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
// 从数据库中读取已经保存的定时任务信息
private List<JobInfo> readJobInfosFromDB() {
// 从数据库中读取job和trigger的相关信息,比如name、group、cron表达式、jobDataMap等
// 返回一个JobInfo对象的List
}
// 获取Quartz的配置信息
private Properties getQuartzProperties() {
Properties properties = new Properties();
properties.put("org.quartz.scheduler.instanceName", "Scheduler");
properties.put("org.quartz.scheduler.instanceId", "AUTO");
properties.put("org.quartz.threadPool.threadCount", "10");
properties.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
properties.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.MySQLDelegate");
properties.put("org.quartz.jobStore.dataSource", "myDataSource");
properties.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
properties.put("org.quartz.plugin.triggHistory.class", "org.quartz.plugins.history.LoggingTriggerHistoryPlugin");
return properties;
}
// 获取Quartz的DataSource
@Bean
public DataSource myDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
在这个示例代码中,我们通过调用readJobInfosFromDB
方法读取数据库中已经保存的定时任务信息,然后通过调用Quartz的API动态地创建job和trigger。其中,JobInfo
是一个自定义的Java类,用于封装从数据库中读取出来的job和trigger的相关信息(比如名称、调用频率等)。
5. 示例说明
这里我们以定时发送邮件为例,给出一个完整的示例说明。假设我们需要实现一个定时发送邮件的功能,可以通过前端页面输入需要发送的内容、收件人邮箱、发送的时间等参数。程序根据这些参数创建Job和Trigger,并且保证在设定的时间到达时执行发送邮件的逻辑。
首先,我们需要在数据库中创建一个表,用于保存定时任务的信息。我们可以手动创建一个名为job_detail
的表,表结构如下:
字段名 | 数据类型 | 描述 |
---|---|---|
name | varchar(100) | Job的名称 |
group | varchar(100) | Job所属的分组 |
cron_expression | varchar(100) | Cron表达式,用于描述Job的触发时间 |
job_data | text | 存储Job的数据,比如要发送的邮件内容、收件人等信息 |
接着,在项目的配置文件中,我们需要添加Quartz相关的配置,如下所示:
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- 配置quartz -->
<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobFactory">
<bean class="org.springframework.scheduling.quartz.SpringBeanJobFactory"/>
</property>
<property name="autoStartup" value="true"/>
<property name="dataSource" ref="dataSource"/>
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">MyScheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<prop key="org.quartz.threadPool.threadCount">10</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.MySQLDelegate</prop>
<prop key="org.quartz.jobStore.dataSource">dataSource</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.jobStore.isClustered">false</prop>
<prop key="org.quartz.plugin.triggHistory.class">org.quartz.plugins.history.LoggingTriggerHistoryPlugin</prop>
</props>
</property>
</bean>
<!-- 配置job -->
<bean id="sendMailJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.example.SendMailJob"/>
<property name="durability" value="true"/>
<property name="requestsRecovery" value="true"/>
</bean>
<!-- 配置trigger -->
<bean id="sendMailTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="sendMailJobDetail"/>
<property name="cronExpression" value="0/5 * * * * ?"/>
<property name="startDelay" value="1000"/>
</bean>
<!-- 注册job和trigger到调度器中 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="sendMailJobDetail"/>
</list>
</property>
<property name="triggers">
<list>
<ref bean="sendMailTrigger"/>
</list>
</property>
</bean>
<!-- 创建JobManager,用于动态创建和部署job和trigger -->
<bean id="jobManager" class="com.example.JobManager">
<property name="dataSource" ref="dataSource"/>
</bean>
在这个示例配置文件中,我们定义了一个名为sendMailJobDetail
的job和对应的sendMailTrigger
。同时,我们在配置文件中添加了JobManager
这个Java类的定义,用于后续的动态创建job和trigger操作。
最后,我们来看看如何实现动态创建Job和Trigger的代码:
首先,我们需要在Java类中定义一个名为JobInfo
的类,用于封装从数据库中读取出来的job和trigger的相关信息。具体代码如下:
public class JobInfo {
private String name; // job的名称
private String group; // job所属的分组
private String cronExpression; // cron表达式,用于描述job的触发时间
private Map<String, Object> jobData; // 存储job的数据,比如要发送邮件的内容、收件人等信息
// 省略getter和setter
}
接着,我们来实现JobManager
类中的代码逻辑:
public class JobManager {
private static Scheduler scheduler;
@Autowired
private DataSource dataSource;
@PostConstruct
public void init() {
try {
StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
schedulerFactory.initialize(getQuartzProperties());
scheduler = schedulerFactory.getScheduler();
scheduler.setJobFactory(new SpringBeanJobFactory());
scheduler.start();
// 从数据库中读取已经保存的定时任务信息
List<JobInfo> jobInfos = readJobInfosFromDB();
for (JobInfo jobInfo : jobInfos) {
JobKey jobKey = new JobKey(jobInfo.getName(), jobInfo.getGroup());
if (!scheduler.checkExists(jobKey)) {
// 创建job
JobDetail jobDetail = JobBuilder.newJob(SendMailJob.class)
.withIdentity(jobKey)
.usingJobData(new JobDataMap(jobInfo.getJobData()))
.storeDurably()
.build();
scheduler.addJob(jobDetail, true);
// 创建trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobInfo.getName(), jobInfo.getGroup())
.startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND)) // 延迟1秒执行
.withSchedule(CronScheduleBuilder.cronSchedule(jobInfo.getCronExpression()))
.build();
scheduler.scheduleJob(trigger);
}
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
// 从数据库中读取已经保存的定时任务信息
private List<JobInfo> readJobInfosFromDB() {
List<JobInfo> jobInfos = new ArrayList<>();
try (Connection con = dataSource.getConnection()) {
PreparedStatement ps = con.prepareStatement("select name,group,cron_expression,job_data from job_detail");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
JobInfo jobInfo = new JobInfo();
jobInfo.setName(rs.getString("name"));
jobInfo.setGroup(rs.getString("group"));
jobInfo.setCronExpression(rs.getString("cron_expression"));
jobInfo.setJobData(new ObjectMapper().readValue(rs.getString("job_data"), Map.class));
jobInfos.add(jobInfo);
}
} catch (Exception e) {
e.printStackTrace();
}
return jobInfos;
}
// 获取Quartz的配置信息
private Properties getQuartzProperties() {
Properties properties = new Properties();
properties.put("org.quartz.scheduler.instanceName", "Scheduler");
properties.put("org.quartz.scheduler.instanceId", "AUTO");
properties.put("org.quartz.threadPool.threadCount", "10");
properties.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
properties.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.MySQLDelegate");
properties.put("org.quartz.jobStore.dataSource", "myDataSource");
properties.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
properties.put("org.quartz.plugin.triggHistory.class", "org.quartz.plugins.history.LoggingTriggerHistoryPlugin");
return properties;
}
// 获取Quartz的DataSource
@Bean
public DataSource myDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
在这个示例代码中,JobManager
类主要实现了三个方法:
init
方法:在程序启动时调用,用于初始化Quartz的调度器和Trigger、Job等信息。readJobInfosFromDB
方法:从数据库中读取保存的Job和Trigger的相关信息,并转化为Java对象返回。- `getQuartzProperties
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Spring整合Quartz实现动态定时任务 - Python技术站