详解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代理模式(jdk proxy)

    Java代理模式(JDK Proxy)攻略 Java代理模式是一种非常常用的设计模式,它可以为某个对象提供一个代理对象,在代理对象中对目标对象进行增强、控制或者调整,而不用改变原有的对象和代码。该模式可以在不改变原有代码基础上,增强代码的功能和控制,从而实现特定的需求。 代理模式的使用场景 代理模式在实际开发过程中有着广泛的应用,一些常见的场景如下: 远程代…

    Java 2023年5月23日
    00
  • JNI语言基本知识

    JNI(Java Native Interface)是Java虚拟机提供的一个接口,允许Java程序调用本地C/C++方法或者本地C/C++程序调用Java方法。在进行JNI开发时,需要了解JNI语言的一些基本知识,下面是详细攻略: JNI语言基本知识 1. JNI开发环境搭建 在进行JNI开发前,需要安装C/C++ 编译器和Java开发工具包(JDK)。同…

    Java 2023年5月26日
    00
  • 基于Java字符串 “==” 与 “equals” 的深入理解

    当我们在Java中使用字符串时,经常会遇到判断两个字符串是否相等的情况。在这种情况下,通常有两种方式进行比较:使用 “==” 或者使用 “equals”。然而,这两种方式有什么不同?为什么我们不能总是使用 “==” 进行比较? “==” 和 “equals” 的区别 在Java中,”==” 运算符用于比较两个对象是否是同一个对象,即它们是否指向内存中的同一个…

    Java 2023年5月27日
    00
  • java实现秒表功能

    下面是Java实现秒表功能的完整攻略: 确定界面布局 首先,要确定秒表界面的布局,可以使用Swing包提供的JFrame、JLabel等组件。 一个简单的秒表GUI界面可以包含如下控件: 用于显示秒数的JLabel。 用于开始、停止、清零等操作的JButton。 (可选)用于记录计时时间的列表或文本框。 编写计时器逻辑 初始秒表面板布局完成后,需要编写计时器…

    Java 2023年5月20日
    00
  • 在Windows10中安装TomCat的教程图解

    以下是“在Windows10中安装TomCat的教程图解”的完整攻略: 准备工作 在安装Tomcat之前,需要先确保以下几点: 确认Windows10已经安装了Java开发环境(JDK)。可以到Oracle官网下载JDK,也可以通过搜索引擎搜索其他可信的JDK下载地址。 下载Tomcat安装包。可以到Tomcat官网下载最新的Tomcat安装包,也可以通过搜…

    Java 2023年5月19日
    00
  • SpringMVC如何自定义响应的HTTP状态码

    SpringMVC如何自定义响应的HTTP状态码 在 Spring MVC 中,我们可以自定义响应的 HTTP 状态码。自定义 HTTP 状态码可以帮助我们更好地处理请求和响应,提高 Web 应用程序的可读性和可维护性。本文将详细讲解 SpringMVC 如何自定义响应的 HTTP 状态码,包括如何使用 @ResponseStatus 注解和如何使用 Res…

    Java 2023年5月18日
    00
  • SpringBoot依赖管理的源码解析

    SpringBoot依赖管理的源码解析 SpringBoot依赖管理包括以下三个方面:依赖注入、自动配置、起步依赖。 1.1 依赖注入 SpringBoot采用了Java注解的方式来实现依赖注入,主要包括以下注解: @Autowired:自动装配,SpringBoot会自动将Bean注入到需要的地方; @Component:将当前类标记为Spring Bea…

    Java 2023年5月19日
    00
  • 解决JMap抓取heap使用统计信息报错的问题

    下面我就来详细讲解如何解决JMap抓取heap使用统计信息报错的问题。 背景 在使用JMap命令抓取Java应用程序Heap使用统计信息时,可能会遇到以下报错信息: Error: Unable to perform heap dump on unreachable object 该错误通常表示JMap已经找不到对应的对象,导致无法进行Heap Dump操作。…

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