springBoot-Quartz 定时任务

1.1 Quartz 概述

Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现。该项目于 2009 年被 Terracotta 收购,目前是 Terracotta 旗下的一个项目。读者可以到 http://www.quartz-scheduler.org/站点下载 Quartz 的发布版本及其源代码。

1.2 Quartz特点

作为一个优秀的开源调度框架,Quartz 具有以下特点:

强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;
灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;
分布式和集群能力,Terracotta 收购后在原来功能基础上作了进一步提升。
另外,作为 Spring 默认的调度框架,Quartz 很容易与 Spring 集成实现灵活可配置的调度功能。

quartz调度核心元素:

  • Scheduler:任务调度器,是实际执行任务调度的控制器。在spring中通过SchedulerFactoryBean封装起来。
  • Trigger:触发器,用于定义任务调度的时间规则,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比较多,本文主要介绍这种方式。CronTrigger在spring中封装在CronTriggerFactoryBean中。
  • Calendar:它是一些日历特定时间点的集合。一个trigger可以包含多个Calendar,以便排除或包含某些时间点。
  • JobDetail:用来描述Job实现类及其它相关的静态信息,如Job名字、关联监听器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean两种实现,如果任务调度只需要执行某个类的某个方法,就可以通过MethodInvokingJobDetailFactoryBean来调用。
  • Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。实现Job接口的任务,默认是无状态的,若要将Job设置成有状态的,在quartz中是给实现的Job添加@DisallowConcurrentExecution注解(以前是实现StatefulJob接口,现在已被Deprecated),在与spring结合中可以在spring配置文件的job detail中配置concurrent参数。

1.3 Quartz 集群配置

quartz集群是通过数据库表来感知其他的应用的,各个节点之间并没有直接的通信。只有使用持久的JobStore才能完成Quartz集群。
数据库表:以前有12张表,现在只有11张表,现在没有存储listener相关的表,多了QRTZ_SIMPROP_TRIGGERS表:

  • 建表sql 点这里
    如果没有积分可以留言,我发给你哦。只是可能会比较晚
    在这里插入图片描述
    QRTZ_LOCKS就是Quartz集群实现同步机制的行锁表,包括以下几个锁:CALENDAR_ACCESS 、JOB_ACCESS、MISFIRE_ACCESS 、STATE_ACCESS 、TRIGGER_ACCESS。

动手

记得要先新建数据库,新建默认创建的11 张表哦

导入maven 依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--quartz-->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.1</version>
        </dependency>
        <!--因为quartz 需要有Spring context 所有引入mail包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>3.2.0</version>
        </dependency>
        
            <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>

QuartzConfig

package com.abel.quartz.config;

import java.beans.PropertyVetoException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import com.zaxxer.hikari.HikariDataSource;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
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 org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * Created by yangyibo on 2019/1/16.
 */
@Configuration
public class QuartzConfig {

    /**
     * 1.通过name+group获取唯一的jobKey;2.通过groupname来获取其下的所有jobkey
     */
    final static String GROUP_NAME = "QuartzJobGroups";

    @Value("${quartz.scheduler.instanceName}")
    private String quartzInstanceName;

    @Value("${spring.datasource.driverClassName}")
    private String myDSDriver;

    @Value("${spring.datasource.url}")
    private String myDSUrl;

    @Value("${spring.datasource.username}")
    private String myDSUser;

    @Value("${spring.datasource.password}")
    private String myDSPassword;

    @Value("${org.quartz.dataSource.myDS.maxConnections}")
    private int myDSMaxConnections;

    /**
     * 设置属性
     *
     * @return
     * @throws IOException
     */
    private Properties quartzProperties() throws IOException {
        Properties prop = new Properties();
        // 调度标识名 集群中每一个实例都必须使用相同的名称
        prop.put("quartz.scheduler.instanceName", quartzInstanceName);
        // ID设置为自动获取 每一个必须不同
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        // 禁用quartz软件更新
        prop.put("org.quartz.scheduler.skipUpdateCheck", "true");
        prop.put("org.quartz.scheduler.jmx.export", "true");


        // 数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
        prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate");
        // 数据保存方式为数据库持久化
        prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        // 数据库别名 随便取
        prop.put("org.quartz.jobStore.dataSource", "quartzDataSource");
        //prop.put("org.quartz.jobStore.dataSource", "myDS");
        // 表的前缀,默认QRTZ_
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        // 是否加入集群
        prop.put("org.quartz.jobStore.isClustered", "true");

        // 调度实例失效的检查时间间隔
        prop.put("org.quartz.jobStore.clusterCheckinInterval", "20000");
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
        // 信息保存时间 ms 默认值60秒
        prop.put("org.quartz.jobStore.misfireThreshold", "120000");
        prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
        prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE");

        // 程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        // 定线程数,至少为1(无默认值)(一般设置为1-100之间的整数合适)
        prop.put("org.quartz.threadPool.threadCount", "10");
        // 设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
        prop.put("org.quartz.threadPool.threadPriority", "5");
        prop.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");

        prop.put("org.quartz.plugin.triggHistory.class", "org.quartz.plugins.history.LoggingJobHistoryPlugin");
        prop.put("org.quartz.plugin.shutdownhook.class", "org.quartz.plugins.management.ShutdownHookPlugin");
        prop.put("org.quartz.plugin.shutdownhook.cleanShutdown", "true");

        //#自定义连接池
        //org.quartz.dataSource.myDS.connectionProvider.class=com.poly.pay.schedule.DruidConnectionProvider

        return prop;
    }

    /**
     * 数据源
     *
     * @return
     * @throws PropertyVetoException
     */
    @Bean
    public HikariDataSource createDataSource() throws PropertyVetoException {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(myDSUrl);
        dataSource.setDriverClassName(myDSDriver);
        dataSource.setUsername(myDSUser);
        dataSource.setPassword(myDSPassword);
        dataSource.setMaximumPoolSize(myDSMaxConnections);
        return dataSource;
    }


    /**
     * 创建触发器工厂
     *
     * @param jobDetail
     * @param cronExpression
     * @return
     */
    private static CronTriggerFactoryBean cronTriggerFactoryBean(JobDetail jobDetail, String cronExpression) {
        CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
        factoryBean.setJobDetail(jobDetail);
        factoryBean.setCronExpression(cronExpression);
        return factoryBean;
    }


/****************************************************以下配置需要注意******************************************************/


    /**
     * 调度工厂
     * 此处配置需要调度的触发器 例如 executeJobTrigger
     *
     * @param executeJobTrigger
     * @return
     * @throws IOException
     * @throws PropertyVetoException
     */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("executeJobTrigger") Trigger executeJobTrigger) throws IOException, PropertyVetoException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        // this allows to update triggers in DB when updating settings in config file:
        //用于quartz集群,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
        factory.setOverwriteExistingJobs(true);
        //用于quartz集群,加载quartz数据源
        //factory.setDataSource(dataSource);
        //QuartzScheduler 延时启动,应用启动完10秒后 QuartzScheduler 再启动
        //factory.setStartupDelay(10);
        //用于quartz集群,加载quartz数据源配置
        factory.setAutoStartup(true);
        factory.setQuartzProperties(quartzProperties());
        factory.setApplicationContextSchedulerContextKey("applicationContext");
        factory.setDataSource(createDataSource());
        //注册触发器
        Trigger[] triggers = {executeJobTrigger};
        factory.setTriggers(triggers);

        return factory;
    }


    /**
     * 加载触发器
     *
     * 新建触发器进行job 的调度  例如 executeJobDetail
     * @param jobDetail
     * @return
     */
    @Bean(name = "executeJobTrigger")
    public CronTriggerFactoryBean executeJobTrigger(@Qualifier("executeJobDetail") JobDetail jobDetail) {
        //每天凌晨3点执行
        return cronTriggerFactoryBean(jobDetail, "0 1 0 * * ? ");
    }


    /**
     * 加载job
     *
     * 新建job 类用来代理
     *
     *
     * @return
     */
    @Bean
    public JobDetailFactoryBean executeJobDetail() {
        return createJobDetail(InvokingJobDetailFactory.class, GROUP_NAME, "executeJob");
    }


    /**
     * 执行规则job工厂
     *
     * 配置job 类中需要定时执行的 方法  execute
     * @param jobClass
     * @param groupName
     * @param targetObject
     * @return
     */
    private static JobDetailFactoryBean createJobDetail(Class<? extends Job> jobClass,
                                                        String groupName,
                                                        String targetObject) {
        JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
        factoryBean.setJobClass(jobClass);
        factoryBean.setDurability(true);
        factoryBean.setRequestsRecovery(true);
        factoryBean.setGroup(groupName);
        Map<String, String> map = new HashMap<>();
        map.put("targetMethod", "execute");
        map.put("targetObject", targetObject);
        factoryBean.setJobDataAsMap(map);
        return factoryBean;
    }

}

InvokingJobDetailFactory


package com.abel.quartz.config;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.lang.reflect.Method;


/**
 * Created by yangyibo on 2019/2/1.
 */
public class InvokingJobDetailFactory extends QuartzJobBean {

    /**
     * 计划任务所在类
     */
    private String targetObject;

    /**
     * 具体需要执行的计划任务
     */
    private String targetMethod;

    private ApplicationContext ctx;

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        try {
            Object obj = ctx.getBean(targetObject);
            Method m = null;
            try {
                m = obj.getClass().getMethod(targetMethod);
                //调用被代理对象的方法
                m.invoke(obj);
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            throw new JobExecutionException(e);
        }
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.ctx = applicationContext;
    }

    public void setTargetObject(String targetObject) {
        this.targetObject = targetObject;
    }

    public void setTargetMethod(String targetMethod) {
        this.targetMethod = targetMethod;
    }
}


ExecuteJob

package com.abel.quartz.job;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * Created by yangyibo on 2019/2/1.
 */
@Service
public class ExecuteJob {
    private static final Logger logger = LoggerFactory.getLogger(ExecuteJob.class);

    /**
     * 方法名在quartz定义
     */
    public void execute() {
        System.out.println("定时任务执行了。。。。。");

    }
}

application.properties

## tomcat配置
server.port=8090
#server.tomcat.maxHttpHeaderSize=8192
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.messages.encoding=UTF-8
# tomcat最大线程数,默认为200  
server.tomcat.max-threads=800
# session最大超时时间(分钟),默认为30
server.session-timeout=60

## spring 配置
spring.application.name=springboot-Quartz
application.main=com.abel.quartz.Application
        

## 主数据源,默认的
spring.datasource.url=jdbc:mysql://localhost:3306/quart?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driverClassName=com.mysql.jdbc.Driver

## 连接池配置
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
#最小空闲连接
spring.datasource.hikari.minimum-idle=10
#连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count)
spring.datasource.hikari.maximum-pool-size=30
#spring.datasource.hikari.auto-commit=true
#一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟。minimumIdle<maximumPoolSize时生效
spring.datasource.hikari.idle-timeout=120000
#自定义连接池名
#spring.datasource.hikari.pool-name=DatebookHikariCP
#一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟,建议设置比数据库超时时长少30秒,参考MySQL wait_timeout参数(show variables like '%timeout%';)
spring.datasource.hikari.max-lifetime=1800000
#等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:30秒
spring.datasource.hikari.connection-timeout=30000
#指定验证连接有效性的超时时间,默认是5秒
spring.datasource.hikari.validation-timeout=3000
spring.datasource.hikari.connection-test-query=SELECT 1

# 调度标识名 集群中每一个实例都必须使用相同的名称
quartz.scheduler.instanceName=QuartScheduler
# 允许最大连接
org.quartz.dataSource.myDS.maxConnections=10

源代码地址:https://github.com/527515025/springBoot/tree/master/springboot-Quartz
参考资料:https://blog.csdn.net/lkl_csdn/article/details/73613033

  • 9
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
Spring Boot Quartz定时任务是一种在Spring Boot项目中实现定时任务调度的方法。它可以根据设定的时间间隔或者Cron表达式来执行任务。通过引用中提供的源代码和配置文件,可以直接导入Spring Boot项目并配置好数据库即可使用。 为了实现定时任务,需要在项目中创建一个继承自QuartzJobBean的Job类,并重写executeInternal方法。该方法定义了定时任务的具体逻辑,可以在其中执行需要定时执行的业务操作。 另外,还需要创建一个Quartz定时任务的配置类,其中使用@Configuration注解标记为配置类,配置相关的定时任务信息。在该配置类中,可以通过@Bean注解创建JobDetail和Trigger实例,分别定义任务的具体细节和触发器的配置信息。在这个配置类中,我提供了一个示例,用于演示如何配置一个定时任务。 综上所述,Spring Boot Quartz定时任务是一种灵活可靠的定时任务调度方法,可以通过Spring Boot的特性方便地实现任务的定时执行。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [springboot+quartz定时任务实现 纯代码](https://download.csdn.net/download/qq_38971617/12284286)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [SpringBoot整合Quartz实现定时任务](https://blog.csdn.net/qq_29305715/article/details/123517569)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值