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

yizhihongxing

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

    下面是JAVA实现打印ASCII码表的完整攻略: 步骤一:了解ASCII码表 ASCII码(American Standard Code for Information Interchange 美国信息交换标准代码)是一种字符编码方式,使用数字127来表示128个字符(包括字母、数字和符号),它们分别对应不同的ASCII码值。了解ASCII码表对于编写打印A…

    Java 2023年5月23日
    00
  • Spring通过Java配置集成Tomcat的方法

    下面我来详细讲解“Spring通过Java配置集成Tomcat的方法”的完整攻略,首先需要明确以下几个步骤: 导入相关依赖库; 编写Spring配置文件; 编写Java配置类; 启动Tomcat服务器。 下面我会逐一讲解每一个步骤,并提供两个示例供参考。 1. 导入相关依赖库 在项目的pom.xml或build.gradle文件中加入以下依赖库: <!…

    Java 2023年5月19日
    00
  • Java用递归方法解决汉诺塔问题详解

    Java用递归方法解决汉诺塔问题详解 问题描述 汉诺塔问题的经典描述是:在有三根柱子的情况下,有三个大小不同的盘子从下往上按从大到小的顺序放在柱子A上,要将这三个盘子移动到柱子C上,要求每次只能移动一个盘子,且大盘子不能放在小盘子上面。 解题思路 汉诺塔问题是递归问题的典型,使用递归可以比较简单地解决该问题。 我们可以将解决汉诺塔问题的方法抽象为三个步骤: …

    Java 2023年5月19日
    00
  • Java编码算法与哈希算法深入分析使用方法

    Java编码算法与哈希算法深入分析使用方法攻略 什么是编码算法? 编码算法是一种将数据从一种格式或表示方式转换为另一种格式或表示方式的算法。在Java编程中,常见的编码算法有Base64,URL编码以及HTML编码等等。 Base64编码 Base64编码是一种将二进制数据转换为可打印的ASCII字符的编码方式。Base64编码将数据每3个字节一组进行编码,…

    Java 2023年5月19日
    00
  • 详解 Java中日期数据类型的处理之格式转换的实例

    下面是详解Java中日期数据类型的处理之格式转换的实例的完整攻略。 前言 在Java中,常用的日期数据类型有java.util.Date和java.time.LocalDateTime等。在处理日期时,我们有时需要把日期按照一定的格式进行转换,例如将2022-01-01转换成2022年01月01日。本文将对Java中日期格式转换进行详细讲解,并提供具体的代码…

    Java 2023年5月20日
    00
  • Java实现Kafka生产者和消费者的示例

    下面我会分步骤详细讲解如何使用Java实现Kafka生产者和消费者的示例。在这个过程中,我将会使用两个实例来演示具体的实现过程。 准备工作 在开始之前,请确保你已经完成了以下准备工作: 安装了Kafka集群和ZooKeeper 具备Java编程基础 示例一:Kafka生产者 1. 引入Kafka依赖 首先,我们需要在项目中引入Kafka的依赖。可以使用Mav…

    Java 2023年5月20日
    00
  • Spring-IOC容器中的常用注解与使用方法详解

    Spring-IOC容器中的常用注解与使用方法详解 在Spring框架中,IOC容器是一个非常重要的组件,它负责管理应用程序中的所有Bean对象。在本文中,我们将介绍Spring-IOC容器中的常用注解及其使用方法,并提供两个示例说明。 常用注解 @Component @Component注解是Spring框架中最基本的注解之一,它用于将一个Java类声明为…

    Java 2023年5月18日
    00
  • Java下载远程服务器文件到本地(基于http协议和ssh2协议)

    Java下载远程服务器文件到本地(基于http协议和ssh2协议) 在Java编程中,我们经常需要从远程服务器下载文件到本地。这篇文章将介绍如何使用Java实现基于http协议和ssh2协议的文件下载操作。 基于HTTP协议下载文件 使用Java下载http协议的文件,我们可以使用Java中自带的URL和URLConnection类。 下面是一个示例代码,它…

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