详解Spring整合Quartz实现动态定时任务

当我们需要实现一些动态的、可配置的任务调度,比如定时发送邮件、定时生成报表,我们可以借助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技术站

(0)
上一篇 2023年6月15日
下一篇 2023年6月15日

相关文章

  • java读取txt文件并输出结果

    下面是“Java读取txt文件并输出结果”的完整攻略: 1. 读取txt文件 1.1 创建File对象 首先,我们需要创建一个File对象,用来指定要读取的txt文件的路径及文件名。例如,读取名为example.txt的文件,代码如下: File file = new File("example.txt"); 1.2 创建FileRead…

    Java 2023年5月26日
    00
  • java 中volatile和lock原理分析

    这是一篇关于Java中volatile和lock原理分析的完整攻略。在本文中,我们将逐一介绍这两个关键字的定义以及它们的使用。我们将会使用到两个具体的示例来说明这两个关键字的使用方法以及背后的原理。 Volatile 定义 先来看一下volatile。volatile是Java中的关键字,用于修饰变量。它的主要作用是保证在多线程环境下,某个被volatile…

    Java 2023年5月26日
    00
  • Java实现指定目录下的文件查找详解

    下面开始讲解“Java实现指定目录下的文件查找详解”的攻略。 1. 需求背景 很多时候,我们需要查找指定目录下的某个或某些文件,这时候我们可以借助Java提供的API来实现。本文主要讲解Java如何实现指定目录下的文件查找。 2. 实现步骤 具体实现步骤如下: 2.1. 获取目录下所有的文件和子目录 我们可以使用Java提供的File类的listFiles(…

    Java 2023年5月19日
    00
  • 微信小程序实现走马灯效果实例

    下面我将为您详细讲解“微信小程序实现走马灯效果实例”的完整攻略,包含以下部分: 项目介绍 预备工作 代码实现 示例说明 项目介绍 在微信小程序中,有一个常用的功能就是走马灯效果,可以用来展示一些动态信息或者广告等内容。本项目将演示如何在微信小程序中实现走马灯效果。 预备工作 在开始本项目之前,您需要准备以下环境和工具: 微信开发者工具 一台可以运行微信开发者…

    Java 2023年5月23日
    00
  • Java 队列实现原理及简单实现代码

    下面就详细讲解“Java队列实现原理及简单实现代码”的完整攻略。 队列基本概念 在讲解队列的实现原理和代码之前,先了解一下队列的基本概念: 队列(Queue)是一种先进先出(FIFO,First In First Out)的数据结构。它可以用链表或数组来实现。队列在计算机中广泛应用,例如在操作系统、网络通信、数据库系统等方面经常被使用。 在队列中,新的元素插…

    Java 2023年5月18日
    00
  • Java的Hibernate框架中一对多的单向和双向关联映射

    Java的Hibernate框架中,一对多关联映射通常用于表示两个表之间的一对多关系。在这种关系中,一个“一”的实体可以关联多个“多”的实体。Hibernate框架支持单向和双向的一对多关联映射。 单向一对多关联映射 在Hibernate框架中,单向一对多关联映射通常是通过在多的一方中定义对一方的外键来实现的。以下是一个示例: 定义“一”的实体 @Entit…

    Java 2023年5月31日
    00
  • js内置对象 学习笔记

    我们来详细讲解一下JS内置对象的学习笔记。 标准内置对象 JS内置对象指的是在JS语言标准中定义的对象,以全局作用域之下的属性形式存在。可以分为以下几类: 基本数据类型(Number、String、Boolean、Symbol、BigInt、null、undefined) 内置对象(Array、Date、RegExp、Promise、Proxy、Map、Se…

    Java 2023年5月26日
    00
  • JAVA实战项目实现客户选购系统详细流程

    JAVA实战项目实现客户选购系统详细流程攻略 系统需求分析 客户选购系统是一个基于Web的在线应用程序。通过该系统客户可以在网上浏览商品并进行购买。系统需要满足以下需求: 提供商品浏览功能,客户可以浏览商品分类和商品详细信息。 提供购物车管理功能,客户可以将商品加入购物车,修改购物车中商品数量,删除购物车中商品等。 提供订单管理功能,客户可以查看自己的订单、…

    Java 2023年5月30日
    00
合作推广
合作推广
分享本页
返回顶部